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 +}
