这是我参与「第三届青训营 -后端场」笔记创作活动的第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服务和存储服务的桥梁,需要和双方的开发人员频繁沟通,制定好对应的结构体,接口,才能达到一个高效的开发。