搜索引擎开发(三)引擎层部分设计 | 青训营笔记

125 阅读3分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的第5篇笔记

引擎层

引擎层是建议在索引层之上,和 Web 层进行交互从而生成执行方案的模块

比如用户发送 Http 请求创建一个索引,经过Web层的解析后把内容传递给引擎层, 引擎层就会执行对应创建索引的方法

引擎层主要提供的方法有:新增索引,新增文档,删除文档,搜索内容等

为了方便管理,给每个引擎类配置一个索引管理器,负责索引的一些操作

新增索引

根据 Web层 解析的请求参数,使用索引管理器创建一个新的索引

func (gde *GoDanceEngine) CreateIndex(params map[string]string, body []byte) error 

删除索引

同样调用索引管理器的删除索引方法

func (gde *GoDanceEngine) DeleteIndex(params map[string]string)

新增字段

根据请求参数,给指定的索引添加字段

func (gde *GoDanceEngine) AddField(indexName string, field segment.SimpleFieldInfo) error

文档增删改

根据请求的类型来进行对应的操作 POST : 新增文档 PUT : 修改文档 DELETE : 删除文档

func (gde *GoDanceEngine) DocumentOptions(method string, params map[string]string, body []byte) (string, error) {

   indexName, hasIndex := params["index"]
   if !hasIndex {
      return Fail, errors.New(ParamsError)
   }

   switch method {
   case "POST":
     // 新增文档
   case "DELETE":
     // 删除文档
   case "PUT":
     // 修改文档
      
   default:
      return Fail, errors.New(ParamsError)
   }
}

搜索文档

这是搜索引擎的核心功能,对 Web层 的请求参数进行解析从而进行对应的操作,我们的搜索引擎支持按日期过滤,关键词过滤等操作。

核心就是获取请求参数,根据请求参数来指定搜索和过滤的细节,最终确定要返回的结果。 我们的引擎层支持分页,搜索相关的必选请求参数有:index 索引名, pageSize:每页的大小,curPage:当前页数

还有一些用户自定义的字段,用于搜索。比如要搜索 author 是 TwinklingMeteor 的文档,只需要加上请求参数: author=TwinklingMeteor,在经过引擎层后,该参数会被解析成一个查询的结构,传递给下面的索引层。

func (gde *GoDanceEngine) Search(params map[string]string) (string, error) {

   startTime := time.Now()
   indexName, hasIndex := params["index"]
   pageSize, hasPageSize := params["pageSize"]
   curPage, hasCurPage := params["curPage"]
   

   if !hasIndex || !hasPageSize || !hasCurPage {
      return Fail, errors.New(ParamsError)
   }

   // 获取索引
   idx := gde.idxManager.GetIndex(indexName)
   if idx == nil {
      return NotFound, errors.New(IndexNotFound)
   }

   // 建立过滤条件和搜索条件
   searchFilters, searchQueries := gde.parseParams(params)
   docQueryNodes := make([]utils.DocIdNode, 0)
   docFilterIds := make([]uint64, 0)

   // 对每个 ids 求交集
   for _, query := range searchQueries {
      ids, ok := idx.SearchKeyDocIds(query)
   }

   // 对每个 Ids 求交集
   for _, filter := range searchFilters {
      ids, ok := idx.SearchFilterDocIds(filter)
   }

   //  对 docQueryNodes 和 docFilterIds求交集

   lens := int64(len(docQueryNodes))
   if lens == 0 {
      return NotFound, nil
   }

   //计算起始和终止位置
   start, end, err := gde.calcStartEnd(pageSize, curPage, lens)
   if err != nil {
      return NotFound, nil
   }

   var resultSet DefaultResult

   resultSet.Results = make([]map[string]string, 0)
   for _, docNode := range docQueryNodes[start:end] {
      doc, ok := idx.GetDocument(docNode.Docid)
      if ok {
         resultSet.Results = append(resultSet.Results, doc)
      }
   }

   resultSet.From = start + 1
   resultSet.To = end
   resultSet.Status = "OK"
   resultSet.TotalCount = lens
   endTime := time.Now()
   resultSet.CostTime = fmt.Sprintf("%v", endTime.Sub(startTime))

   r, err := json.Marshal(resultSet)
   if err != nil {
      return NotFound, err
   }

   return string(r), nil
}

总结

引擎层的开发难点就是作为一个连接Web服务和存储服务的桥梁,需要和双方的开发人员频繁沟通,制定好对应的结构体,接口,才能达到一个高效的开发。