Lucene查找过程源码分析
从最简单的开始分析:根据某个字段搜索到匹配的文档,这个过程就像是从词典找到一个拼音同音字组成的词语有哪些一样,主要分为两步
1、从字典的检索目录找到所有对应的页码
2、找到对应页码查看其组成的词语有哪些
数据
// 数据
Product product_1 = new Product(1, "华为手机", 3000, 10, "华为.jpg", "华为", "300*300", 5);
Product product_2 = new Product(2, "苹果手机", 8000, 30, "苹果.jpg", "苹果", "500*500", 15);
// 指定字段为name
QueryParser queryParser = new QueryParser("name", analyzer);
// 指定内容为手机
Query query = queryParser.parse("手机");
// 搜索,取10条
TopDocs topDocs_10 = indexSearcher.search(query, 10);
从上面的伪代码可以到两份数据,分别是两个品牌的手机数据,我们希望找到所有name为手机的文档(这里假定name字段被分词为华为 苹果 手机三个term)
查找
IndexSearcher
IndexSearcher
是整个搜索过程的核心类。它利用IndexReader
读取索引数据,并根据查询条件执行搜索。
search()
public void search(Query query, Collector results)
throws IOException {
query = rewrite(query);
search(leafContexts, createWeight(query, results.needsScores(), 1), results);
}
Weight
Weight
是Lucene中一个抽象类,它代表了查询的“权重”。在搜索过程中,每个查询都会生成一个Weight
对象,它负责处理查询的具体执行逻辑和评分机制。
createWeight()
public Weight createWeight(IndexSearcher searcher, boolean needsScores, float boost) throws IOException {
final IndexReaderContext context = searcher.getTopReaderContext();
final TermContext termState;
if (perReaderTermState == null
|| perReaderTermState.wasBuiltFor(context) == false) {
if (needsScores) {
// 构建tremState加速查找
termState = TermContext.build(context, term);
}
...
}
termState
非常重要,实际构建完毕后就相当于已经找到了term对应的文档信息
public static TermContext build(IndexReaderContext context, Term term)
throws IOException {
...
for (final LeafReaderContext ctx : context.leaves()) {
if (terms != null) {
final TermsEnum termsEnum = terms.iterator();
// 根据term搜索相关文档
if (termsEnum.seekExact(bytes)) {
final TermState termState = termsEnum.termState();
perReaderTermState.register(termState, ctx.ord, termsEnum.docFreq(), termsEnum.totalTermFreq());
}
}
}
return perReaderTermState;
}
SegmentTermsEnum
SegmentTermsEnum
是 Lucene 中用于遍历和查找术语(term)的一个类,特别是在索引的某个段(segment)中。它是 Lucene 的 TermsEnum
的一个实现,主要用于对特定的索引段中的术语进行精确定位和迭代。
seekCeil()
代码省略
...
我们知道term字典在Lucene中实际使用FST进行存储,这里方法中便是尝试从所有词项(例子的name的词项为:华为 苹果 手机)中逐个匹配是否存在,这样我们就确定了term存在于哪些文档
TermQuery
TermQuery
它用于精确匹配单个术语(term)。在 Lucene 中,术语通常指的是一个字段的某个具体的值,比如一个词语或数字。TermQuery
是最基本的查询类型之一,适用于需要查找完全匹配的文档的场景
scorer()
public Scorer scorer(LeafReaderContext context) throws IOException {
// 找到term的对应的迭代器
final TermsEnum termsEnum = getTermsEnum(context);
if (termsEnum == null) {
return null;
}
// 找到term索引的迭代器
PostingsEnum docs = termsEnum.postings(null, needsScores ? PostingsEnum.FREQS : PostingsEnum.NONE);
assert docs != null;
return new TermScorer(this, docs, similarity.simScorer(stats, context));
}
termsEnum
这个词项的迭代器实际上是从上面的termState
获取的,拿到了它就相当于拿到了所有我们查找文档的基本信息
PostingsEnum
则可以认为是对文档的索引信息迭代器(还包含其他信息),拿到了它就相当于我们明确了查找文档的准确信息
总结
到这里,一个ES精准查询的查找部分的主脉络就算结束了,但实际的查询会更为复杂,包括包含多字段时需要进行数据的合并,根据文档的分数进一步筛选,范围查询、模糊查询等复杂的情况