- 浏览: 284661 次
- 性别:
- 来自: 湖南岳阳
-
最新评论
-
ternus:
兄弟,我用boboBrowse 也遇到了排序的问题,上线了讨论 ...
lucene 分组 bobo-Browse 排序的问题 -
luli0822:
Awesome bookmarks of those guru ...
流行的jQuery信息提示插件(jQuery Tooltip Plugin) -
shenbai:
如果你要在前台运行,你应该run得是ElasticSearch ...
ElasticSearch 源码分析 环境入门 -
cl1154781231:
<s:peroperty value="#at ...
关于Struts2中标签的一些心得 -
RonQi:
转载的吗?http://blog.csdn.net/stray ...
利用bobo-browse 实现lucene的分组统计功能
关于Hits类。
这个Hits类可是非常的重要,因为Lucene使用了缓存机制,关于缓存的实现就是在这个Hits类中。Hits工作过程中,使用了LRU算法,即通过一个HitDoc结构来实现一个双向链表,使用LRU置换算法,记录用户最近访问过的Document。
开门见山,直接拿出Hits类的实现代码来说话。
package org.apache.lucene.search;
import java.io.IOException;
import java.util.Vector;
import java.util.Iterator;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.CorruptIndexException;
public final class Hits {
private Weight weight;
private Searcher searcher;
private Filter filter = null;
private Sort sort = null;
private int length; // Hits的长度,即满足查询的结果数量
private Vector hitDocs = new Vector(); // 用作缓存检索结果的(Hit)
private HitDoc first; // head of LRU cache
private HitDoc last; // tail of LRU cache
private int numDocs = 0; // number cached
private int maxDocs = 200; // max to cache
Hits(Searcher s, Query q, Filter f) throws IOException {
weight = q.weight(s);
searcher = s;
filter = f;
getMoreDocs(50); // retrieve 100 initially | 从缓存中取出检索结果,如果缓存为null,则需要查询,查询后将结果加入缓存中
}
Hits(Searcher s, Query q, Filter f, Sort o) throws IOException {
weight = q.weight(s);
searcher = s;
filter = f;
sort = o;
getMoreDocs(50); //
retrieve 100 initially | 从缓存中取出检索结果,如果缓存为null,则需要查询,查询后将结果加入缓存中
}
/**
* 将满足检索结果的Document加入到缓存hitDocs中
*/
private final void getMoreDocs(int min) throws IOException {
/////////////////////////////////////////////////////////////////////////////////////////////
System.out.println("■■■■■■■■■■■■■■■■■■■■■■■■进入getMoreDocs()方法中时,hitDocs.size="+hitDocs.size());
///////////////////////////////////////////////////////////////////////////////////////////
if (hitDocs.size() > min) {
min = hitDocs.size();
}
int n = min * 2; // 扩充缓存容量为默认的2倍(默认最小情况下,也要扩充缓存。即使检索结果为1条记录,缓存的长度也扩充为100)
TopDocs topDocs = (sort == null) ? searcher.search(weight, filter, n) : searcher.search(weight, filter, n, sort);
length = topDocs.totalHits;
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
float scoreNorm = 1.0f;
if (length > 0 && topDocs.getMaxScore() > 1.0f) {
scoreNorm = 1.0f / topDocs.getMaxScore();
}
int end = scoreDocs.length < length ? scoreDocs.length : length;
for (int i = hitDocs.size(); i < end; i++) {
hitDocs.addElement(new HitDoc(scoreDocs[i].score * scoreNorm,
scoreDocs[i].doc));
}
/////////////////////////////////////////////////////////////////////////////////////////////
System.out.println("■■■■■■■■■■■■■■■■■■■■■■■■离开getMoreDocs()方法中时,hitDocs.size="+hitDocs.size());
///////////////////////////////////////////////////////////////////////////////////////////
}
// 返回Hits的长度,即满足查询的Document的数量,并非是缓存Vector hitDocs的长度
public final int length() {
return length;
}
// 根据Document的编号获取到Document
public final Document doc(int n) throws CorruptIndexException, IOException {
/////////////////////////////////////////////////////////////////////////////////////////////
System.out.println("hitDocs.size()="+hitDocs.size());
/////////////////////////////////////////////////////////////////////////////////////////////
HitDoc hitDoc = hitDoc(n);
// Update LRU cache of documents
remove(hitDoc); // remove from list, if there
addToFront(hitDoc); // add to front of list
if (numDocs > maxDocs) { // if cache is full
HitDoc oldLast = last;
remove(last); // flush last
oldLast.doc = null; // let doc get gc'd
}
if (hitDoc.doc == null) {
hitDoc.doc = searcher.doc(hitDoc.id); // cache miss: read document
}
return hitDoc.doc;
}
// 得到第n个Document的得分
public final float score(int n) throws IOException {
return hitDoc(n).score;
}
// 得到第n个Document的编号
public final int id(int n) throws IOException {
return hitDoc(n).id;
}
public Iterator iterator() {
return new HitIterator(this);
}
private final HitDoc hitDoc(int n) throws IOException {
if (n >= length) {
throw new IndexOutOfBoundsException("Not a valid hit number: " + n);
}
if (n >= hitDocs.size()) {
getMoreDocs(n);
}
return (HitDoc) hitDocs.elementAt(n);
}
private final void addToFront(HitDoc hitDoc) { // insert at front of cache
if (first == null) {
last = hitDoc;
} else {
first.prev = hitDoc;
}
hitDoc.next = first;
first = hitDoc;
hitDoc.prev = null;
numDocs++;
}
private final void remove(HitDoc hitDoc) { // remove from cache
if (hitDoc.doc == null) { // it's not in the list
return; // abort
}
if (hitDoc.next == null) {
last = hitDoc.prev;
} else {
hitDoc.next.prev = hitDoc.prev;
}
if (hitDoc.prev == null) {
first = hitDoc.next;
} else {
hitDoc.prev.next = hitDoc.next;
}
numDocs--;
}
}
final class HitDoc {
float score;
int id;
Document doc = null;
HitDoc next; // in doubly-linked cache
HitDoc prev; // in doubly-linked cache
HitDoc(float s, int i) {
score = s;
id = i;
}
}
上面代码中,红色标注的部分为后面测试之用。
一次查询时,需要构造一个Query实例。从Hits类的成员变量来看,在检索的过程中,一个Query实例并不是只使用一次,那么多次使用进行查询就需要记录这个Query实例的状态。
为了更加直观,写了一个测试类,来观察缓存长度的分配情况:
package org.shirdrn.lucene.learn.test;
import java.io.IOException;
import java.util.Date;
import java.util.Iterator;
import org.apache.lucene.analysis.cjk.CJKAnalyzer;
import org.apache.lucene.document.DateTools;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.search.Hit;
import org.apache.lucene.search.Hits;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.store.LockObtainFailedException;
public class MyHitsTest {
public void create() throws CorruptIndexException, LockObtainFailedException, IOException{
String indexPath = "H:\\index";
IndexWriter writer = new IndexWriter(indexPath,new CJKAnalyzer(),true);
for(int i=0;i<500;i++){
Document doc = new Document();
doc.add(new Field("title","搜索引擎收录新页面与文章原创性问题",Field.Store.YES,Field.Index.TOKENIZED));
doc.add(new Field("date",
DateTools.timeToString((new Date().getTime()), DateTools.Resolution.MINUTE),
Field.Store.YES, Field.Index.UN_TOKENIZED));
doc.add(new Field("author","Shirdrn",Field.Store.YES,Field.Index.UN_TOKENIZED));
String contents = "如果分词后的多个关键字,在关键文章分词中,某一关键片段中出现的关键频率最高,这个关键片段当然是Google检索结果呈现的那两行关键摘要。";
doc.add(new Field("contents",contents,Field.Store.NO,Field.Index.TOKENIZED));
writer.addDocument(doc);
}
writer.optimize();
writer.close();
}
public void search() throws CorruptIndexException, IOException{
Query query = new TermQuery(new Term("contents","关键"));
String indexPath = "H:\\index";
IndexSearcher searcher = new IndexSearcher(indexPath);
Hits hits = searcher.search(query);
System.out.println("★★★共检索出满足的结果 "+hits.length()+" 条。");
Iterator it = hits.iterator();
while(it.hasNext()){
System.out.print("★调用Hits的doc()方法,(Vector): ");
hits.doc(0); //执行这一行代码是为了观察:当获取一个Document的时候缓存的长度,因为第一次查看缓存的时候其长度是为0的,如果检索结果数量不为0则之后缓存长度是不为0的,至少为100
Hit hit = (Hit)it.next();
System.out.println("★检索到的Hit的ID为 : "+hit.getId());
}
}
}
class MyTest{
public static void main(String[] args) throws CorruptIndexException, LockObtainFailedException, IOException, ParseException {
MyHitsTest hitsTest = new MyHitsTest();
//hitsTest.create();
hitsTest.search();
}
}
首先要构造500个Document,建立索引,之后,执行检索的操作,结果如下所示:
■■■■■■■■■■■■■■■■■■■■■■■■进入getMoreDocs()方法中时,hitDocs.size=0
■■■■■■■■■■■■■■■■■■■■■■■■离开getMoreDocs()方法中时,hitDocs.size=100
★★★共检索出满足的结果 500 条。
★调用Hits的doc()方法,(Vector): hitDocs.size()=100★检索到的Hit的ID为 : 0
★调用Hits的doc()方法,(Vector): hitDocs.size()=100★检索到的Hit的ID为 : 1
★调用Hits的doc()方法,(Vector): hitDocs.size()=100★检索到的Hit的ID为 : 2
……
★调用Hits的doc()方法,(Vector): hitDocs.size()=100★检索到的Hit的ID为 : 98
★调用Hits的doc()方法,(Vector): hitDocs.size()=100★检索到的Hit的ID为 : 99
★调用Hits的doc()方法,(Vector): hitDocs.size()=100■■■■■■■■■■■■■■■■■■■■■■■■进入getMoreDocs()方法中时,hitDocs.size=100
■■■■■■■■■■■■■■■■■■■■■■■■离开getMoreDocs()方法中时,hitDocs.size=200
★检索到的Hit的ID为 : 100
★调用Hits的doc()方法,(Vector): hitDocs.size()=200★检索到的Hit的ID为 : 101
★调用Hits的doc()方法,(Vector): hitDocs.size()=200★检索到的Hit的ID为 : 102
……
★调用Hits的doc()方法,(Vector): hitDocs.size()=200★检索到的Hit的ID为 : 198
★调用Hits的doc()方法,(Vector): hitDocs.size()=200★检索到的Hit的ID为 : 199
★调用Hits的doc()方法,(Vector): hitDocs.size()=200■■■■■■■■■■■■■■■■■■■■■■■■进入getMoreDocs()方法中时,hitDocs.size=200
■■■■■■■■■■■■■■■■■■■■■■■■离开getMoreDocs()方法中时,hitDocs.size=400
★检索到的Hit的ID为 : 200
★调用Hits的doc()方法,(Vector): hitDocs.size()=400★检索到的Hit的ID为 : 201
★调用Hits的doc()方法,(Vector): hitDocs.size()=400★检索到的Hit的ID为 : 202
……
★调用Hits的doc()方法,(Vector): hitDocs.size()=400★检索到的Hit的ID为 : 398
★调用Hits的doc()方法,(Vector): hitDocs.size()=400★检索到的Hit的ID为 : 399
★调用Hits的doc()方法,(Vector): hitDocs.size()=400■■■■■■■■■■■■■■■■■■■■■■■■进入getMoreDocs()方法中时,hitDocs.size=400
■■■■■■■■■■■■■■■■■■■■■■■■离开getMoreDocs()方法中时,hitDocs.size=500
★检索到的Hit的ID为 : 400
★调用Hits的doc()方法,(Vector): hitDocs.size()=500★检索到的Hit的ID为 : 401
★调用Hits的doc()方法,(Vector): hitDocs.size()=500★检索到的Hit的ID为 : 402
……
由结果可见看到,构造一个Hits的实例的时候,调用getMoreDocs()方法。
第一次进入getMoreDocs()方法时,hitDocs.size() = 0 > min = 50不成立,接着n = min*2 = 50*2 = 100,因此离开getMoreDocs()方法时hitDocs.size() = 100;
第二次进入getMoreDocs()方法时,hitDocs.size() = 100 > min = 50成立,从而设置min = hitDocs.size() = 100,接着n = min*2 = 100*2 = 200, 因此离开getMoreDocs()方法时hitDocs.size() = 200;
第三次进入getMoreDocs()方法时,hitDocs.size() = 200 > min = 100成立,从而设置min = hitDocs.size() = 200,接着n = min*2 = 200*2 = 400, 因此离开getMoreDocs()方法时hitDocs.size() = 400;
如果满足查询的检索结果的Document数量足够大的话,应该继续是:
第四次进入getMoreDocs()方法时,hitDocs.size() = 400,离开getMoreDocs()方法时hitDocs.size() = 800;
第五次进入getMoreDocs()方法时,hitDocs.size() = 800,离开getMoreDocs()方法时hitDocs.size() = 1600;
……
根据上面,最后一次(第四次)进入getMoreDocs()方法的时候,hitDocs.size() = 400 > min = 400不成立,接着n = min*2 = 400*2 = 800,此时虽然缓存扩充了,但是执行searcher.search(weight, filter, n) 的时候取到了100条满足条件的Document,故而缓存的实际大小为hitDocs.size() = 500, 因此离开getMoreDocs()方法时hitDocs.size() = 500,其实此次如果满足查询的Document数量足够,可以达到hitDocs.size() = 800。
发表评论
-
全文检索的基本原理
2010-02-25 10:22 876一、总论 根据http://lucene.apache.or ... -
lucene 分组 bobo-Browse 排序的问题
2010-02-01 16:18 2224今天碰到了一个问题,用bobo分组后对价格升序 居然100 ... -
开源搜索引擎
2010-02-01 14:31 1695开放源代码搜索引擎为 ... -
lucene中的filter器群组及其缓存大盘点
2010-01-20 23:18 1202lucene中的filter其实并不起眼,大家对其对性能的影响 ... -
利用bobo-browse 实现lucene的分组统计功能
2010-01-18 17:50 2940bobo-browse 是一用java写的lucene扩展组件 ... -
lucene Field部分参数设置含义
2009-11-07 17:51 1249<script type="text/ja ... -
刚下载,开始学习lucene时看的文章
2009-09-04 18:43 1431Lucene 2.0.0下载安装及测试 【下载】 下载链接 ... -
Lucene 2.3.1 阅读学习(41)
2009-09-04 18:42 1412当执行Hits htis = search(query);这一 ... -
Lucene-2.3.1 源代码阅读学习(40)
2009-09-04 18:41 989关于Lucene检索结果的排序问题。 已经知道,Lucene ... -
Lucene-2.3.1 源代码阅读学习(39)
2009-09-04 18:41 1155关于Lucene得分的计算。 在IndexSearcher类 ... -
Lucene-2.3.1 源代码阅读学习(39)
2009-09-04 18:38 565关于Lucene得分的计算。 在IndexSearcher类 ... -
Lucene-2.3.1 源代码阅读学习(38)
2009-09-04 18:38 922关于QueryParser。 QueryParser是用来解 ... -
Lucene-2.3.1 源代码阅读学习(37)
2009-09-04 18:37 630关于MultiTermQuery查询。 这里研究继承自Mul ... -
Lucene-2.3.1 源代码阅读学习(36)
2009-09-04 18:37 808关于MultiTermQuery查询。 ... -
Lucene-2.3.1 源代码阅读学习(35)
2009-09-04 18:36 842关于MultiPhraseQuery(多短语查询)。 Mul ... -
Lucene-2.3.1 源代码阅读学习(34)
2009-09-04 18:36 640关于PhraseQuery。 PhraseQuery查询是将 ... -
Lucene-2.3.1 源代码阅读学习(33)
2009-09-04 18:35 870关于范围查询RangeQuery。 ... -
Lucene-2.3.1 源代码阅读学习(32)
2009-09-04 18:35 1156关于SpanQuery(跨度搜索),它是Query的子类,但是 ... -
Lucene-2.3.1 源代码阅读学习(31)
2009-09-04 18:34 873关于前缀查询PrefixQuery(前缀查询)。 准备工作就 ... -
Lucene-2.3.1 源代码阅读学习(30)
2009-09-04 18:34 1063关于Query的学习。 主要使用TermQuery和Bool ...
相关推荐
《Lucene-2.3.1 源代码阅读学习》 Lucene是Apache软件基金会的一个开放源码项目,它是一个高性能、全文本搜索库,为开发者提供了在Java应用程序中实现全文检索功能的基础架构。本篇文章将深入探讨Lucene 2.3.1版本...
《Lucene 2.3.1.jar:洞察搜索引擎的核心技术》 在信息技术的海洋中,搜索引擎扮演着至关重要的角色,而Lucene则是其中的一颗璀璨明珠。作为一个开源全文检索库,Lucene为开发者提供了强大的文本搜索功能。在这里,...
lucene-core-2.3.1.jar
这个版本的Elasticsearch是基于Lucene库构建的,适用于各种用途,包括日志分析、监控、信息检索以及大数据的存储和分析。 在Linux环境下安装Elasticsearch 2.3.1,首先需要确保系统满足以下前提条件: 1. **Java...
apache-nutch-2.3.1-src.tar ,网络爬虫的源码, 用ivy2管理, ant runtime 编译 apache-nutch-2.3.1-src.tar ,网络爬虫的源码, 用ivy2管理, ant runtime 编译
lucene.net2.3.1开源项目 lucene是一个开源的全文检索项目,原本是java版的,后来就有了.net版,我上传的这个是.net的2.3.1版本,帮助大家解决全文检索引擎。
这个文档对lucene进行了介绍并且进行深入的使用,文档分开发了,网络太慢了。。。
Elasticsearch是一个强大的开源搜索引擎,基于Lucene库构建,提供了分布式、实时、高可用的全文检索服务。在2.3.1版本中,它已经集成了两种常用的中文分词器:IK分词器和Ansj分词器,以及Head插件,这为中文文档的...
lucene.net2.3.1开源项目 lucene是一个开源的全文检索项目,原本是java版的,后来就有了.net版,我上传的这个是.net的2.3.1版本。解决全文检索引擎。此为一部分,二部分打开地址...
lucene.net 2.3.1,基于lucene.net可构建强大的搜索功能。
Lucene.Net 2.3.1开发介绍 —— 二、分词(四),这是一个系列的文档,太多了,只好分开
标题“lucene2.3.1”指的是Lucene的2.3.1版本,这个版本在当时是广泛使用的,具有一定的稳定性和成熟度。 Lucene的核心功能包括文档索引、搜索以及结果排序。其工作原理可以分为以下几个关键步骤: 1. 文档分析与...
7. **Lucene.Net-2.3.1-VS2005.csproj** 这是 Visual Studio 2005 项目的配置文件,包含了构建 Lucene.Net 2.3.1 版本所需的编译指令、依赖项和项目设置。开发者可以使用这个文件在 Visual Studio 中打开和构建源...
**Lucene.Net 2.9.1:一个强大的全文搜索引擎库** **一、Lucene.Net 简介** Lucene.Net 是 Apache Lucene 搜索引擎库的 .NET 版本...通过深入学习和实践,开发者可以利用 Lucene.Net 构建出高效、准确的全文搜索系统。
《Lucene.NET 2.3.1与盘古分词结合的深度实践》 在信息检索和搜索引擎领域,Lucene.NET作为一个开源、高性能的全文检索库,被广泛应用于各种应用场景。而盘古分词作为针对中文处理的优秀分词工具,能够有效地对中文...
描述中提到的“lucene-2.3.1-src”很可能是源码压缩包,开发者可以通过源码了解其内部实现细节,或者进行二次开发。在实际应用中,我们通常会关注以下几个关键步骤: 1. 创建索引:定义一个Document对象,包含要...
### Lucene.NET 2.3.1 学习笔记 #### 一、Lucene.NET 简介 Lucene.NET 是一个高性能、全功能的文本搜索引擎库,它为.NET平台提供了一个灵活且强大的全文检索解决方案。该库允许开发者将搜索功能集成到应用程序中,...
### 示例:使用 Lucene.NET 2.3.1 版本 对于 `lucene.net-2.3.1` 这个版本,你需要按照当时的 API 文档进行操作。首先,创建索引目录,然后实例化 `IndexWriter`,接着使用 `Document` 类添加文档,最后关闭 `...
**Lucene.Net 2.3.1:全中文解析与应用指南** Lucene.Net是一个开源、高性能的全文检索库,它是Apache Lucene项目在.NET平台上的实现。这个库为开发者提供了一种简单而强大的方式来集成全文搜索功能到他们的应用...