You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@answer.apache.org by li...@apache.org on 2024/11/19 10:02:06 UTC

(incubator-answer) branch feat/1.4.2/tag updated: feat(question): add linked count feature to question

This is an automated email from the ASF dual-hosted git repository.

linkinstar pushed a commit to branch feat/1.4.2/tag
in repository https://gitbox.apache.org/repos/asf/incubator-answer.git


The following commit(s) were added to refs/heads/feat/1.4.2/tag by this push:
     new 5cbd1906 feat(question): add linked count feature to question
5cbd1906 is described below

commit 5cbd1906ae159421388ffefcbe24d4cdab8af7c3
Author: LinkinStars <li...@foxmail.com>
AuthorDate: Tue Nov 19 18:01:38 2024 +0800

    feat(question): add linked count feature to question
---
 internal/entity/question_entity.go           |  1 +
 internal/migrations/migrations.go            |  1 +
 internal/migrations/v24.go                   | 34 +++++++++++++++++++++++
 internal/repo/question/question_repo.go      | 40 ++++++++++++++++++++++++++++
 internal/schema/question_schema.go           |  4 +--
 internal/service/question_common/question.go | 23 ++++++++++++++++
 6 files changed, 101 insertions(+), 2 deletions(-)

diff --git a/internal/entity/question_entity.go b/internal/entity/question_entity.go
index 1931282c..9e5dcd11 100644
--- a/internal/entity/question_entity.go
+++ b/internal/entity/question_entity.go
@@ -73,6 +73,7 @@ type Question struct {
 	LastAnswerID     string    `xorm:"not null default 0 BIGINT(20) last_answer_id"`
 	PostUpdateTime   time.Time `xorm:"post_update_time TIMESTAMP"`
 	RevisionID       string    `xorm:"not null default 0 BIGINT(20) revision_id"`
+	LinkedCount      int       `xorm:"not null default 0 INT(11) linked_count"`
 }
 
 // TableName question table name
diff --git a/internal/migrations/migrations.go b/internal/migrations/migrations.go
index 81a54ae7..00e44198 100644
--- a/internal/migrations/migrations.go
+++ b/internal/migrations/migrations.go
@@ -99,6 +99,7 @@ var migrations = []Migration{
 	NewMigration("v1.3.6", "add hot score to question table", addQuestionHotScore, true),
 	NewMigration("v1.4.0", "add badge/badge_group/badge_award table", addBadges, true),
 	NewMigration("v1.4.1", "add question link", addQuestionLink, true),
+	NewMigration("v1.4.2", "add the number of question links", addQuestionLinkedCount, false),
 }
 
 func GetMigrations() []Migration {
diff --git a/internal/migrations/v24.go b/internal/migrations/v24.go
new file mode 100644
index 00000000..015352ae
--- /dev/null
+++ b/internal/migrations/v24.go
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package migrations
+
+import (
+	"context"
+	"github.com/apache/incubator-answer/internal/entity"
+
+	"xorm.io/xorm"
+)
+
+func addQuestionLinkedCount(ctx context.Context, x *xorm.Engine) error {
+	type Question struct {
+		LinkedCount int `xorm:"not null default 0 INT(11) linked_count"`
+	}
+	return x.Context(ctx).Sync(new(entity.Question))
+}
diff --git a/internal/repo/question/question_repo.go b/internal/repo/question/question_repo.go
index 1132501e..e1d94f2e 100644
--- a/internal/repo/question/question_repo.go
+++ b/internal/repo/question/question_repo.go
@@ -410,6 +410,8 @@ func (qr *questionRepo) GetQuestionPage(ctx context.Context, page, pageSize int,
 	case "unanswered":
 		session.Where("question.answer_count = 0")
 		session.OrderBy("question.pin desc,question.created_at DESC")
+	case "frequent":
+		session.OrderBy("question.pin DESC, question.linked_count DESC")
 	}
 
 	total, err = pager.Help(page, pageSize, &questionList, &entity.Question{}, session)
@@ -708,6 +710,42 @@ func (qr *questionRepo) LinkQuestion(ctx context.Context, link ...*entity.Questi
 	return
 }
 
+// UpdateQuestionLinkCount update question link count
+func (qr *questionRepo) UpdateQuestionLinkCount(ctx context.Context, questionID string) (err error) {
+	// count the number of links
+	count, err := qr.data.DB.Context(ctx).
+		Where("to_question_id = ?", questionID).
+		Where("status = ?", entity.QuestionLinkStatusAvailable).
+		Count(&entity.QuestionLink{})
+	if err != nil {
+		return errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
+	}
+
+	// update the number of links
+	_, err = qr.data.DB.Context(ctx).ID(questionID).
+		Cols("linked_count").Update(&entity.Question{LinkedCount: int(count)})
+	if err != nil {
+		return errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
+	}
+	return
+}
+
+// GetLinkedQuestionIDs get linked question ids
+func (qr *questionRepo) GetLinkedQuestionIDs(ctx context.Context, questionID string, status int) (
+	questionIDs []string, err error) {
+	questionIDs = make([]string, 0)
+	err = qr.data.DB.Context(ctx).
+		Select("to_question_id").
+		Table(new(entity.QuestionLink).TableName()).
+		Where("from_question_id = ?", questionID).
+		Where("status = ?", status).
+		Find(&questionIDs)
+	if err != nil {
+		return nil, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
+	}
+	return questionIDs, nil
+}
+
 // RecoverQuestionLink batch recover question link
 func (qr *questionRepo) RecoverQuestionLink(ctx context.Context, links ...*entity.QuestionLink) (err error) {
 	return qr.UpdateQuestionLinkStatus(ctx, entity.QuestionLinkStatusAvailable, links...)
@@ -784,6 +822,8 @@ func (qr *questionRepo) GetQuestionLink(ctx context.Context, page, pageSize int,
 	case "unanswered":
 		session.Where("question.answer_count = 0")
 		session.OrderBy("question.pin desc,question.created_at DESC")
+	case "frequent":
+		session.OrderBy("question.pin DESC, question.linked_count DESC")
 	}
 
 	if page > 0 && pageSize > 0 {
diff --git a/internal/schema/question_schema.go b/internal/schema/question_schema.go
index de19a8a5..accc3374 100644
--- a/internal/schema/question_schema.go
+++ b/internal/schema/question_schema.go
@@ -356,7 +356,7 @@ const (
 type QuestionPageReq struct {
 	Page      int    `validate:"omitempty,min=1" form:"page"`
 	PageSize  int    `validate:"omitempty,min=1" form:"page_size"`
-	OrderCond string `validate:"omitempty,oneof=newest active hot score unanswered recommend" form:"order"`
+	OrderCond string `validate:"omitempty,oneof=newest active hot score unanswered recommend frequent" form:"order"`
 	Tag       string `validate:"omitempty,gt=0,lte=100" form:"tag"`
 	Username  string `validate:"omitempty,gt=0,lte=100" form:"username"`
 	InDays    int    `validate:"omitempty,min=1" form:"in_days"`
@@ -504,7 +504,7 @@ type GetQuestionLinkReq struct {
 	Page       int    `validate:"omitempty,min=1" form:"page"`
 	PageSize   int    `validate:"omitempty,min=1,max=100" form:"page_size"`
 	QuestionID string `validate:"required" form:"question_id"`
-	OrderCond  string `validate:"omitempty,oneof=newest active hot score unanswered recommend" form:"order"`
+	OrderCond  string `validate:"omitempty,oneof=newest active hot score unanswered recommend frequent" form:"order"`
 	InDays     int    `validate:"omitempty,min=1" form:"in_days"`
 
 	LoginUserID string `json:"-"`
diff --git a/internal/service/question_common/question.go b/internal/service/question_common/question.go
index 08c74f29..fc01159e 100644
--- a/internal/service/question_common/question.go
+++ b/internal/service/question_common/question.go
@@ -81,6 +81,8 @@ type QuestionRepo interface {
 	RemoveAllUserQuestion(ctx context.Context, userID string) (err error)
 	UpdateSearch(ctx context.Context, questionID string) (err error)
 	LinkQuestion(ctx context.Context, link ...*entity.QuestionLink) (err error)
+	GetLinkedQuestionIDs(ctx context.Context, questionID string, status int) (questionIDs []string, err error)
+	UpdateQuestionLinkCount(ctx context.Context, questionID string) (err error)
 	RemoveQuestionLink(ctx context.Context, link ...*entity.QuestionLink) (err error)
 	RecoverQuestionLink(ctx context.Context, link ...*entity.QuestionLink) (err error)
 	UpdateQuestionLinkStatus(ctx context.Context, status int, links ...*entity.QuestionLink) (err error)
@@ -704,6 +706,17 @@ func (qs *QuestionCommon) UpdateQuestionLink(ctx context.Context, questionID, an
 	if err != nil {
 		return parsedText, err
 	}
+	// Update the number of question links that have been removed
+	linkedQuestionIDs, err := qs.questionRepo.GetLinkedQuestionIDs(ctx, questionID, entity.QuestionLinkStatusDeleted)
+	if err != nil {
+		log.Errorf("get linked question ids error %v", err)
+	} else {
+		for _, id := range linkedQuestionIDs {
+			if err := qs.questionRepo.UpdateQuestionLinkCount(ctx, id); err != nil {
+				log.Errorf("update question link count error %v", err)
+			}
+		}
+	}
 
 	links := checker.GetQuestionLink(originalText)
 	if len(links) == 0 {
@@ -799,6 +812,16 @@ func (qs *QuestionCommon) UpdateQuestionLink(ctx context.Context, questionID, an
 		}
 	}
 
+	// update question linked count
+	for _, link := range validLinks {
+		if len(link.ToQuestionID) == 0 {
+			continue
+		}
+		if err := qs.questionRepo.UpdateQuestionLinkCount(ctx, link.ToQuestionID); err != nil {
+			log.Errorf("update question link count error %v", err)
+		}
+	}
+
 	return parsedText, nil
 }