简介
使用redis实现全部文章精确到段落的搜索
实现思路
- 文章分段,使用一张表单独记录下段落信息
- 段落分词,把段落划分成词
- 分词后使用有序集合记录到redis中,每个词语后记录含有该分词的段落ID集
- 使用一个哈希键记录下每个段落的分词,用于分词信息的删除
- 查询时先分词,再根据分的词把分词查到的对应的段落
- 返回结果
具体实现
文章分段
- 文章段落表结构
type TextModel struct {
gorm.Model
ArticleID uint `json:"articleID"`
Head string `json:"head"`
Body string `json:"body"`
}
- 分段函数
//这里因为我的项目里出现了循环导包,所以没有使用gorm的映射表进行处理
type TextModel struct {
ArticleID uint `json:"article_id"`
Head string `json:"head"`
Body string `json:"body"`
}
func MdContentTransformation(id uint, title string, content string) (list []TextModel) {
lines := strings.Split(content, "\n")
var headList []string
var bodyList []string
var body string
headList = append(headList, title)
var flag bool
for _, line := range lines {
if strings.HasPrefix(line, "```") {
flag = !flag
}
if !flag && strings.HasPrefix(line, "#") {
// 标题行
headList = append(headList, getHead(line))
if strings.TrimSpace(body) != "" {
bodyList = append(bodyList, getBody(body))
}
body = ""
continue
}
body += line
}
if body != "" {
bodyList = append(bodyList, getBody(body))
}
if len(headList) > len(bodyList) {
bodyList = append(bodyList, "")
}
if len(headList) != len(bodyList) {
log.Errorf("headList与bodyList 不一致 \n headList:%q %d\\\n bodyList: %q %d\n", headList, len(headList), bodyList, len(bodyList))
return
}
for i := 0; i < len(headList); i++ {
list = append(list, TextModel{
ArticleID: id,
Head: headList[i],
Body: bodyList[i],
})
}
return
}
func getHead(head string) string {
s := strings.TrimSpace(strings.Join(strings.Split(head, " ")[1:], " "))
return s
}
func getBody(body string) string {
body = strings.TrimSpace(body)
return body
}
段落分词
- 使用第三方库进行分词
https://github.com/go-ego/gse
第三方库下载
go get -u github.com/go-ego/gse
- 库初始化
func InitGse() {
newGse, _ := gse.New()
global_gse.Gse = newGse
}
- 分词函数
func textParticiple(textList ...string) (words []string) {
for _, text := range textList {
word := global_gse.Gse.CutSearch(text, true)
words = append(words, word...)
}
return
}
把分过的词保存到到redis中
- 使用redis集合每个词对应的段落ID
func SetTextSearchIndex(textID uint, words []string) {
for _, word := range words {
if word == "" {
continue
}
global.Redis.SAdd(fmt.Sprintf("text_%s", word), textID)
}
}
- 使用哈希键记录每个文章对应的段落和词语信息,用于信息删除
func SetTextSearchWords(articleID uint, textID uint, words []string) {
_words, _ := json.Marshal(words)
global.Redis.HSet(fmt.Sprintf("text_search_words_%d", articleID), strconv.Itoa(int(textID)), _words)
}
查询操作
- 通过将文本分词过后的查询词语获取一个段落集合
func GetTextSearchIndex(text string) []string {
//分词
words := global_gse.Gse.CutSearch(text, true)
var _words []string
for _, word := range words {
_words = append(_words, fmt.Sprintf("text_%s", word))
}
vals, _ := global.Redis.SUnion(_words...).Result()
return vals
}
- 根据查到id列表查询数据库
idList := redis_article.GetTextSearchIndex(cr.Key)
query := global.DB.Where("id in ?", idList)
后续处理,使搜索的文字有高亮提示
- 可以将搜索的文字套在特定的标签中然后再返回信息