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

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

commit 74368ec9d4490fac5f4f35c579b4e91410fc0d8d
Author: Sonui <[email protected]>
AuthorDate: Fri Oct 11 00:55:10 2024 +0800

    perf: optimize question link processing
---
 internal/service/content/answer_service.go   |  86 +--------------------
 internal/service/content/question_service.go |  85 +--------------------
 internal/service/question_common/question.go | 108 +++++++++++++++++++++++++++
 3 files changed, 112 insertions(+), 167 deletions(-)

diff --git a/internal/service/content/answer_service.go 
b/internal/service/content/answer_service.go
index bd70fc93..d4983c3c 100644
--- a/internal/service/content/answer_service.go
+++ b/internal/service/content/answer_service.go
@@ -22,7 +22,6 @@ package content
 import (
        "context"
        "encoding/json"
-       "strings"
        "time"
 
        "github.com/apache/incubator-answer/internal/service/event_queue"
@@ -44,7 +43,6 @@ import (
        "github.com/apache/incubator-answer/internal/service/revision_common"
        "github.com/apache/incubator-answer/internal/service/role"
        usercommon 
"github.com/apache/incubator-answer/internal/service/user_common"
-       "github.com/apache/incubator-answer/pkg/checker"
        "github.com/apache/incubator-answer/pkg/converter"
        "github.com/apache/incubator-answer/pkg/htmltext"
        "github.com/apache/incubator-answer/pkg/token"
@@ -275,7 +273,7 @@ func (as *AnswerService) Insert(ctx context.Context, req 
*schema.AnswerAddReq) (
                return "", err
        }
        if insertData.Status == entity.AnswerStatusAvailable {
-               insertData.ParsedText, err = as.updateAnswerLink(ctx, 
insertData)
+               insertData.ParsedText, err = 
as.questionCommon.UpdateQuestionLink(ctx, insertData.QuestionID, insertData.ID, 
insertData.ParsedText, insertData.OriginalText)
                if err != nil {
                        return "", err
                }
@@ -398,7 +396,7 @@ func (as *AnswerService) Update(ctx context.Context, req 
*schema.AnswerUpdateReq
        if !canUpdate {
                revisionDTO.Status = entity.RevisionUnreviewedStatus
        } else {
-               insertData.ParsedText, err = as.updateAnswerLink(ctx, 
insertData)
+               insertData.ParsedText, err = 
as.questionCommon.UpdateQuestionLink(ctx, insertData.QuestionID, insertData.ID, 
insertData.ParsedText, insertData.OriginalText)
                if err != nil {
                        return "", err
                }
@@ -746,83 +744,3 @@ func (as *AnswerService) notificationAnswerTheQuestion(ctx 
context.Context,
        externalNotificationMsg.NewAnswerTemplateRawData = rawData
        as.externalNotificationQueueService.Send(ctx, externalNotificationMsg)
 }
-
-func (as *AnswerService) updateAnswerLink(ctx context.Context, answer 
*entity.Answer) (string, error) {
-       err := as.questionRepo.RemoveQuestionLink(ctx, &entity.QuestionLink{
-               FromQuestionID: uid.DeShortID(answer.QuestionID),
-               FromAnswerID:   uid.DeShortID(answer.ID),
-       })
-       retParsedText := answer.ParsedText
-       if err != nil {
-               return retParsedText, err
-       }
-       links := checker.GetQuestionLink(answer.OriginalText)
-       // validate links
-       questionLinks := make([]*entity.QuestionLink, 0)
-       answerCache := make(map[string]string)
-       questionCache := make(map[string]string)
-       answerIDList := make([]string, 0)
-       questionIDList := make([]string, 0)
-       for _, link := range links {
-               if link.AnswerID != "" {
-                       answerIDList = append(answerIDList, link.AnswerID)
-               }
-               if link.QuestionID != "" {
-                       questionIDList = append(questionIDList, link.QuestionID)
-               }
-       }
-       answerInfoList, err := as.answerRepo.GetByIDs(ctx, answerIDList...)
-       if err != nil {
-               return answer.ParsedText, err
-       }
-       for _, answer := range answerInfoList {
-               answerCache[answer.ID] = answer.QuestionID
-       }
-       questionInfoList, err := as.questionRepo.FindByID(ctx, questionIDList)
-       if err != nil {
-               return answer.ParsedText, err
-       }
-       for _, question := range questionInfoList {
-               questionCache[question.ID] = question.ParsedText
-       }
-
-       for _, link := range links {
-               if link.QuestionID != "" {
-                       if _, ok := questionCache[link.QuestionID]; !ok {
-                               continue
-                       }
-               }
-               if link.AnswerID != "" {
-                       if _, ok := answerCache[link.AnswerID]; !ok {
-                               continue
-                       }
-                       if link.QuestionID == "" {
-                               link.QuestionID = answerCache[link.AnswerID]
-                       }
-               }
-
-               addLink := &entity.QuestionLink{
-                       FromQuestionID: uid.DeShortID(answer.QuestionID),
-                       FromAnswerID:   uid.DeShortID(answer.ID),
-                       ToQuestionID:   uid.DeShortID(link.QuestionID),
-                       ToAnswerID:     uid.DeShortID(link.AnswerID),
-               }
-               if link.QuestionID != "" {
-                       retParsedText = strings.ReplaceAll(retParsedText, 
"#"+link.QuestionID, "<a 
href=\"/questions/"+link.QuestionID+"\">#"+link.QuestionID+"</a>")
-               }
-               if link.AnswerID != "" {
-                       questionID := answerCache[link.AnswerID]
-                       addLink.ToQuestionID = questionID
-                       retParsedText = strings.ReplaceAll(retParsedText, 
"#"+link.AnswerID, "<a 
href=\"/questions/"+questionID+"/"+link.AnswerID+"\">#"+link.AnswerID+"</a>")
-               }
-               if addLink.FromQuestionID == addLink.ToQuestionID {
-                       continue
-               }
-               questionLinks = append(questionLinks, addLink)
-       }
-       if err = as.questionRepo.LinkQuestion(ctx, questionLinks...); err != 
nil {
-               return retParsedText, err
-       }
-
-       return retParsedText, nil
-}
diff --git a/internal/service/content/question_service.go 
b/internal/service/content/question_service.go
index d2692d8c..7cfd39a0 100644
--- a/internal/service/content/question_service.go
+++ b/internal/service/content/question_service.go
@@ -349,7 +349,7 @@ func (qs *QuestionService) AddQuestion(ctx context.Context, 
req *schema.Question
                return nil, err
        }
        if question.Status == entity.QuestionStatusAvailable {
-               question.ParsedText, err = qs.updateQuestionLink(ctx, question)
+               question.ParsedText, err = 
qs.questioncommon.UpdateQuestionLink(ctx, question.ID, "", question.ParsedText, 
question.OriginalText)
                if err != nil {
                        return nil, err
                }
@@ -964,7 +964,7 @@ func (qs *QuestionService) UpdateQuestion(ctx 
context.Context, req *schema.Quest
                //Direct modification
                revisionDTO.Status = entity.RevisionReviewPassStatus
                //update question to db
-               question.ParsedText, err = qs.updateQuestionLink(ctx, question)
+               question.ParsedText, err = 
qs.questioncommon.UpdateQuestionLink(ctx, question.ID, "", question.ParsedText, 
question.OriginalText)
                if err != nil {
                        return questionInfo, err
                }
@@ -1621,87 +1621,6 @@ func (qs *QuestionService) SitemapCron(ctx 
context.Context) {
        qs.questioncommon.SitemapCron(ctx)
 }
 
-func (qs *QuestionService) updateQuestionLink(ctx context.Context, 
questionInfo *entity.Question) (string, error) {
-       err := qs.questionRepo.RemoveQuestionLink(ctx, &entity.QuestionLink{
-               FromQuestionID: questionInfo.ID,
-               FromAnswerID:   "0",
-       })
-       if err != nil {
-               return questionInfo.ParsedText, err
-       }
-       retParsedText := questionInfo.ParsedText
-       links := checker.GetQuestionLink(questionInfo.OriginalText)
-       // validate links
-       questionLinks := make([]*entity.QuestionLink, 0)
-       answerCache := make(map[string]string)
-       questionCache := make(map[string]string)
-       answerIDList := make([]string, 0)
-       questionIDList := make([]string, 0)
-       for _, link := range links {
-               if link.AnswerID != "" {
-                       answerIDList = append(answerIDList, link.AnswerID)
-               }
-               if link.QuestionID != "" {
-                       questionIDList = append(questionIDList, link.QuestionID)
-               }
-       }
-       answerInfoList, err := qs.answerRepo.GetByIDs(ctx, answerIDList...)
-       if err != nil {
-               return questionInfo.ParsedText, err
-       }
-       for _, answer := range answerInfoList {
-               answerCache[answer.ID] = answer.QuestionID
-       }
-       questionInfoList, err := qs.questionRepo.FindByID(ctx, questionIDList)
-       if err != nil {
-               return questionInfo.ParsedText, err
-       }
-       for _, question := range questionInfoList {
-               questionCache[question.ID] = question.ParsedText
-       }
-
-       for _, link := range links {
-               if link.QuestionID != "" {
-                       if _, ok := questionCache[link.QuestionID]; !ok {
-                               continue
-                       }
-               }
-               if link.AnswerID != "" {
-                       if _, ok := answerCache[link.AnswerID]; !ok {
-                               continue
-                       }
-                       if link.QuestionID == "" {
-                               link.QuestionID = answerCache[link.AnswerID]
-                       }
-               }
-
-               addLink := &entity.QuestionLink{
-                       FromQuestionID: uid.DeShortID(questionInfo.ID),
-                       FromAnswerID:   "0",
-                       ToQuestionID:   uid.DeShortID(link.QuestionID),
-                       ToAnswerID:     uid.DeShortID(link.AnswerID),
-               }
-
-               if link.QuestionID != "" {
-                       retParsedText = strings.ReplaceAll(retParsedText, 
"#"+link.QuestionID, "<a 
href=\"/questions/"+link.QuestionID+"\">#"+link.QuestionID+"</a>")
-               }
-               if link.AnswerID != "" {
-                       questionID := answerCache[link.AnswerID]
-                       addLink.ToQuestionID = questionID
-                       retParsedText = strings.ReplaceAll(retParsedText, 
"#"+link.AnswerID, "<a 
href=\"/questions/"+questionID+"/"+link.AnswerID+"\">#"+link.AnswerID+"</a>")
-               }
-               if addLink.FromQuestionID == addLink.ToQuestionID {
-                       continue
-               }
-               questionLinks = append(questionLinks, addLink)
-       }
-       if err = qs.questionRepo.LinkQuestion(ctx, questionLinks...); err != 
nil {
-               return retParsedText, err
-       }
-
-       return retParsedText, nil
-}
-
 func (qs *QuestionService) GetQuestionLink(ctx context.Context, req 
*schema.GetQuestionLinkReq) (
        questions []*schema.QuestionPageResp, total int64, err error) {
        if req.OrderCond == schema.QuestionOrderCondHot {
diff --git a/internal/service/question_common/question.go 
b/internal/service/question_common/question.go
index f242b3f8..63c8f5a2 100644
--- a/internal/service/question_common/question.go
+++ b/internal/service/question_common/question.go
@@ -22,7 +22,9 @@ package questioncommon
 import (
        "context"
        "encoding/json"
+       "fmt"
        "math"
+       "strings"
        "time"
 
        "github.com/apache/incubator-answer/internal/base/constant"
@@ -687,3 +689,109 @@ func (qs *QuestionCommon) ShowFormatWithTag(ctx 
context.Context, data *entity.Qu
        info.Tags = Tags
        return info
 }
+
+func (qs *QuestionCommon) UpdateQuestionLink(ctx context.Context, questionID, 
answerID, parsedText, originalText string) (string, error) {
+       err := qs.questionRepo.RemoveQuestionLink(ctx, &entity.QuestionLink{
+               FromQuestionID: uid.DeShortID(questionID),
+               FromAnswerID:   uid.DeShortID(answerID),
+       })
+       if err != nil {
+               return parsedText, err
+       }
+
+       links := checker.GetQuestionLink(originalText)
+       if len(links) == 0 {
+               return parsedText, nil
+       }
+
+       // get answer ids and question ids
+       answerIDs := make([]string, 0, len(links))
+       questionIDs := make([]string, 0, len(links))
+       for _, link := range links {
+               if link.AnswerID != "" {
+                       answerIDs = append(answerIDs, link.AnswerID)
+               }
+               if link.QuestionID != "" {
+                       questionIDs = append(questionIDs, link.QuestionID)
+               }
+       }
+
+       // get answer info and build cache
+       answerInfoList, err := qs.answerRepo.GetByIDs(ctx, answerIDs...)
+       if err != nil {
+               return parsedText, err
+       }
+       answerCache := make(map[string]string, len(answerInfoList))
+       for _, ans := range answerInfoList {
+               answerID := uid.DeShortID(ans.ID)
+               questionID := ans.QuestionID
+               answerCache[answerID] = questionID
+       }
+
+       // get question info and build cache
+       questionInfoList, err := qs.questionRepo.FindByID(ctx, questionIDs)
+       if err != nil {
+               return parsedText, err
+       }
+       questionCache := make(map[string]struct{}, len(questionInfoList))
+       for _, q := range questionInfoList {
+               questionID := uid.DeShortID(q.ID)
+               questionCache[questionID] = struct{}{}
+       }
+
+       // process links and generate new QuestionLink
+       validLinks := make([]*entity.QuestionLink, 0, len(links))
+       for _, link := range links {
+               linkQuestionID := uid.DeShortID(link.QuestionID)
+               linkAnswerID := uid.DeShortID(link.AnswerID)
+               // validate question id
+               if _, exists := questionCache[linkQuestionID]; linkQuestionID 
!= "0" && !exists {
+                       continue
+               }
+
+               // validate answer id
+               if linkAnswerID != "0" {
+                       linkedQuestionID, exists := answerCache[linkAnswerID]
+                       if !exists {
+                               continue
+                       }
+                       // if question id is empty, get it from answer cache
+                       if link.QuestionID == "" {
+                               link.QuestionID = linkedQuestionID
+                       }
+               }
+
+               // build new link
+               newLink := &entity.QuestionLink{
+                       FromQuestionID: uid.DeShortID(questionID),
+                       FromAnswerID:   uid.DeShortID(answerID),
+                       ToQuestionID:   uid.DeShortID(link.QuestionID),
+                       ToAnswerID:     uid.DeShortID(link.AnswerID),
+               }
+               // replace link in parsed text
+               if link.QuestionID != "" {
+                       htmlLink := fmt.Sprintf("<a 
href=\"/questions/%s\">#%s</a>", link.QuestionID, link.QuestionID)
+                       parsedText = strings.ReplaceAll(parsedText, 
"#"+link.QuestionID, htmlLink)
+               }
+               if link.AnswerID != "" {
+                       linkedQuestionID := answerCache[linkAnswerID]
+                       htmlLink := fmt.Sprintf("<a 
href=\"/questions/%s/%s\">#%s</a>", linkedQuestionID, link.AnswerID, 
link.AnswerID)
+                       parsedText = strings.ReplaceAll(parsedText, 
"#"+link.AnswerID, htmlLink)
+                       newLink.ToQuestionID = uid.DeShortID(linkedQuestionID)
+               }
+               // avoid link to self
+               if newLink.FromQuestionID != newLink.ToQuestionID {
+                       validLinks = append(validLinks, newLink)
+               }
+       }
+
+       // add new links to repo
+       if len(validLinks) > 0 {
+               err = qs.questionRepo.LinkQuestion(ctx, validLinks...)
+               if err != nil {
+                       return parsedText, err
+               }
+       }
+
+       return parsedText, nil
+}

Reply via email to