searcher.Search(types.SearchRequest{Text: "百度中国"})

// 查找满足搜索条件的文档,此函数线程安全
func (engine *Engine) Search(request types.SearchRequest) (output types.SearchResponse) {
if !engine.initialized {
log.Fatal("必须先初始化引擎")
} var rankOptions types.RankOptions
if request.RankOptions == nil {
rankOptions = *engine.initOptions.DefaultRankOptions
} else {
rankOptions = *request.RankOptions
}
if rankOptions.ScoringCriteria == nil {
rankOptions.ScoringCriteria = engine.initOptions.DefaultRankOptions.ScoringCriteria
} // 收集关键词
tokens := []string{}
if request.Text != "" {
querySegments := engine.segmenter.Segment([]byte(request.Text))
for _, s := range querySegments {
token := s.Token().Text()
if !engine.stopTokens.IsStopToken(token) {
tokens = append(tokens, s.Token().Text())
}
}
} else {
for _, t := range request.Tokens {
tokens = append(tokens, t)
}
} // 建立排序器返回的通信通道
rankerReturnChannel := make(
chan rankerReturnRequest, engine.initOptions.NumShards) // 生成查找请求
lookupRequest := indexerLookupRequest{
countDocsOnly: request.CountDocsOnly,
tokens: tokens,
labels: request.Labels,
docIds: request.DocIds,
options: rankOptions,
rankerReturnChannel: rankerReturnChannel,
orderless: request.Orderless,
} // 向索引器发送查找请求
for shard := ; shard < engine.initOptions.NumShards; shard++ {
engine.indexerLookupChannels[shard] <- lookupRequest
} // 从通信通道读取排序器的输出
numDocs :=
rankOutput := types.ScoredDocuments{}
timeout := request.Timeout
isTimeout := false
if timeout <= {
// 不设置超时
for shard := ; shard < engine.initOptions.NumShards; shard++ {
rankerOutput := <-rankerReturnChannel
if !request.CountDocsOnly {
for _, doc := range rankerOutput.docs {
rankOutput = append(rankOutput, doc)
}
}
numDocs += rankerOutput.numDocs
}
} else {
// 设置超时
deadline := time.Now().Add(time.Millisecond * time.Duration(request.Timeout))
for shard := ; shard < engine.initOptions.NumShards; shard++ {
select {
case rankerOutput := <-rankerReturnChannel:
if !request.CountDocsOnly {
for _, doc := range rankerOutput.docs {
rankOutput = append(rankOutput, doc)
}
}
numDocs += rankerOutput.numDocs
case <-time.After(deadline.Sub(time.Now())):
isTimeout = true
break
}
}
} // 再排序
if !request.CountDocsOnly && !request.Orderless {
if rankOptions.ReverseOrder {
sort.Sort(sort.Reverse(rankOutput))
} else {
sort.Sort(rankOutput)
}
} // 准备输出
output.Tokens = tokens
// 仅当CountDocsOnly为false时才充填output.Docs
if !request.CountDocsOnly {
if request.Orderless {
// 无序状态无需对Offset截断
output.Docs = rankOutput
} else {
var start, end int
if rankOptions.MaxOutputs == {
start = utils.MinInt(rankOptions.OutputOffset, len(rankOutput))
end = len(rankOutput)
} else {
start = utils.MinInt(rankOptions.OutputOffset, len(rankOutput))
end = utils.MinInt(start+rankOptions.MaxOutputs, len(rankOutput))
}
output.Docs = rankOutput[start:end]
}
}
output.NumDocs = numDocs
output.Timeout = isTimeout
return
}

索引器接受查找请求:

func (engine *Engine) indexerLookupWorker(shard int) {
for {
request := <-engine.indexerLookupChannels[shard] // 关键 var docs []types.IndexedDocument
var numDocs int
if request.docIds == nil {
docs, numDocs = engine.indexers[shard].Lookup(request.tokens, request.labels, nil, request.countDocsOnly)
} else {
docs, numDocs = engine.indexers[shard].Lookup(request.tokens, request.labels, request.docIds, request.countDocsOnly)
} if request.countDocsOnly {
request.rankerReturnChannel <- rankerReturnRequest{numDocs: numDocs}
continue
} if len(docs) == {
request.rankerReturnChannel <- rankerReturnRequest{}
continue
} if request.orderless {
var outputDocs []types.ScoredDocument
for _, d := range docs {
outputDocs = append(outputDocs, types.ScoredDocument{
DocId: d.DocId,
TokenSnippetLocations: d.TokenSnippetLocations,
TokenLocations: d.TokenLocations})
} request.rankerReturnChannel <- rankerReturnRequest{
docs: outputDocs,
numDocs: len(outputDocs),
}
continue
} rankerRequest := rankerRankRequest{
countDocsOnly: request.countDocsOnly,
docs: docs,
options: request.options,
rankerReturnChannel: request.rankerReturnChannel,
}
engine.rankerRankChannels[shard] <- rankerRequest
}
}

lookup函数实现:

// 查找包含全部搜索键(AND操作)的文档
// 当docIds不为nil时仅从docIds指定的文档中查找
func (indexer *Indexer) Lookup(
tokens []string, labels []string, docIds map[uint64]bool, countDocsOnly bool) (docs []types.IndexedDocument, numDocs int) {
if indexer.initialized == false {
log.Fatal("索引器尚未初始化")
} indexer.DocInfosShard.RLock()
defer indexer.DocInfosShard.RUnlock() if indexer.DocInfosShard.NumDocuments == {
return
}
numDocs = // 合并关键词和标签为搜索键
keywords := make([]string, len(tokens)+len(labels))
copy(keywords, tokens)
copy(keywords[len(tokens):], labels) indexer.InvertedIndexShard.RLock() table := make([]*types.KeywordIndices, len(keywords))
for i, keyword := range keywords {
indices, found := indexer.InvertedIndexShard.InvertedIndex[keyword]
if !found {
// 当反向索引表中无此搜索键时直接返回
indexer.InvertedIndexShard.RUnlock()
return
} else {
// 否则加入反向表中
table[i] = indices
}
// 当没有找到时直接返回
if len(table) == {
indexer.InvertedIndexShard.RUnlock()
return
} // 归并查找各个搜索键出现文档的交集
// 从后向前查保证先输出DocId较大文档
indexPointers := make([]int, len(table))
for iTable := ; iTable < len(table); iTable++ {
indexPointers[iTable] = indexer.getIndexLength(table[iTable]) -
}
// 平均文本关键词长度,用于计算BM25
avgDocLength := indexer.InvertedIndexShard.TotalTokenLength / float32(indexer.DocInfosShard.NumDocuments)
indexer.InvertedIndexShard.RUnlock() for ; indexPointers[] >= ; indexPointers[]-- {
// 以第一个搜索键出现的文档作为基准,并遍历其他搜索键搜索同一文档
baseDocId := indexer.getDocId(table[], indexPointers[]) // 全局范围查找目标文档是否存在
if _, ok := indexer.DocInfosShard.DocInfos[baseDocId]; !ok {
// if !IsDocExist(baseDocId) {
// 文档信息中不存在反向索引文档时,跳过
// 该情况由不对称删除操作所造成
continue
} if docIds != nil {
_, found := docIds[baseDocId]
if !found {
continue
}
}
iTable :=
found := true
for ; iTable < len(table); iTable++ {
// 二分法比简单的顺序归并效率高,也有更高效率的算法,
// 但顺序归并也许是更好的选择,考虑到将来需要用链表重新实现
// 以避免反向表添加新文档时的写锁。
// TODO: 进一步研究不同求交集算法的速度和可扩展性。
position, foundBaseDocId := indexer.searchIndex(table[iTable],
, indexPointers[iTable], baseDocId)
if foundBaseDocId {
indexPointers[iTable] = position
} else {
if position == {
// 该搜索键中所有的文档ID都比baseDocId大,因此已经没有
// 继续查找的必要。
return
} else {
// 继续下一indexPointers[0]的查找
indexPointers[iTable] = position -
found = false
break
}
}
} if found {
indexedDoc := types.IndexedDocument{} // 当为LocationsIndex时计算关键词紧邻距离
if indexer.initOptions.IndexType == types.LocationsIndex {
// 计算有多少关键词是带有距离信息的
numTokensWithLocations :=
for i, t := range table[:len(tokens)] {
if len(t.Locations[indexPointers[i]]) > {
numTokensWithLocations++
}
}
if numTokensWithLocations != len(tokens) {
if !countDocsOnly {
docs = append(docs, types.IndexedDocument{
DocId: baseDocId,
})
}
numDocs++
break
}
// 计算搜索键在文档中的紧邻距离
tokenProximity, tokenLocations := computeTokenProximity(table[:len(tokens)], indexPointers, tokens)
indexedDoc.TokenProximity = int32(tokenProximity)
indexedDoc.TokenSnippetLocations = tokenLocations // 添加TokenLocations
indexedDoc.TokenLocations = make([][]int, len(tokens))
for i, t := range table[:len(tokens)] {
indexedDoc.TokenLocations[i] = t.Locations[indexPointers[i]]
}
} // 当为LocationsIndex或者FrequenciesIndex时计算BM25
if indexer.initOptions.IndexType == types.LocationsIndex ||
indexer.initOptions.IndexType == types.FrequenciesIndex {
bm25 := float32()
d := indexer.DocInfosShard.DocInfos[baseDocId].TokenLengths
for i, t := range table[:len(tokens)] {
var frequency float32
if indexer.initOptions.IndexType == types.LocationsIndex {
frequency = float32(len(t.Locations[indexPointers[i]]))
} else {
frequency = t.Frequencies[indexPointers[i]]
} // 计算BM25
if len(t.DocIds) > && frequency > && indexer.initOptions.BM25Parameters != nil && avgDocLength != {
// 带平滑的idf
idf := float32(math.Log2(float64(indexer.DocInfosShard.NumDocuments)/float64(len(t.DocIds)) + ))
k1 := indexer.initOptions.BM25Parameters.K1
b := indexer.initOptions.BM25Parameters.B
bm25 += idf * frequency * (k1 + ) / (frequency + k1*(-b+b*d/avgDocLength))
}
}
indexedDoc.BM25 = float32(bm25)
} indexedDoc.DocId = baseDocId
if !countDocsOnly {
docs = append(docs, indexedDoc)
}
numDocs++
}
}
return
}

wukong引擎源码分析之搜索——docid有序的数组里二分归并求交集,如果用跳表的话,在插入索引时会更快的更多相关文章

  1. wukong引擎源码分析之索引——part 1 倒排列表本质是有序数组存储

    searcher.IndexDocument(0, types.DocumentIndexData{Content: "此次百度收购将成中国互联网最大并购"}) engine.go ...

  2. wukong引擎源码分析之索引——part 3 文档评分 无非就是将docid对应的fields信息存储起来,为搜索结果rank评分用

    之前的文章分析过,接受索引请求处理的代码在segmenter_worker.go里: func (engine *Engine) segmenterWorker() { for { request : ...

  3. wukong引擎源码分析之索引——part 2 持久化 直接set(key,docID数组)在kv存储里

    前面说过,接收indexerRequest的代码在index_worker.go里: func (engine *Engine) indexerAddDocumentWorker(shard int) ...

  4. 【OpenCV】SIFT原理与源码分析:关键点搜索与定位

    <SIFT原理与源码分析>系列文章索引:http://www.cnblogs.com/tianyalu/p/5467813.html 由前一步<DoG尺度空间构造>,我们得到了 ...

  5. Elasticsearch源码分析—线程池(十一) ——就是从队列里处理请求

    Elasticsearch源码分析—线程池(十一) 转自:https://www.felayman.com/articles/2017/11/10/1510291570687.html 线程池 每个节 ...

  6. 【源码分析】Mybatis使用中,同一个事物里,select查询不出之前insert的数据

    一.问题场景模拟问题:第二次查询和第一次查询结果一模一样,没有查询出我新插入的数据 猜测:第二次查询走了Mybatis缓存 疑问:那为什么会走缓存呢? 1.service方法 @Override @T ...

  7. 转:Irrlicht 0.1引擎源码分析与研究(一)

    目录(?)[-] 主要技术特性 引擎概览 Irrlicht的窗口管理   Irrlicht引擎主要是由一个名叫Nikolaus Gebhardt奥地利人所设计,是sourceforge上的一个开源项目 ...

  8. MQTT再学习 -- MQTT 客户端源码分析

    MQTT 源码分析,搜索了一下发现网络上讲的很少,多是逍遥子的那几篇. 参看:逍遥子_mosquitto源码分析系列 参看:MQTT libmosquitto源码分析 参看:Mosquitto学习笔记 ...

  9. ArrayList 源码分析和自定义ArrayList实现

    概述 ArrayList 是基于数组实现的,是一个能自动扩展的动态数组. ArrayList 是线程不安全的,多线程情况下添加元素会出现数组越界的情况,而且数组赋值操作不是原子操作,会导致多线程情况下 ...

随机推荐

  1. hdu 4778 Gems Fight! 状压dp

    转自wdd :http://blog.csdn.net/u010535824/article/details/38540835 题目链接:hdu 4778 状压DP 用DP[i]表示从i状态选到结束得 ...

  2. PAT (Advanced Level) 1086. Tree Traversals Again (25)

    入栈顺序为先序遍历,出栈顺序为中序遍历. #include<cstdio> #include<cstring> #include<cmath> #include&l ...

  3. ffmpeg 时间戳

    转http://blog.csdn.net/yfh1985sdq/article/details/5721953 AVpacket里的时间戳pts和dts.单位好像是us. 问 : 时间戳pts和dt ...

  4. luogu P1351 联合权值

    题目描述 无向连通图G 有n 个点,n - 1 条边.点从1 到n 依次编号,编号为 i 的点的权值为W i ,每条边的长度均为1 .图上两点( u , v ) 的距离定义为u 点到v 点的最短距离. ...

  5. java中简单内存计算

    今天面试遇到一个问题,假设一个类中只声明一个int类型,那么这个对象多大,这里先写出解决方案,首先引入内存计算工具lucene-core, <dependency> <groupId ...

  6. Linux下xz与tar的区别

    同一文件,tar.xz格式比tar.gz格式小了三分之一! 说明: xz是一个使用LZMA压缩算法的无损数据压缩文件格式. 和gzip与bzip2一样,同样支持多文件压缩,但是约定不能将多于一个的目标 ...

  7. iOS开发-用keychain替代UDID

    从2013-5-1日开始苹果就禁止对UUID的应用的通过了.所以我们需要用一些办法替换,下面我就是用keychain的访问替换掉UUID的. 那么,关于Keychain的应用,Apple提供了一个叫G ...

  8. python遍历两个列表,若长度不等,用None填充

    zip经常会遇到截断问题,如:a = [1,2,3], b = [4,5,6,7],则zip(a,b) = [(1, 4), (2, 5), (3, 6)] 可考虑使用map: map(lambda ...

  9. leetcode最长递增子序列问题

    题目描写叙述: 给定一个数组,删除最少的元素,保证剩下的元素是递增有序的. 分析: 题目的意思是删除最少的元素.保证剩下的元素是递增有序的,事实上换一种方式想,就是寻找最长的递增有序序列.解法有非常多 ...

  10. BeagleBone Black Industrial系统更新设置一贴通

    前言 原创文章,转载引用务必注明链接.水平有限,欢迎指正. 本文使用markdown写成,为获得更好的阅读体验,推荐访问我的博客原文: http://www.omoikane.cn/2016/09/1 ...