This is an automated email from the ASF dual-hosted git repository.
kumfo pushed a commit to branch feat/1.2.5/search
in repository https://gitbox.apache.org/repos/asf/incubator-answer.git
The following commit(s) were added to refs/heads/feat/1.2.5/search by this push:
new 6cd0f3e8 feat: optimize search results
6cd0f3e8 is described below
commit 6cd0f3e8144070c0dc0c038d56f08f7efe047518
Author: kumfo <[email protected]>
AuthorDate: Mon Dec 11 16:30:20 2023 +0800
feat: optimize search results
---
internal/repo/search_common/search_repo.go | 124 +++++++++---------------
internal/service/search_common/search.go | 4 +-
internal/service/search_parser/search_parser.go | 15 ++-
internal/service/search_service.go | 4 +-
4 files changed, 62 insertions(+), 85 deletions(-)
diff --git a/internal/repo/search_common/search_repo.go
b/internal/repo/search_common/search_repo.go
index de3c03eb..7e282876 100644
--- a/internal/repo/search_common/search_repo.go
+++ b/internal/repo/search_common/search_repo.go
@@ -50,10 +50,10 @@ var (
"`question`.`id`",
"`question`.`id` as `question_id`",
"`title`",
- "`parsed_text`",
+ "`question`.`parsed_text` as `parsed_text`",
"`question`.`created_at` as `created_at`",
- "`user_id`",
- "`vote_count`",
+ "`question`.`user_id` as `user_id`",
+ "`question`.`vote_count` as `vote_count`",
"`answer_count`",
"CASE WHEN `accepted_answer_id` > 0 THEN 2 ELSE 0 END as
`accepted`",
"`question`.`status` as `status`",
@@ -98,54 +98,41 @@ func NewSearchRepo(
}
// 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, page, size int, order string) (resp
[]*schema.SearchResult, total int64, err error) {
words = filterWords(words)
-
var (
- b *builder.Builder
- ub *builder.Builder
- qfs = qFields
- afs = aFields
- argsQ = []interface{}{}
- argsA = []interface{}{}
+ b *builder.Builder
+ qfs = qFields
+ args = []interface{}{}
)
if order == "relevance" {
if len(words) > 0 {
- qfs, argsQ = addRelevanceField([]string{"title",
"original_text"}, words, qfs)
- afs, argsA =
addRelevanceField([]string{"`answer`.`original_text`"}, words, afs)
+ qfs, args =
addRelevanceField([]string{"`question`.`title`", "`question`.`original_text`"},
words, qfs)
} else {
order = "newest"
}
}
- b = builder.MySQL().Select(qfs...).From("`question`")
- ub = builder.MySQL().Select(afs...).From("`answer`").
- LeftJoin("`question`", "`question`.id = `answer`.question_id")
-
- b.Where(builder.Lt{"`question`.`status`":
entity.QuestionStatusDeleted}).
- And(builder.Eq{"`question`.`show`": entity.QuestionShow})
- ub.Where(builder.Lt{"`question`.`status`":
entity.QuestionStatusDeleted}).
- And(builder.Lt{"`answer`.`status`":
entity.AnswerStatusDeleted}).
- And(builder.Eq{"`question`.`show`": entity.QuestionShow})
-
- argsQ = append(argsQ, entity.QuestionStatusDeleted, entity.QuestionShow)
- argsA = append(argsA, entity.QuestionStatusDeleted,
entity.AnswerStatusDeleted, entity.QuestionShow)
+ // create builder
+ b = builder.MySQL().Select(qfs...).From("`question`").
+ LeftJoin("`answer`", "`question`.`id` =
`answer`.`question_id`").
+ Where(builder.Lt{"`question`.`status`":
entity.QuestionStatusDeleted}).
+ And(builder.Eq{"`question`.`show`": entity.QuestionShow}).
+ And(builder.Lt{"`answer`.`status`": entity.AnswerStatusDeleted})
+ args = append(args, entity.QuestionStatusDeleted, entity.QuestionShow,
entity.AnswerStatusDeleted)
- likeConQ := builder.NewCond()
- likeConA := builder.NewCond()
+ // add content like match conditions
+ likeCon := builder.NewCond()
for _, word := range words {
- likeConQ = likeConQ.Or(builder.Like{"title", word}).
- Or(builder.Like{"original_text", word})
- argsQ = append(argsQ, "%"+word+"%")
- argsQ = append(argsQ, "%"+word+"%")
-
- likeConA = likeConA.Or(builder.Like{"`answer`.original_text",
word})
- argsA = append(argsA, "%"+word+"%")
+ likeCon = likeCon.Or(builder.Like{"`question`.`title`", word}).
+ Or(builder.Like{"`question`.`original_text`", word}).
+ Or(builder.Like{"`answer`.original_text", word})
+ args = append(args, "%"+word+"%")
+ args = append(args, "%"+word+"%")
+ args = append(args, "%"+word+"%")
}
-
- b.Where(likeConQ)
- ub.Where(likeConA)
+ b.Where(likeCon)
// check tag
for ti, tagID := range tagIDs {
@@ -155,53 +142,25 @@ func (sr *searchRepo) SearchContents(ctx context.Context,
words []string, tagIDs
ast + ".tag_id": tagID,
ast + ".status": entity.TagRelStatusAvailable,
})
- ub.Join("INNER", "tag_rel as "+ast, "question_id =
"+ast+".object_id").
- And(builder.Eq{
- ast + ".tag_id": tagID,
- ast + ".status": entity.TagRelStatusAvailable,
- })
- argsQ = append(argsQ, entity.TagRelStatusAvailable, tagID)
- argsA = append(argsA, entity.TagRelStatusAvailable, tagID)
+ args = append(args, entity.TagRelStatusAvailable, tagID)
}
// check user
if userID != "" {
- b.Where(builder.Eq{"question.user_id": userID})
- ub.Where(builder.Eq{"answer.user_id": userID})
- argsQ = append(argsQ, userID)
- argsA = append(argsA, userID)
- }
-
- // check vote
- if votes == 0 {
- b.Where(builder.Eq{"question.vote_count": votes})
- ub.Where(builder.Eq{"answer.vote_count": votes})
- argsQ = append(argsQ, votes)
- argsA = append(argsA, votes)
- } else if votes > 0 {
- b.Where(builder.Gte{"question.vote_count": votes})
- ub.Where(builder.Gte{"answer.vote_count": votes})
- argsQ = append(argsQ, votes)
- argsA = append(argsA, votes)
- }
-
- //b = b.Union("all", ub)
- ubSQL, _, err := ub.ToSQL()
- if err != nil {
- return
- }
- bSQL, _, err := b.ToSQL()
- if err != nil {
- return
+ userCond := builder.NewCond()
+ userCond.Or(builder.Eq{"question.user_id": userID}).
+ Or(builder.Eq{"answer.user_id": userID})
+ args = append(args, userID)
+ args = append(args, userID)
+ b.Where(userCond)
}
- sql := fmt.Sprintf("(%s UNION ALL %s)", bSQL, ubSQL)
- countSQL, _, err := builder.MySQL().Select("count(*) total").From(sql,
"c").ToSQL()
+ countSQL, _, err := builder.MySQL().Select("count(*) total").From(b,
"c").ToSQL()
if err != nil {
return
}
- querySQL, _, err := builder.MySQL().Select("*").From(sql,
"t").OrderBy(sr.parseOrder(ctx, order)).Limit(size, page-1).ToSQL()
+ querySQL, _, err := b.OrderBy(sr.parseOrder(ctx, order)).Limit(size,
page-1).ToSQL()
if err != nil {
return
}
@@ -210,12 +169,10 @@ func (sr *searchRepo) SearchContents(ctx context.Context,
words []string, tagIDs
countArgs := []interface{}{}
queryArgs = append(queryArgs, querySQL)
- queryArgs = append(queryArgs, argsQ...)
- queryArgs = append(queryArgs, argsA...)
+ queryArgs = append(queryArgs, args...)
countArgs = append(countArgs, countSQL)
- countArgs = append(countArgs, argsQ...)
- countArgs = append(countArgs, argsA...)
+ countArgs = append(countArgs, args...)
res, err := sr.data.DB.Context(ctx).Query(queryArgs...)
if err != nil {
@@ -236,7 +193,7 @@ func (sr *searchRepo) SearchContents(ctx context.Context,
words []string, tagIDs
}
// 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, votes, views, answers int, page, size int,
order string) (resp []*schema.SearchResult, total int64, err error) {
words = filterWords(words)
var (
qfs = qFields
@@ -255,6 +212,15 @@ func (sr *searchRepo) SearchQuestions(ctx context.Context,
words []string, tagID
b.Where(builder.Lt{"`question`.`status`":
entity.QuestionStatusDeleted}).And(builder.Eq{"`question`.`show`":
entity.QuestionShow})
args = append(args, entity.QuestionStatusDeleted, entity.QuestionShow)
+ // check vote
+ if votes == 0 {
+ b.And(builder.Eq{"question.vote_count": votes})
+ args = append(args, votes)
+ } else if votes > 0 {
+ b.And(builder.Gte{"question.vote_count": votes})
+ args = append(args, votes)
+ }
+
likeConQ := builder.NewCond()
for _, word := range words {
likeConQ = likeConQ.Or(builder.Like{"title", word}).
diff --git a/internal/service/search_common/search.go
b/internal/service/search_common/search.go
index d1aea547..3ff83957 100644
--- a/internal/service/search_common/search.go
+++ b/internal/service/search_common/search.go
@@ -26,8 +26,8 @@ import (
)
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)
- 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)
+ SearchContents(ctx context.Context, words []string, tagIDs []string,
userID string, page, size int, order string) (resp []*schema.SearchResult,
total int64, err error)
+ SearchQuestions(ctx context.Context, words []string, tagIDs []string,
notAccepted bool, votes, 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)
ParseSearchPluginResult(ctx context.Context, sres
[]plugin.SearchResult) (resp []*schema.SearchResult, err error)
}
diff --git a/internal/service/search_parser/search_parser.go
b/internal/service/search_parser/search_parser.go
index a5dfd670..ae0363d0 100644
--- a/internal/service/search_parser/search_parser.go
+++ b/internal/service/search_parser/search_parser.go
@@ -56,12 +56,15 @@ func (sp *SearchParser) ParseStructure(ctx context.Context,
dto *schema.SearchDT
// match tags
cond.Tags = sp.parseTags(ctx, &query)
- // match all
+ //default: match all
cond.UserID = sp.parseUserID(ctx, &query, dto.UserID)
- cond.VoteAmount = sp.parseVotes(&query)
cond.Words = sp.parseWithin(&query)
// match questions
+ cond.VoteAmount = sp.parseVotes(&query)
+ if cond.VoteAmount > -1 {
+ cond.TargetType = constant.QuestionObjectType
+ }
cond.NotAccepted = sp.parseNotAccepted(&query)
if cond.NotAccepted {
cond.TargetType = constant.QuestionObjectType
@@ -101,6 +104,14 @@ func (sp *SearchParser) ParseStructure(ctx
context.Context, dto *schema.SearchDT
if len(cond.Words) > limitWords {
cond.Words = cond.Words[:limitWords]
}
+
+ // only tags, not words and search is default, only search question
+ if len(cond.Words) == 0 &&
+ len(cond.Tags) > 0 &&
+ cond.TargetType != constant.QuestionObjectType &&
+ cond.TargetType != constant.AnswerObjectType {
+ cond.TargetType = constant.QuestionObjectType
+ }
return
}
diff --git a/internal/service/search_service.go
b/internal/service/search_service.go
index 46d71374..73c96436 100644
--- a/internal/service/search_service.go
+++ b/internal/service/search_service.go
@@ -69,10 +69,10 @@ func (ss *SearchService) Search(ctx context.Context, dto
*schema.SearchDTO) (res
if finder == nil {
if cond.SearchAll() {
resp.SearchResults, resp.Total, err =
- ss.searchRepo.SearchContents(ctx, cond.Words,
cond.Tags, cond.UserID, cond.VoteAmount, dto.Page, dto.Size, dto.Order)
+ ss.searchRepo.SearchContents(ctx, cond.Words,
cond.Tags, cond.UserID, dto.Page, dto.Size, dto.Order)
} else if cond.SearchQuestion() {
resp.SearchResults, resp.Total, err =
- ss.searchRepo.SearchQuestions(ctx, cond.Words,
cond.Tags, cond.NotAccepted, cond.Views, cond.AnswerAmount, dto.Page, dto.Size,
dto.Order)
+ ss.searchRepo.SearchQuestions(ctx, cond.Words,
cond.Tags, cond.NotAccepted, cond.VoteAmount, cond.Views, cond.AnswerAmount,
dto.Page, dto.Size, dto.Order)
} else if cond.SearchAnswer() {
resp.SearchResults, resp.Total, err =
ss.searchRepo.SearchAnswers(ctx, cond.Words,
cond.Tags, cond.Accepted, cond.QuestionID, dto.Page, dto.Size, dto.Order)