Files
common-server-public/bbs-go/server/internal/pkg/search/search.go
2025-03-03 17:43:50 +08:00

193 lines
4.6 KiB
Go

package search
import (
"bbs-go/internal/cache"
"bbs-go/internal/models"
html2 "bbs-go/internal/pkg/html"
"bbs-go/internal/pkg/markdown"
"bbs-go/internal/repositories"
"html"
"log/slog"
"math"
"time"
"github.com/blevesearch/bleve/v2"
"github.com/mitchellh/mapstructure"
"github.com/mlogclub/simple/common/dates"
"github.com/mlogclub/simple/common/strs"
"github.com/mlogclub/simple/sqls"
"github.com/spf13/cast"
)
var index bleve.Index
func Init(indexPath string) {
var err error
if index, err = bleve.Open(indexPath); err != nil {
if err == bleve.ErrorIndexPathDoesNotExist {
index = newIndex(indexPath)
} else {
slog.Error(err.Error())
}
}
}
func NewTopicDoc(topic *models.Topic) *TopicDocument {
if topic == nil {
return nil
}
doc := &TopicDocument{
Id: topic.Id,
NodeId: topic.NodeId,
UserId: topic.UserId,
Title: topic.Title,
Status: topic.Status,
Recommend: topic.Recommend,
CreateTime: topic.CreateTime,
}
// 处理内容
content := markdown.ToHTML(topic.Content)
content = html2.GetHtmlText(content)
content = html.EscapeString(content)
doc.Content = content
// 处理用户
user := cache.UserCache.Get(topic.UserId)
if user != nil {
doc.Nickname = user.Nickname
}
// 处理标签
tags := getTopicTags(topic.Id)
var tagsArr []string
for _, tag := range tags {
tagsArr = append(tagsArr, tag.Name)
}
tagsArr = append(tagsArr, "hello")
doc.Tags = tagsArr
return doc
}
func getTopicTags(topicId int64) []models.Tag {
topicTags := repositories.TopicTagRepository.Find(sqls.DB(), sqls.NewCnd().Where("topic_id = ?", topicId))
var tagIds []int64
for _, topicTag := range topicTags {
tagIds = append(tagIds, topicTag.TagId)
}
return cache.TagCache.GetList(tagIds)
}
// IndexData 索引数据
func UpdateTopicIndex(topic *models.Topic) {
doc := NewTopicDoc(topic)
if doc == nil {
return
}
err := index.Index(cast.ToString(topic.Id), doc)
if err != nil {
slog.Error(err.Error())
} else {
slog.Info("add topic search index", slog.Any("id", topic.Id))
}
}
func DeleteTopicIndex(id int64) error {
return index.Delete(cast.ToString(id))
}
// 分页查询
func SearchTopic(keyword string, nodeId int64, timeRange, page, limit int) (docs []TopicDocument, paging *sqls.Paging, err error) {
paging = &sqls.Paging{Page: page, Limit: limit}
query := bleve.NewBooleanQuery()
query.AddMust(bleve.NewMatchAllQuery())
if strs.IsNotBlank(keyword) {
query.AddMust(bleve.NewMatchQuery(keyword))
}
if nodeId != 0 {
if nodeId == -1 { // 推荐
boolFieldQuery := bleve.NewBoolFieldQuery(true)
boolFieldQuery.SetField("recommend")
query.AddMust(boolFieldQuery)
} else {
f := float64(nodeId)
b := true
nodeIdQuery := bleve.NewNumericRangeInclusiveQuery(&f, &f, &b, &b)
nodeIdQuery.SetField("nodeId")
query.AddMust(nodeIdQuery)
}
}
if timeRange != 0 {
var beginTime int64
if timeRange == 1 { // 一天内
beginTime = dates.Timestamp(time.Now().Add(-24 * time.Hour))
} else if timeRange == 2 { // 一周内
beginTime = dates.Timestamp(time.Now().Add(-7 * 24 * time.Hour))
} else if timeRange == 3 { // 一月内
beginTime = dates.Timestamp(time.Now().AddDate(0, -1, 0))
} else if timeRange == 4 { // 一年内
beginTime = dates.Timestamp(time.Now().AddDate(-1, 0, 0))
}
min := float64(beginTime)
max := float64(math.MaxInt64)
createTimeQuery := bleve.NewNumericRangeQuery(&min, &max)
createTimeQuery.SetField("createTime")
query.AddMust(createTimeQuery)
}
searchRequest := bleve.NewSearchRequest(query)
searchRequest.From = paging.Offset()
searchRequest.Size = paging.Limit
searchRequest.Fields = []string{"*"}
searchRequest.Highlight = bleve.NewHighlightWithStyle("html")
searchRequest.Highlight.AddField("title")
searchRequest.Highlight.AddField("content")
result, err := index.Search(searchRequest)
if err != nil {
slog.Error("搜索失败:", slog.Any("err", err))
}
for _, hit := range result.Hits {
storedDoc := make(map[string]interface{})
for key, field := range hit.Fields {
storedDoc[key] = field
}
for field, fragments := range hit.Fragments {
if len(fragments) > 0 {
storedDoc[field] = fragments[0]
}
}
if tagField, ok := storedDoc["tags"]; ok {
switch v := tagField.(type) {
case string:
storedDoc["tags"] = []string{v}
case []interface{}:
var tags []string
for _, tag := range v {
tags = append(tags, tag.(string))
}
storedDoc["tags"] = tags
}
}
var doc TopicDocument
if err := mapstructure.Decode(storedDoc, &doc); err != nil {
slog.Error(err.Error())
}
docs = append(docs, doc)
}
return
}