fix(search): fix incorrect tags when search

This commit is contained in:
LinkinStars 2023-08-30 17:41:44 +08:00
parent a9540bf330
commit 0bd540661c
6 changed files with 86 additions and 68 deletions

View File

@ -184,7 +184,7 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database,
questionController := controller.NewQuestionController(questionService, answerService, rankService, siteInfoCommonService, captchaService) questionController := controller.NewQuestionController(questionService, answerService, rankService, siteInfoCommonService, captchaService)
answerController := controller.NewAnswerController(answerService, rankService, captchaService) answerController := controller.NewAnswerController(answerService, rankService, captchaService)
searchParser := search_parser.NewSearchParser(tagCommonService, userCommon) searchParser := search_parser.NewSearchParser(tagCommonService, userCommon)
searchRepo := search_common.NewSearchRepo(dataData, uniqueIDRepo, userCommon) searchRepo := search_common.NewSearchRepo(dataData, uniqueIDRepo, userCommon, tagCommonService)
searchService := service.NewSearchService(searchParser, searchRepo) searchService := service.NewSearchService(searchParser, searchRepo)
searchController := controller.NewSearchController(searchService, captchaService) searchController := controller.NewSearchController(searchService, captchaService)
serviceRevisionService := service.NewRevisionService(revisionRepo, userCommon, questionCommon, answerService, objService, questionRepo, answerRepo, tagRepo, tagCommonService, notificationQueueService, activityQueueService) serviceRevisionService := service.NewRevisionService(revisionRepo, userCommon, questionCommon, answerService, objService, questionRepo, answerRepo, tagRepo, tagCommonService, notificationQueueService, activityQueueService)

View File

@ -3,6 +3,7 @@ package search_common
import ( import (
"context" "context"
"fmt" "fmt"
tagcommon "github.com/answerdev/answer/internal/service/tag_common"
"github.com/answerdev/answer/plugin" "github.com/answerdev/answer/plugin"
"strconv" "strconv"
"strings" "strings"
@ -19,7 +20,6 @@ import (
usercommon "github.com/answerdev/answer/internal/service/user_common" usercommon "github.com/answerdev/answer/internal/service/user_common"
"github.com/answerdev/answer/pkg/converter" "github.com/answerdev/answer/pkg/converter"
"github.com/answerdev/answer/pkg/obj" "github.com/answerdev/answer/pkg/obj"
"github.com/jinzhu/copier"
"github.com/segmentfault/pacman/errors" "github.com/segmentfault/pacman/errors"
"xorm.io/builder" "xorm.io/builder"
) )
@ -58,19 +58,26 @@ type searchRepo struct {
data *data.Data data *data.Data
userCommon *usercommon.UserCommon userCommon *usercommon.UserCommon
uniqueIDRepo unique.UniqueIDRepo uniqueIDRepo unique.UniqueIDRepo
tagCommon *tagcommon.TagCommonService
} }
// NewSearchRepo new repository // NewSearchRepo new repository
func NewSearchRepo(data *data.Data, uniqueIDRepo unique.UniqueIDRepo, userCommon *usercommon.UserCommon) search_common.SearchRepo { func NewSearchRepo(
data *data.Data,
uniqueIDRepo unique.UniqueIDRepo,
userCommon *usercommon.UserCommon,
tagCommon *tagcommon.TagCommonService,
) search_common.SearchRepo {
return &searchRepo{ return &searchRepo{
data: data, data: data,
uniqueIDRepo: uniqueIDRepo, uniqueIDRepo: uniqueIDRepo,
userCommon: userCommon, userCommon: userCommon,
tagCommon: tagCommon,
} }
} }
// SearchContents search question and answer data // SearchContents search question and answer data
func (sr *searchRepo) SearchContents(ctx context.Context, words []string, tagIDs []string, userID string, votes int, page, size int, order string) (resp []schema.SearchResult, total int64, err error) { func (sr *searchRepo) SearchContents(ctx context.Context, words []string, tagIDs []string, userID string, votes int, page, size int, order string) (resp []*schema.SearchResult, total int64, err error) {
words = filterWords(words) words = filterWords(words)
var ( var (
@ -210,7 +217,7 @@ func (sr *searchRepo) SearchContents(ctx context.Context, words []string, tagIDs
} }
// SearchQuestions search question data // SearchQuestions search question data
func (sr *searchRepo) SearchQuestions(ctx context.Context, words []string, tagIDs []string, notAccepted bool, views, answers int, page, size int, order string) (resp []schema.SearchResult, total int64, err error) { func (sr *searchRepo) SearchQuestions(ctx context.Context, words []string, tagIDs []string, notAccepted bool, views, answers int, page, size int, order string) (resp []*schema.SearchResult, total int64, err error) {
words = filterWords(words) words = filterWords(words)
var ( var (
qfs = qFields qfs = qFields
@ -319,7 +326,7 @@ func (sr *searchRepo) SearchQuestions(ctx context.Context, words []string, tagID
} }
// SearchAnswers search answer data // SearchAnswers search answer data
func (sr *searchRepo) SearchAnswers(ctx context.Context, words []string, tagIDs []string, accepted bool, questionID string, page, size int, order string) (resp []schema.SearchResult, total int64, err error) { func (sr *searchRepo) SearchAnswers(ctx context.Context, words []string, tagIDs []string, accepted bool, questionID string, page, size int, order string) (resp []*schema.SearchResult, total int64, err error) {
words = filterWords(words) words = filterWords(words)
var ( var (
@ -428,7 +435,7 @@ func (sr *searchRepo) parseOrder(ctx context.Context, order string) (res string)
} }
// ParseSearchPluginResult parse search plugin result // ParseSearchPluginResult parse search plugin result
func (sr *searchRepo) ParseSearchPluginResult(ctx context.Context, sres []plugin.SearchResult) (resp []schema.SearchResult, err error) { func (sr *searchRepo) ParseSearchPluginResult(ctx context.Context, sres []plugin.SearchResult) (resp []*schema.SearchResult, err error) {
var ( var (
qres []map[string][]byte qres []map[string][]byte
res = make([]map[string][]byte, 0) res = make([]map[string][]byte, 0)
@ -455,82 +462,79 @@ func (sr *searchRepo) ParseSearchPluginResult(ctx context.Context, sres []plugin
} }
// parseResult parse search result, return the data structure // parseResult parse search result, return the data structure
func (sr *searchRepo) parseResult(ctx context.Context, res []map[string][]byte) (resp []schema.SearchResult, err error) { func (sr *searchRepo) parseResult(ctx context.Context, res []map[string][]byte) (resp []*schema.SearchResult, err error) {
questionIDs := make([]string, 0)
userIDs := make([]string, 0)
resultList := make([]*schema.SearchResult, 0)
for _, r := range res { for _, r := range res {
var ( questionIDs = append(questionIDs, string(r["question_id"]))
objectKey, userIDs = append(userIDs, string(r["user_id"]))
status string tp, _ := time.ParseInLocation("2006-01-02 15:04:05", string(r["created_at"]), time.Local)
object := &schema.SearchObject{
ID: string(r["id"]),
QuestionID: string(r["question_id"]),
Title: string(r["title"]),
Excerpt: htmltext.FetchExcerpt(string(r["parsed_text"]), "...", 240),
CreatedAtParsed: tp.Unix(),
UserInfo: &schema.SearchObjectUser{
ID: string(r["user_id"]),
},
Tags: make([]*schema.TagResp, 0),
VoteCount: converter.StringToInt(string(r["vote_count"])),
Accepted: string(r["accepted"]) == "2",
AnswerCount: converter.StringToInt(string(r["answer_count"])),
}
tags []schema.TagResp objectKey, err := obj.GetObjectTypeStrByObjectID(string(r["id"]))
tagsEntity []entity.Tag
object schema.SearchObject
)
objectKey, err = obj.GetObjectTypeStrByObjectID(string(r["id"]))
if err != nil { if err != nil {
continue continue
} }
tp, _ := time.ParseInLocation("2006-01-02 15:04:05", string(r["created_at"]), time.Local)
// get user info
userInfo, _, e := sr.userCommon.GetUserBasicInfoByID(ctx, string(r["user_id"]))
if e != nil {
err = errors.InternalServer(reason.DatabaseError).WithError(e).WithStack()
return
}
// get tags
err = sr.data.DB.Context(ctx).
Select("`display_name`,`slug_name`,`main_tag_slug_name`,`recommend`,`reserved`").
Table("tag").
Join("INNER", "tag_rel", "tag.id = tag_rel.tag_id").
Where(builder.Eq{"tag_rel.object_id": r["question_id"]}).
And(builder.Eq{"tag_rel.status": entity.TagRelStatusAvailable}).
UseBool("recommend", "reserved").
OrderBy("tag.recommend DESC, tag.reserved DESC, tag.id DESC").
Find(&tagsEntity)
if err != nil {
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
return
}
_ = copier.Copy(&tags, tagsEntity)
switch objectKey { switch objectKey {
case "question": case "question":
for k, v := range entity.AdminQuestionSearchStatus { for k, v := range entity.AdminQuestionSearchStatus {
if v == converter.StringToInt(string(r["status"])) { if v == converter.StringToInt(string(r["status"])) {
status = k object.StatusStr = k
break break
} }
} }
case "answer": case "answer":
for k, v := range entity.AdminAnswerSearchStatus { for k, v := range entity.AdminAnswerSearchStatus {
if v == converter.StringToInt(string(r["status"])) { if v == converter.StringToInt(string(r["status"])) {
status = k object.StatusStr = k
break break
} }
} }
} }
object = schema.SearchObject{ resultList = append(resultList, &schema.SearchResult{
ID: string(r["id"]),
QuestionID: string(r["question_id"]),
Title: string(r["title"]),
Excerpt: htmltext.FetchExcerpt(string(r["parsed_text"]), "...", 240),
CreatedAtParsed: tp.Unix(),
UserInfo: userInfo,
Tags: tags,
VoteCount: converter.StringToInt(string(r["vote_count"])),
Accepted: string(r["accepted"]) == "2",
AnswerCount: converter.StringToInt(string(r["answer_count"])),
StatusStr: status,
}
resp = append(resp, schema.SearchResult{
ObjectType: objectKey, ObjectType: objectKey,
Object: object, Object: object,
}) })
} }
return
tagsMap, err := sr.tagCommon.BatchGetObjectTag(ctx, questionIDs)
if err != nil {
return nil, err
}
userInfoMap, err := sr.userCommon.BatchUserBasicInfoByID(ctx, userIDs)
if err != nil {
return nil, err
}
for _, item := range resultList {
tags, ok := tagsMap[item.Object.QuestionID]
if ok {
item.Object.Tags = tags
}
if userInfo := userInfoMap[item.Object.UserInfo.ID]; userInfo != nil {
item.Object.UserInfo.Username = userInfo.Username
item.Object.UserInfo.DisplayName = userInfo.DisplayName
item.Object.UserInfo.Rank = userInfo.Rank
item.Object.UserInfo.Status = userInfo.Status
}
}
return resultList, nil
} }
func addRelevanceField(searchFields, words, fields []string) (res []string, args []interface{}) { func addRelevanceField(searchFields, words, fields []string) (res []string, args []interface{}) {

View File

@ -90,13 +90,21 @@ type SearchObject struct {
Accepted bool `json:"accepted"` Accepted bool `json:"accepted"`
AnswerCount int `json:"answer_count"` AnswerCount int `json:"answer_count"`
// user info // user info
UserInfo *UserBasicInfo `json:"user_info"` UserInfo *SearchObjectUser `json:"user_info"`
// tags // tags
Tags []TagResp `json:"tags"` Tags []*TagResp `json:"tags"`
// Status // Status
StatusStr string `json:"status"` StatusStr string `json:"status"`
} }
type SearchObjectUser struct {
ID string `json:"id"`
Username string `json:"username"`
DisplayName string `json:"display_name"`
Rank int `json:"rank"`
Status string `json:"status"`
}
type TagResp struct { type TagResp struct {
ID string `json:"-"` ID string `json:"-"`
SlugName string `json:"slug_name"` SlugName string `json:"slug_name"`
@ -111,13 +119,13 @@ type SearchResult struct {
// object_type // object_type
ObjectType string `json:"object_type"` ObjectType string `json:"object_type"`
// this object // this object
Object SearchObject `json:"object"` Object *SearchObject `json:"object"`
} }
type SearchResp struct { type SearchResp struct {
Total int64 `json:"count"` Total int64 `json:"count"`
// search response // search response
SearchResults []SearchResult `json:"list"` SearchResults []*SearchResult `json:"list"`
} }
type SearchDescResp struct { type SearchDescResp struct {

View File

@ -7,8 +7,8 @@ import (
) )
type SearchRepo interface { type SearchRepo interface {
SearchContents(ctx context.Context, words []string, tagIDs []string, userID string, votes, page, size int, order string) (resp []schema.SearchResult, total int64, err error) SearchContents(ctx context.Context, words []string, tagIDs []string, userID string, votes, page, size int, order string) (resp []*schema.SearchResult, total int64, err error)
SearchQuestions(ctx context.Context, words []string, tagIDs []string, notAccepted bool, views, answers int, page, size int, order string) (resp []schema.SearchResult, total int64, err error) SearchQuestions(ctx context.Context, words []string, tagIDs []string, notAccepted bool, views, answers int, page, size int, order string) (resp []*schema.SearchResult, total int64, err error)
SearchAnswers(ctx context.Context, words []string, tagIDs []string, accepted bool, questionID string, page, size int, order string) (resp []schema.SearchResult, total int64, err error) SearchAnswers(ctx context.Context, words []string, tagIDs []string, accepted bool, questionID string, page, size int, order string) (resp []*schema.SearchResult, total int64, err error)
ParseSearchPluginResult(ctx context.Context, sres []plugin.SearchResult) (resp []schema.SearchResult, err error) ParseSearchPluginResult(ctx context.Context, sres []plugin.SearchResult) (resp []*schema.SearchResult, err error)
} }

View File

@ -436,6 +436,9 @@ func (ts *TagCommonService) tagFormatRecommendAndReserved(ctx context.Context, t
// BatchGetObjectTag batch get object tag // BatchGetObjectTag batch get object tag
func (ts *TagCommonService) BatchGetObjectTag(ctx context.Context, objectIds []string) (map[string][]*schema.TagResp, error) { func (ts *TagCommonService) BatchGetObjectTag(ctx context.Context, objectIds []string) (map[string][]*schema.TagResp, error) {
objectIDTagMap := make(map[string][]*schema.TagResp) objectIDTagMap := make(map[string][]*schema.TagResp)
if len(objectIds) == 0 {
return objectIDTagMap, nil
}
objectTagRelList, err := ts.tagRelRepo.BatchGetObjectTagRelList(ctx, objectIds) objectTagRelList, err := ts.tagRelRepo.BatchGetObjectTagRelList(ctx, objectIds)
if err != nil { if err != nil {
return objectIDTagMap, err return objectIDTagMap, err

View File

@ -106,9 +106,12 @@ func (us *UserCommon) UpdateQuestionCount(ctx context.Context, userID string, nu
return us.userRepo.UpdateQuestionCount(ctx, userID, num) return us.userRepo.UpdateQuestionCount(ctx, userID, num)
} }
func (us *UserCommon) BatchUserBasicInfoByID(ctx context.Context, IDs []string) (map[string]*schema.UserBasicInfo, error) { func (us *UserCommon) BatchUserBasicInfoByID(ctx context.Context, userIDs []string) (map[string]*schema.UserBasicInfo, error) {
userMap := make(map[string]*schema.UserBasicInfo) userMap := make(map[string]*schema.UserBasicInfo)
userList, err := us.userRepo.BatchGetByID(ctx, IDs) if len(userIDs) == 0 {
return userMap, nil
}
userList, err := us.userRepo.BatchGetByID(ctx, userIDs)
if err != nil { if err != nil {
return userMap, err return userMap, err
} }