如果你觉得我的代码里有bug,可以联系我,我的qq是1308567317 。
一年前写了一篇文章介绍有顺序的spanNearQuery,地址为:http://suichangkele.iteye.com/blog/2348256,现在才会想起来,还有一篇没写呢,即没有顺序的spanNearQuery,今天在看之前的博客的时候,突然发现,所以写一下吧。
没有顺序的spanQuery的意思是,只要多个span挨着足够近的距离即可,不需要按照一定的顺序出现,而且也没有不能重叠的限制(在有顺序的SpanNearQuery中是有不能重叠的这个限制的)。在SpanNearQuery.getSpans(AtomicReaderContext, Bits, Map<Term, TermContext>)方法,即获得span的方法中
public Spans getSpans(final AtomicReaderContext context, Bits acceptDocs, Map<Term, TermContext> termContexts) throws IOException { if (clauses.size() == 0) // optimize 0-clause case return new SpanOrQuery(getClauses()).getSpans(context, acceptDocs, termContexts); if (clauses.size() == 1) // optimize 1-clause case return clauses.get(0).getSpans(context, acceptDocs, termContexts); return inOrder ? (Spans) new NearSpansOrdered(this, context, acceptDocs, termContexts, collectPayloads) : (Spans) new NearSpansUnordered(this, context, acceptDocs, termContexts); }
可以发现,如果没有指定顺序,则返回一个NearSpansUnordered,看看这个类的代码。
private SpanNearQuery query; /** 存储的额是封装了span的东西,按照用户查询的顺序存储的 */ private List<SpansCell> ordered = new ArrayList<>(); // spans in query order /** 所有的span */ private Spans[] subSpans; private int slop; // from query //这个链表是按照从小到大的顺序存放的,按照span的大小存放的。用于移动span到同一个doc /**链表的头*/ private SpansCell first; // linked list of spans /**链表的尾*/ private SpansCell last; // sorted by doc only /** 多个subSpan(即子span)内部的长度,在计算整个slope的时候,要减去 */ private int totalLength; // sum of current lengths /** 优先队列,用于找到当前最小的span和按照从小到大的顺序生成队列 */ private CellQueue queue; // sorted queue of spans /** 所有的span中当前读取的doc最大的span或者是相同的doc中位置最后的span, 可以直接理解为位置最后的span */ private SpansCell max; // max element in queue private boolean more = true; // true iff not done private boolean firstTime = true; // true before first next() public NearSpansUnordered(SpanNearQuery query, AtomicReaderContext context, Bits acceptDocs, Map<Term, TermContext> termContexts) throws IOException { this.query = query; this.slop = query.getSlop(); SpanQuery[] clauses = query.getClauses(); queue = new CellQueue(clauses.length);//优先队列, subSpans = new Spans[clauses.length]; for (int i = 0; i < clauses.length; i++) { SpansCell cell = new SpansCell(clauses[i].getSpans(context, acceptDocs, termContexts), i);//这个就是封装了一个span,用来在更新span的next的时候记录所有的span中最大(即doc最大或者同样的doc但是位置最后)的span。 ordered.add(cell);//这个是一个链表,在将多个span移动到同一个doc的时候使用,就像booleanQuery在合并多个AND的倒排表的时候的逻辑一样。 subSpans[i] = cell.spans; } }
他是将span用SpanCell封装了,封装的目的是在调用spanCell的next的时候,会顺别找到所有的span中的最大值(判断的标准在下面有)
private class SpansCell extends Spans { /** 封装的span */ private Spans spans; private SpansCell next; /**封装的span当前匹配的长度*/ private int length = -1; private int index; public SpansCell(Spans spans, int index) { this.spans = spans; this.index = index; } //他的next方法会更新max,因为优先队列是个最小堆,只能找到最小的对象,所以最大的对象要单独维护。 @Override public boolean next() throws IOException { return adjust(spans.next()); } @Override public boolean skipTo(int target) throws IOException { return adjust(spans.skipTo(target)); } /**更新max值 */ private boolean adjust(boolean condition) { if (length != -1) { totalLength -= length; //减去当前的长度,重新计算长度 } if (condition) { length = end() - start(); totalLength += length; // add new length if (max == null || doc() > max.doc() || (doc() == max.doc()) && (end() > max.end())) {//这个就是最大的值得判断条件,如果docid更大,则更大,如果docid相等,则找位置最大的。 max = this; } } more = condition; return condition; } 还有很多方法省略,都是直接调用的封装的span的方法。 }
看看最重要的next方法:
@Override public boolean next() throws IOException { if (firstTime) {// initList(true); listToQueue();//构建优先队列 firstTime = false; } else if (more) { if (min().next()) { // 更新堆鼎的对象,读取下一个位置,现在可能就不在同一个doc上了 queue.updateTop(); // maintain queue } else { more = false; } } while (more) { boolean queueStale = false;//是否优先队列失效,即要不要重新构建优先队列 if (min().doc() != max.doc()) {//如果不是在同一个doc上,则重置链表,按照span大小排序,因为下面要移动到一个doc上,此时最好用链表,因为使用优先队列的成本太大了。这里体现了使用优先队列的用处,即用于构建一个有顺序的链表。 queueToList(); queueStale = true; } // 将所有的span都移动到一个doc上!这里不重新构建优先队列,因为代价太大。 while (more && first.doc() < last.doc()) { more = first.skipTo(last.doc()); firstToLast(); // and move it to the end queueStale = true; } if (!more) return false; if (queueStale) {//重建优先队列,因为这样建立的话,成本会比较小 listToQueue(); queueStale = false; } if (atMatch()) {//如果满足条件,则找到了 return true; }s more = min().next();//如果不满足条件,即最大的位置和最小的位置的差太大,则读取最小的位置,这个方法中就会更新max的值。min方法就是获得优先队列中堆顶的元素 if (more) { queue.updateTop(); //更新队列中最小的对象 } } return false; // no more matches }
queueToList这个方法用于构建一个链表,他的目的是用于将所有的span移动到同一个doc上,做这个操作要使用链表,使用链表的原因是使用优先队列的话会比较耗时。处处体现了solr的作者的精明与仔细。这么优秀的代码,太值得学习了
还剩最后一个关键的代码atMatch,即判断当前所有的span的位置是否满足条件,别忘了现在所有的span都在同一个doc上,
private boolean atMatch() { return (min().doc() == max.doc()) && ((max.end() - min().start() - totalLength) <= slop); }
max就是位置最后的span,min就是位置最前的span,他们现在在同一个doc上,要求是最后一个位置的结束位置减去最前面的位置的开始位置小于slop,还要减去这其中每个span的位置的长度,(这个地方有点让我很疑惑,为啥要减去呢?但是没大问题,我们可以修改一下这个的代码,去掉这一部分)。
对于让我很疑惑的totalLength,我自己做了一个实验,我用 java php cpp net c go erlang这几个词做的索引,然后使用java go erlang查询,slope是3的时候就查不到,但是大于3就可以查到,可以erlang这个span的end是7,java的start是0,如果没有totalLength的话,明显是不匹配得,所以这个totalLength应该是考虑了span的个数。如果读者有更好的理解的话,可以联系我,我的qq是1308567317
这样就看完了没有顺序的。他的要求是在一个doc上最后面的一个位置减去最前面的额位置的差小于一个值,但是也考虑了span的个数(即totalLength)。
相关推荐
Lucene.Net 2.3.1开发介绍 —— 二、分词(四),这是一个系列的文档,太多了,只好分开
《开发自己的搜索引擎——Lucene+Heritrix(第2版)_随书光盘.rar》是一个包含资源的压缩包,主要用于帮助读者深入理解并实践搜索引擎的开发。Lucene和Heritrix是两个重要的开源工具,它们在构建搜索引擎的过程中起着...
《开发自己的搜索引擎——Lucene+Heritrix》是一本深入探讨如何构建自定义搜索引擎的书籍,结合了Apache Lucene和Heritrix两个强大的开源工具。Lucene是Java开发的全文检索库,而Heritrix则是一款功能丰富的网络爬虫...
第一版发布之后,由于其内容的全面性和实用性,获得了广泛的好评,因此第二版的推出对于希望学习最新版本Lucene的读者来说非常有价值。 ### 描述知识点: 描述中提到的“有很多简单明了的demo”,指的是这本书中...
【Lucene大文本建索引】 在使用Lucene处理大文本时,遇到的主要问题是内存溢出。当尝试一次性处理200M左右的文本时,可能会遇到`java.lang.OutOfMemoryError: Java heap space`错误。这主要是由于Lucene在内存中缓冲...
Java搜索工具——Lucene实例总结(一) 在Java开发中,搜索引擎已经成为不可或缺的一部分,而Apache Lucene正是一个强大的全文搜索引擎库。这篇博文将带你深入理解Lucene的基本概念和使用方式,帮助你快速入门并掌握...
【基于Java的全文索引检索引擎——Lucene】 Lucene是一个用Java编写的开源全文检索引擎库,由Doug Cutting创建并贡献给Apache基金会,成为Jakarta项目的一部分,后来成为Apache软件基金会下的顶级项目。它的主要...
本书深入浅出地介绍了Lucene——一个开源的使用Java语言编写的全文搜索引擎开发包。它通过浅显的语言、大量的图注、丰富的代码示例,以及清晰的结构为读者呈现出作为优秀开源项目的Lucene 所体现的强大功能。全书共...
《Lucene实战(中文版第二版)》是针对搜索引擎开发领域的经典著作,它详细介绍了如何使用Apache Lucene这个强大的全文搜索引擎库。Lucene是Java语言实现的开源项目,被广泛应用于各种信息检索系统中,包括网站搜索...
本书《Lucene实战第二版》是一本关于如何使用Lucene进行文本检索的实用教程。这本书详细介绍了Lucene的使用方法和内部工作机制,并提供了丰富的代码示例和清晰的解释。它不仅适合那些计划在应用中使用Lucene的开发者...
《Lucene In Action 第二版》是一本深入探讨Apache Lucene全文搜索引擎库的专业书籍,高清中文版的提供为中文读者提供了便利。这本书由Michael McCandless等作者编写,旨在帮助开发者充分利用Lucene的强大功能,构建...
首先,让我们聚焦于Lucene的核心——API。Lucene 2.3.1.jar包含了完整的Java API,它使得开发者能够轻松地构建高效、灵活的全文检索应用。API包括了索引创建、查询解析、结果排序等一系列功能,提供了诸如Document、...
12. **书签功能**:提供的PDF版本带有书签,方便读者快速定位到感兴趣的章节和内容,提高学习效率。 总的来说,《Lucene实战第二版》是开发者深入理解和应用Lucene的宝贵资源,无论你是初次接触还是寻求进阶,都能...
lucene,lucene教程,lucene讲解。 为了对文档进行索引,Lucene 提供了五个基础的类 public class IndexWriter org.apache.lucene.index.IndexWriter public abstract class Directory org.apache.lucene.store....
Lucene实战第二版完整清晰中文版是一本介绍Lucene开源全文搜索引擎开发包的书籍。Lucene是一个用Java编写的功能强大的全文搜索引擎库,它以出色的可扩展性和快速的搜索特性获得了广泛的赞誉。本书详细介绍了如何有效...
《开发自己的搜索引擎——Lucene+Heritrix(第2版)》是一本深入探讨如何构建搜索引擎的专著,其中包含了Lucene和Heritrix两个关键工具的详细使用指南。这本书旨在帮助开发者理解搜索引擎的工作原理,并提供实践性的...
《Lucene in Action 第二版》是一本深入探讨Apache Lucene全文检索库的专业书籍,它在Java开发领域具有很高的权威性。这本书详细介绍了如何利用Lucene进行高效的文本搜索和索引构建,是Java开发者和信息检索爱好者的...
它有六个子类,即 SpanTermQuery、SpanNearQuery、SpanOrQuery、SpanNotQuery、SpanFirstQuery和FieldMaskingSpanQuery。 1. SpanTermQuery SpanTermQuery是一个基本的 span 查询,用于查询指定字段中包含某个词...
《Lucene实战(第二版)》是一本深入探讨Apache Lucene全文搜索引擎库的权威书籍,主要面向对Java和搜索引擎技术感兴趣的开发者。这本书详尽地介绍了如何利用Lucene进行信息检索、文本分析和索引构建,同时也涵盖了...