mirror of https://gitee.com/answerdev/answer.git
fix(search): fix incorrect tags when search
This commit is contained in:
parent
a9540bf330
commit
0bd540661c
|
@ -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)
|
||||||
|
|
|
@ -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{}) {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue