夜猫的小站

golang和jieba实现的一个简易的全文搜索引擎

Published on
阅读时间:4分钟636

本文最近一次更新于 1559 个天前,其中的内容很可能已经有所发展或是发生改变。

前言

由于博客的文章搜索需求,所以考虑实现简易的全文搜索引擎。基于这篇文章jieba的golang实现实现一个简易的全文搜索引擎。虽然最终也没有使用这种方式,算是扩展了下知识。

基于gin的简易代码

1.首先定义文档类型 document.go

import (
    "encoding/xml"
    "os"
)

type Document struct {
    Title string
    URL   string
    Text  string
    ID    int
}

// LoadData 加载表单数据
func loadData(c *gin.Context) ([]Document, error) {
 var docs []Document
 // 查找所有文章
 posts, _, err := models.FindAllPosts()
 if err != nil {
  return nil, err
 }
 // 创建文档
 for i := range posts {
  doc := Document{
   Title: posts[i].Title,
   URL:   "",
   Text:  posts[i].PlainContent,
   ID:    int(posts[i].ID),
  }
  docs = append(docs, doc)
 }
 return docs, nil
}

2.文本分析 tokenizer.go


import "github.com/yanyiwu/gojieba"

// analyze 使用jieba分词进行中文分词
func analyze(text string) []string {
 x := gojieba.NewJieba()
 defer x.Free()
 tokens := x.CutForSearch(text, true)
 return tokens
}

3.创建索引 index.go

type index map[string][]int

// add adds documents to the index.
func (idx index) add(docs []Document) {
 for _, doc := range docs {
  for _, token := range analyze(doc.Text) {
   ids := idx[token]
   if ids != nil && ids[len(ids)-1] == doc.ID {
    continue
   }
   idx[token] = append(ids, doc.ID)
  }
 }
}

// intersection 获取a、b数据的交集
// a and b 需要升序排序并且不包含重复
func intersection(a []int, b []int) []int {
 maxLen := len(a)
 if len(b) > maxLen {
  maxLen = len(b)
 }
 r := make([]int, 0, maxLen)
 var i, j int
 for i < len(a) && j < len(b) {
  if a[i] < b[j] {
   i++
  } else if a[i] > b[j] {
   j++
  } else {
   r = append(r, a[i])
   i++
   j++
  }
 }
 return r
}

// search queries the index for the given text.
func (idx index) search(text string) []int {
 var r []int
 for _, token := range analyze(text) {
  if ids, ok := idx[token]; ok {
   if r == nil {
    r = ids
   } else {
    r = intersection(r, ids)
   }
  } else {
   // Token doesn't exist.
   return nil
  }
 }
 return r
}

4.搜索 search.go

// Search 搜索
func Search(c *gin.Context) {
 s := "HTTPS 和 HTTP/2"
 // 加载文章
 docs, _ := loadData(c)
 // 创建索引
 idx := make(index)
 // 根据数据添加索引
 idx.add(docs)
 // 查找文本匹配索引id
 matchedIDs := idx.search(s)
 start := time.Now()
 log.Printf("Search found %d documents in %v", len(matchedIDs), time.Since(start))
}

5.原理

根据以上流程,可以得出全文搜索的原理

  1. 对于原本文章进行分词,创建关键词片段
  2. 关键词片段添加索引
  3. 开始搜索对,需要搜索的内容也进行分词,
  4. 根据分词内容查询索引,最终查询到搜索结果

参考资料