`
suichangkele
  • 浏览: 200300 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

lucene的spanNearQuery(二)——不带有顺序的

 
阅读更多

 如果你觉得我的代码里有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.Net 2.3.1开发介绍 —— 二、分词(四),这是一个系列的文档,太多了,只好分开

    开发自己的搜索引擎——Lucene+Heritrix(第2版)_随书光盘.rar

    《开发自己的搜索引擎——Lucene+Heritrix(第2版)_随书光盘.rar》是一个包含资源的压缩包,主要用于帮助读者深入理解并实践搜索引擎的开发。Lucene和Heritrix是两个重要的开源工具,它们在构建搜索引擎的过程中起着...

    开发自己的搜索引擎——Lucene+Heritrix

    《开发自己的搜索引擎——Lucene+Heritrix》是一本深入探讨如何构建自定义搜索引擎的书籍,结合了Apache Lucene和Heritrix两个强大的开源工具。Lucene是Java开发的全文检索库,而Heritrix则是一款功能丰富的网络爬虫...

    lucene in action 第二版

    第一版发布之后,由于其内容的全面性和实用性,获得了广泛的好评,因此第二版的推出对于希望学习最新版本Lucene的读者来说非常有价值。 ### 描述知识点: 描述中提到的“有很多简单明了的demo”,指的是这本书中...

    Lucene初试——关于大文本建立索引和中文乱码以及QueryParser检索的一些体会 - sheen口开河 - CSDN博客

    【Lucene大文本建索引】 在使用Lucene处理大文本时,遇到的主要问题是内存溢出。当尝试一次性处理200M左右的文本时,可能会遇到`java.lang.OutOfMemoryError: Java heap space`错误。这主要是由于Lucene在内存中缓冲...

    Java搜索工具——Lucene实例总结(一)

    Java搜索工具——Lucene实例总结(一) 在Java开发中,搜索引擎已经成为不可或缺的一部分,而Apache Lucene正是一个强大的全文搜索引擎库。这篇博文将带你深入理解Lucene的基本概念和使用方式,帮助你快速入门并掌握...

    基于Java的全文索引检索引擎——Lucene

    【基于Java的全文索引检索引擎——Lucene】 Lucene是一个用Java编写的开源全文检索引擎库,由Doug Cutting创建并贡献给Apache基金会,成为Jakarta项目的一部分,后来成为Apache软件基金会下的顶级项目。它的主要...

    lucene in action_中文版(lucene实战)

    本书深入浅出地介绍了Lucene——一个开源的使用Java语言编写的全文搜索引擎开发包。它通过浅显的语言、大量的图注、丰富的代码示例,以及清晰的结构为读者呈现出作为优秀开源项目的Lucene 所体现的强大功能。全书共...

    Lucene实战(中文版第二版)对应Lucene版本

    《Lucene实战(中文版第二版)》是针对搜索引擎开发领域的经典著作,它详细介绍了如何使用Apache Lucene这个强大的全文搜索引擎库。Lucene是Java语言实现的开源项目,被广泛应用于各种信息检索系统中,包括网站搜索...

    lucene实战第二版(最新)

    本书《Lucene实战第二版》是一本关于如何使用Lucene进行文本检索的实用教程。这本书详细介绍了Lucene的使用方法和内部工作机制,并提供了丰富的代码示例和清晰的解释。它不仅适合那些计划在应用中使用Lucene的开发者...

    Lucene In Action 第二版 高清中文版+附书源代码

    《Lucene In Action 第二版》是一本深入探讨Apache Lucene全文搜索引擎库的专业书籍,高清中文版的提供为中文读者提供了便利。这本书由Michael McCandless等作者编写,旨在帮助开发者充分利用Lucene的强大功能,构建...

    lucene-2.3.1.jar

    首先,让我们聚焦于Lucene的核心——API。Lucene 2.3.1.jar包含了完整的Java API,它使得开发者能够轻松地构建高效、灵活的全文检索应用。API包括了索引创建、查询解析、结果排序等一系列功能,提供了诸如Document、...

    Lucene实战第二版中英文PDF(带书签)

    12. **书签功能**:提供的PDF版本带有书签,方便读者快速定位到感兴趣的章节和内容,提高学习效率。 总的来说,《Lucene实战第二版》是开发者深入理解和应用Lucene的宝贵资源,无论你是初次接触还是寻求进阶,都能...

    lucene,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开源全文搜索引擎开发包的书籍。Lucene是一个用Java编写的功能强大的全文搜索引擎库,它以出色的可扩展性和快速的搜索特性获得了广泛的赞誉。本书详细介绍了如何有效...

    开发自己的搜索引擎——Lucene+Heritrix(第2版)_含书(PDF)和光盘

    《开发自己的搜索引擎——Lucene+Heritrix(第2版)》是一本深入探讨如何构建搜索引擎的专著,其中包含了Lucene和Heritrix两个关键工具的详细使用指南。这本书旨在帮助开发者理解搜索引擎的工作原理,并提供实践性的...

    lucene in action 2nd edition, lucene in action 第二版 PDF

    《Lucene in Action 第二版》是一本深入探讨Apache Lucene全文检索库的专业书籍,它在Java开发领域具有很高的权威性。这本书详细介绍了如何利用Lucene进行高效的文本搜索和索引构建,是Java开发者和信息检索爱好者的...

    lucene中的SpanQuery和PhraseQuery详解(有图示)

    它有六个子类,即 SpanTermQuery、SpanNearQuery、SpanOrQuery、SpanNotQuery、SpanFirstQuery和FieldMaskingSpanQuery。 1. SpanTermQuery SpanTermQuery是一个基本的 span 查询,用于查询指定字段中包含某个词...

    Lucene实战(第二版)

    《Lucene实战(第二版)》是一本深入探讨Apache Lucene全文搜索引擎库的权威书籍,主要面向对Java和搜索引擎技术感兴趣的开发者。这本书详尽地介绍了如何利用Lucene进行信息检索、文本分析和索引构建,同时也涵盖了...

Global site tag (gtag.js) - Google Analytics