- 浏览: 928296 次
- 性别:
- 来自: 北京
文章分类
- 全部博客 (445)
- 备忘 (0)
- java基础 (28)
- jsp (15)
- css (4)
- javascript (30)
- struts (7)
- servlet (2)
- struts2 (7)
- jdbc (16)
- hibernate (22)
- ibatis (0)
- jpa (1)
- spring (17)
- spring定时任务 (8)
- 整合开发 (12)
- JavaArticle (0)
- php (6)
- velocity (2)
- mysql (19)
- sqlserver (52)
- oracle (23)
- lucene (49)
- ajax (13)
- dwr (5)
- JFreeChart (1)
- service (14)
- tools (18)
- c#基础 (20)
- 程序安全 (0)
- 学习网站 (1)
- 社会需求 (2)
- flash (1)
- 流媒体 (1)
- java_code (1)
- htmlparser (1)
- 速动画教程 (5)
- 设计模式 (1)
- xml操作 (2)
- uml操作 (4)
- 测试 (1)
- linux (8)
- 版本控制 (4)
- 服务器 (12)
- 安全 (6)
- 美工 (2)
最新评论
-
Zhang_amao:
我想问一下, 你用的lucene版本和highligher的版 ...
使用Lucene的Highlighter实现文件摘要的自动提取 -
wangmengfanwangzhi:
博主,你的QQ是什么啊?有关于lucene的问题想要请教啊~~ ...
Lucene下载及测试 -
cutesunshineriver:
讲得很好,理解起来很顺,对个人学习的帮助性很大,谢谢博主。
velocity入门一 -
libin2722:
我这里有一个任务调度,在晚上3点时候会自动将数据库中某表的数据 ...
Lucene-2.2.0 源代码阅读学习(16) -
greatwqs:
java -cp $JVM_ARGS $classpath ...
java的cp命令
通过对DocumentWriter类的writePostings()方法进行学习。同时,研究并解决几个我一直感到困惑的几个类的用途,以及到底怎样阐述能使自己有一种感性的认识。
writePostings()方法的实现
writePostings()方法是对已经经过倒排的文档,将词条的一些有用信息写入到索引段文件中。该方法的实现如下所示:
private final void writePostings(Posting[] postings, String segment)
throws CorruptIndexException, IOException {
IndexOutput freq = null, prox = null;
TermInfosWriter tis = null; // TermInfosWriter类是与词条的写操作有关的
TermVectorsWriter termVectorWriter = null;
try {
// 打开文件流,为倒排的索引进行存储
freq = directory.createOutput(segment + ".frq"); // 打开segments.frq文件
prox = directory.createOutput(segment + ".prx"); // 打开segments.prx文件
tis = new TermInfosWriter(directory, segment, fieldInfos,termIndexInterval); // 创建一个TermInfosWriter对象
TermInfo ti = new TermInfo(); // 创建一个TermInfo对象,该对象用于在内存中管理词条的
String currentField = null;
boolean currentFieldHasPayloads = false;
for (int i = 0; i < postings.length; i++) { // 遍历Posting数组中的每以个对象
Posting posting = postings[i];
// 检查:是否需要转换成一个新的Field
String termField = posting.term.field();
if (currentField != termField) { // 从Posting数组中获取Field的名称(Strnig类型)如果不为null
// 改变currentField。看是否有需要存储的信息
currentField = termField;
FieldInfo fi = fieldInfos.fieldInfo(currentField); // 根据currentField名称从FieldInfos中找到这个FieldInfo对象
currentFieldHasPayloads = fi.storePayloads;
if (fi.storeTermVector) {
if (termVectorWriter == null) {
termVectorWriter =
new TermVectorsWriter(directory, segment, fieldInfos); // 构造一个TermVectorsWriter对象,该对象与词条向量的写操作相关
termVectorWriter.openDocument();
}
termVectorWriter.openField(currentField); // 根据指定的Field的名称currentField打开一个文件输出流
} else if (termVectorWriter != null) {
termVectorWriter.closeField();
}
}
// 为带有指针的sengments.frq文件和segments.prx文件设置一个入口
ti.set(1, freq.getFilePointer(), prox.getFilePointer(), -1); // ti是一个TernInfo类实例,用来管理词条的
tis.add(posting.term, ti); // tis是一个TermInfosWriter类实例,它的方法为add(Term term, TermInfo ti),将一个<Term, TermInfo>对加入到其中
// 为segments.frq文件添加一个入口
int postingFreq = posting.freq;
if (postingFreq == 1) // optimize freq=1
freq.writeVInt(1); // set low bit of doc num.
else {
freq.writeVInt(0); // the document number
freq.writeVInt(postingFreq); // frequency in doc
}
int lastPosition = 0; // write positions
int[] positions = posting.positions;
Payload[] payloads = posting.payloads;
int lastPayloadLength = -1;
// 下面是对词条Term的Payload和positions信息进行优化处理,写入输出到索引目录中
// The following encoding is being used for positions and payloads:
// Case 1: current field does not store payloads
// Positions -> <PositionDelta>^freq
// PositionDelta -> VInt
// The PositionDelta is the difference between the current
// and the previous position
// Case 2: current field stores payloads
// Positions -> <PositionDelta, Payload>^freq
// Payload -> <PayloadLength?, PayloadData>
// PositionDelta -> VInt
// PayloadLength -> VInt
// PayloadData -> byte^PayloadLength
// In this case PositionDelta/2 is the difference between
// the current and the previous position. If PositionDelta
// is odd, then a PayloadLength encoded as VInt follows,
// if PositionDelta is even, then it is assumed that the
// length of the current Payload equals the length of the
// previous Payload.
for (int j = 0; j < postingFreq; j++) { // 用希腊字母编码
int position = positions[j];
int delta = position - lastPosition;
if (currentFieldHasPayloads) {
int payloadLength = 0;
Payload payload = null;
if (payloads != null) {
payload = payloads[j];
if (payload != null) {
payloadLength = payload.length;
}
}
if (payloadLength == lastPayloadLength) {
// the length of the current payload equals the length
// of the previous one. So we do not have to store the length
// again and we only shift the position delta by one bit
prox.writeVInt(delta * 2);
} else {
// the length of the current payload is different from the
// previous one. We shift the position delta, set the lowest
// bit and store the current payload length as VInt.
prox.writeVInt(delta * 2 + 1);
prox.writeVInt(payloadLength);
lastPayloadLength = payloadLength;
}
if (payloadLength > 0) {
// write current payload
prox.writeBytes(payload.data, payload.offset, payload.length);
}
} else {
// field does not store payloads, just write position delta as VInt
prox.writeVInt(delta);
}
lastPosition = position;
}
if (termVectorWriter != null && termVectorWriter.isFieldOpen()) {
termVectorWriter.addTerm(posting.term.text(), postingFreq, posting.positions, posting.offsets);
}
}
if (termVectorWriter != null)
termVectorWriter.closeDocument();
} finally {
// 关闭所有的流对象
IOException keep = null;
if (freq != null) try { freq.close(); } catch (IOException e) { if (keep == null) keep = e; }
if (prox != null) try { prox.close(); } catch (IOException e) { if (keep == null) keep = e; }
if (tis != null) try { tis.close(); } catch (IOException e) { if (keep == null) keep = e; }
if (termVectorWriter != null) try { termVectorWriter.close(); } catch (IOException e) { if (keep == null) keep = e; }
if (keep != null) throw (IOException) keep.fillInStackTrace();
}
}
上面的writePostings()方法,将与词条相关的一些信息写入到索引目录中,指定的两个文件中,它们是.frq文件(与此条频率有关的)和.prx文件(该文件存储了与词条的位置有关的信息)。
[.fnm文件存储了一个Document中的所有Field的名称,即扩展名由来:Field's name]
关于Posting类
Posting类定义在DocumentWriter类的内部。
在DocumentWriter类的addPosition方法中的最后两行可以看到:
Term term = new Term(field, text, false);
postingTable.put(term, new Posting(term, position, payload, offset));
一个postingTable是一个HashMap,存储的是一个个的<键,值>对。它的键是Term对象,值是Posting对象。现在看看Term和Posting到底拥有哪些信息。
1、关于Term类,它的源代码如下所示:
package org.apache.lucene.index;
public final class Term implements Comparable, java.io.Serializable {
String field; // 一个Field的名称
String text; // 一个词条的文本内容
// 通过Field的名称和词条的文本内容构造一个词条
public Term(String fld, String txt) {
this(fld, txt, true);
}
/* 当打开一个DocumentWriter的时候,会存在一个字符串池(pool),该池中具有Term的field字段,即Field的名称。如果对另一个Field进行分词后,如果产生的词条Term的field(是一个String字符串)在当前的字符串池中已经存在,则返回该字符串field的引用;如果新产生的词条Term的field在当前的字符串池中不存在,说明是一个新的词条(即当前处理的这些词条Term中没有名称为field的词条,也就是,添加到Document中的Field是一个新的Field)则将该Term的field加入到字符串池中。
*/
Term(String fld, String txt, boolean intern) {
field = intern ? fld.intern() : fld; // field names are interned
text = txt; // unless already known to be
}
public final String field() { return field; }
public final String text() { return text; }
/**
* 优化已经构造出来的词条Term。返回一个新的词条(与被优化的那个词条具有相同的名称)。
*/
public Term createTerm(String text)
{
return new Term(field,text,false);
}
/ 如果两个词条Term具有相同的field和text,则返回true
public final boolean equals(Object o) {
if (o == this)
return true;
if (o == null)
return false;
if (!(o instanceof Term))
return false;
Term other = (Term)o;
return field == other.field && text.equals(other.text);
}
// 返回一个词条的field和the text的哈希码之和
public final int hashCode() {
return field.hashCode() + text.hashCode();
}
public int compareTo(Object other) {
return compareTo((Term)other);
}
// 定制客户化排序方式
public final int compareTo(Term other) {
if (field == other.field) // fields are interned
return text.compareTo(other.text);
else
return field.compareTo(other.field);
}
// 重新设置一个词条Term的名称field和文本信息text
final void set(String fld, String txt) {
field = fld;
text = txt;
}
public final String toString() { return field + ":" + text; }
// 根据一个打开的对象输入流,从字符串池中读取一个Term的名称field
private void readObject(java.io.ObjectInputStream in)
throws java.io.IOException, ClassNotFoundException
{
in.defaultReadObject();
field = field.intern();
}
}
Term是文本中的一个词(即word),它是检索的基本单位。
2、关于Posting类,它的源代码如下所示:
final class Posting { // 关于在Document中的一个词条Term的信息
Term term; // 一个Term对象
int freq; // 该指定词条Term在Document中的频率
int[] positions; // 该词条的位置数组,因为在一个Document中可能存在多个相同的该Term,所有它的位置不是唯一的
Payload[] payloads; // 该词条Term的payload信息
TermVectorOffsetInfo [] offsets; // 该词条向量的偏移量数组
Posting(Term t, int position, Payload payload, TermVectorOffsetInfo offset) {
term = t;
freq = 1;
positions = new int[1];
positions[0] = position;
if (payload != null) {
payloads = new Payload[1];
payloads[0] = payload;
} else
payloads = null;
if(offset != null){
offsets = new TermVectorOffsetInfo[1];
offsets[0] = offset;
} else
offsets = null;
}
}
3、关于Payload类
上面,Posting类中讲一个Payload[]数组作为它的成员,到底Payload如何定义?从它的源代码解读:
package org.apache.lucene.index;
import java.io.Serializable;
import org.apache.lucene.analysis.Token;
import org.apache.lucene.analysis.TokenStream;
/**
一个Payload是一个元数据(就是数据的数据) ,在Posting类中用到它,说明它是一个用于描述Posting对象的(构成和特征)。进行分词处理的时候,每切出一个词条Term,即这个词条存在了,则同时存储一个Payload信息。当把一个<Term,Posting>对放到postTable中的时候,就为该Posting增加了一个Payload元数据。
这个元数据最早被添加到哪个类的对象里了呢?从DocumentWriter类的invertDocument()方法中,可以看到对该类的addPosition()方法的调用,这调用addPosition()方法的时候,传了一个Payload,继续看这个Payload的来源,可以看到,是在一个TokenStream打开,进行分词的过程中,从一个Token中获取到的Payload,即 Payload payload = t.getPayload();,这里t是一个Token对象。
继续看addPostition()方法,这行代码ti.payloads[freq] = payload;,其中ti是Posting的实例,每个ti(即Posting)对应着一个Payload[]数组:ti.payloads = new Payload[ti.positions.length];,这个数组的大小为这个Posting的位置数组positions[]的长度。
上面就是Payload的来源,及其一些去向和处理。
之后,根据Payload构造了一个Posting对象,并且在invertDocument()方法中进行分词,根据切出来的词构造了一个词条Term,将Posting实例和Term实例作为一个<Term,Posting>对放到postTable中,如下所示:
Term term = new Term(field, text, false);
postingTable.put(term, new Posting(term, position, payload, offset));
*/
public class Payload implements Serializable {
// payload数据的byte数组
protected byte[] data;
// 在byte数组中的偏移量
protected int offset;
// payload的长度
protected int length;
// 创建一个空的payload
protected Payload() {
}
// 根据一个字节数组,即Payload的内容,创建一个Payload
public Payload(byte[] data) {
this(data, 0, data.length);
}
// 创建一个Payload,指定了它的内容data(一个字节数组) 、字节数组的偏移量、data的长度
public Payload(byte[] data, int offset, int length) {
if (offset < 0 || offset + length > data.length) {
throw new IllegalArgumentException();
}
this.data = data;
this.offset = offset;
this.length = length;
}
// 返回Payload内容的长度
public int length() {
return this.length;
}
// 返回指定索引位置的一个字节byte
public byte byteAt(int index) {
if (0 <= index && index < this.length) {
return this.data[this.offset + index];
}
throw new ArrayIndexOutOfBoundsException(index);
}
// 创建一个字节数组(分配了空间),并将Payload的信息拷贝到该字节数组中
public byte[] toByteArray() {
byte[] retArray = new byte[this.length];
System.arraycopy(this.data, this.offset, retArray, 0, this.length);
return retArray;
}
// 将Payload的数据拷贝到指定的一个字节数组中
public void copyTo(byte[] target, int targetOffset) {
if (this.length > target.length + targetOffset) {
throw new ArrayIndexOutOfBoundsException();
}
System.arraycopy(this.data, this.offset, target, targetOffset, this.length);
}
}
现在,用该对Posting、Payload有了进一步的了解了,一个TokenStream打开以后,进行分词处理,返回的是Token,然后从Token中提取有用的信息,来构造我们需要的词条Term,这时一个词条Term就诞生了。因为一个词条是静态的,并不能反映在实际应用中的动态变化轨迹,所以又使用Posting类对一个Term进行封装,为一个词条赋予了更加丰富的内容。
发表评论
-
创建索引的时候出现的错误
2010-01-04 10:13 1702<OFMsg>251658517"1&q ... -
SQLServer2005获取大数据集时内存不足的解决办法
2009-02-12 10:59 1991今天在修改search的建立索引的程序的时候,发现了这个错误 ... -
使用Lucene的Highlighter实现文件摘要的自动提取
2009-02-06 16:52 6648使用Lucene自带的Highlighter就可以实现对原始文 ... -
Lucene倒排索引原理
2009-02-06 16:08 1155Lucene是一个高性能的java全文检索工具包,它使用的是倒 ... -
Lucene 2.2.0发布自带的HTMLParser的使用
2009-02-06 16:00 2538Lucene 2.2.0发行包中自带 ... -
Lucene关键字高亮显示
2009-02-06 15:53 2375在Lucene的org.apache.lucene.s ... -
Lucene-2.2.0 源代码阅读学习(42)
2009-02-06 15:46 1332关于Hits类。这个Hits类 ... -
Lucene-2.2.0 源代码阅读学习(41)
2009-02-06 15:40 1110当执行Hits htis = search(query);这一 ... -
Lucene-2.2.0 源代码阅读学习(40)
2009-02-06 15:34 1195关于Lucene检索结果的排序问题。 已经知道,Lucene的 ... -
Lucene-2.2.0 源代码阅读学习(39)
2009-02-06 15:31 1118关于Lucene得分的计算。 在IndexSearcher类中 ... -
Lucene-2.2.0 源代码阅读学习(38)
2009-02-06 15:13 1130关于QueryParser。 QueryParser是用来解析 ... -
Lucene-2.2.0 源代码阅读学习(37)
2009-02-06 15:06 1065关于MultiTermQuery查询。 这里研究继承自Mult ... -
Lucene-2.2.0 源代码阅读学习(36)
2009-02-06 15:05 1018关于MultiTermQuery查询。 这里研究FuzzyQu ... -
Lucene-2.2.0 源代码阅读学习(35)
2009-02-06 15:03 941于MultiPhraseQuery(多短语查询)。 Multi ... -
Lucene-2.2.0 源代码阅读学习(34)
2009-02-06 15:02 972关于PhraseQuery。 PhraseQuery查询是将多 ... -
Lucene-2.2.0 源代码阅读学习(33)
2009-02-06 15:01 996关于范围查询RangeQuery。 ... -
Lucene-2.2.0 源代码阅读学习(32)
2009-02-06 15:00 889关于SpanQuery(跨度搜索),它是Query的子类,但是 ... -
Lucene-2.2.0 源代码阅读学习(31)
2009-02-06 14:58 947关于前缀查询PrefixQuery(前缀查询)。 准备工作就是 ... -
Lucene-2.2.0 源代码阅读学习(30)
2009-02-06 14:57 755关于Query的学习。 主要使用TermQuery和Boole ... -
Lucene-2.2.0 源代码阅读学习(29)
2009-02-06 14:54 1159关于IndexSearcher检索器。 ...
相关推荐
lucene-analyzers-2.2.0.jarlucene-analyzers-2.2.0.jarlucene-analyzers-2.2.0.jarlucene-analyzers-2.2.0.jarlucene-analyzers-2.2.0.jarlucene-analyzers-2.2.0.jarlucene-analyzers-2.2.0.jarlucene-analyzers-...
标题中的"lucene-2.2.0zip"指的是Lucene的2.2.0版本,这是一个较早的版本,对于学习和理解Lucene的基础概念非常有帮助。 Lucene 2.2.0的主要特性包括: 1. **全文检索**:Lucene支持对文档内容进行全文检索,允许...
lucene-highlighter-2.2.0.jarlucene-highlighter-2.2.0.jarlucene-highlighter-2.2.0.jarlucene-highlighter-2.2.0.jarlucene-highlighter-2.2.0.jarlucene-highlighter-2.2.0.jarlucene-highlighter-2.2.0.jar
《Lucene-2.3.1 源代码阅读学习》 Lucene是Apache软件基金会的一个开放源码项目,它是一个高性能、全文本搜索库,为开发者提供了在Java应用程序中实现全文检索功能的基础架构。本篇文章将深入探讨Lucene 2.3.1版本...
《深入解析Lucene高亮显示源码:剖析`lucene-highlighter-2.2.0-src.zip`》 Lucene,作为一个开源全文检索库,以其高效、灵活的特点在信息检索领域广泛应用。在处理搜索结果时,为了提升用户体验,通常会采用高亮...
《深入剖析Lucene 2.2.0源代码》 Lucene是一款强大的开源全文搜索引擎库,由Apache软件基金会开发并维护。它为Java开发者提供了一种高性能、可扩展的文本检索核心工具。本文将深入探讨Lucene 2.2.0版本的源代码,...
在前面Lucene-2.2.0 源代码阅读学习(1)中,根据Lucene提供的一个Demo,详细分析研究一下索引器org.apache.lucene.index.IndexWriter类,看看它是如果定义的,掌握它建立索引的机制。 通过IndexWriter类的实现源代码...
赠送源代码:lucene-analyzers-smartcn-7.7.0-sources.jar; 赠送Maven依赖信息文件:lucene-analyzers-smartcn-7.7.0.pom; 包含翻译后的API文档:lucene-analyzers-smartcn-7.7.0-javadoc-API文档-中文(简体)版....
赠送源代码:lucene-core-7.7.0-sources.jar; 赠送Maven依赖信息文件:lucene-core-7.7.0.pom; 包含翻译后的API文档:lucene-core-7.7.0-javadoc-API文档-中文(简体)版.zip; Maven坐标:org.apache.lucene:lucene...
- 通过阅读源代码,可以理解Lucene的内部工作原理,如如何构建索引、执行查询等。 - 分析器部分的源码有助于了解文本预处理过程,包括分词、去除停用词等。 - 探究查询解析器的实现,掌握如何将自然语言转化为...
赠送源代码:lucene-analyzers-common-6.6.0-sources.jar; 赠送Maven依赖信息文件:lucene-analyzers-common-6.6.0.pom; 包含翻译后的API文档:lucene-analyzers-common-6.6.0-javadoc-API文档-中文(简体)版.zip;...
这是一个java开发用的.jar文件,用它和Lucene-core-2.0.0.jar可以实现搜索引擎
赠送源代码:lucene-core-7.2.1-sources.jar; 赠送Maven依赖信息文件:lucene-core-7.2.1.pom; 包含翻译后的API文档:lucene-core-7.2.1-javadoc-API文档-中文(简体)版.zip; Maven坐标:org.apache.lucene:lucene...
赠送源代码:lucene-suggest-6.6.0-sources.jar; 赠送Maven依赖信息文件:lucene-suggest-6.6.0.pom; 包含翻译后的API文档:lucene-suggest-6.6.0-javadoc-API文档-中文(简体)版.zip; Maven坐标:org.apache....
赠送源代码:lucene-backward-codecs-7.3.1-sources.jar; 赠送Maven依赖信息文件:lucene-backward-codecs-7.3.1.pom; 包含翻译后的API文档:lucene-backward-codecs-7.3.1-javadoc-API文档-中文(简体)-英语-对照...
赠送源代码:lucene-core-6.6.0-sources.jar; 赠送Maven依赖信息文件:lucene-core-6.6.0.pom; 包含翻译后的API文档:lucene-core-6.6.0-javadoc-API文档-中文(简体)版.zip; Maven坐标:org.apache.lucene:lucene...
赠送源代码:lucene-spatial-extras-7.3.1-sources.jar; 赠送Maven依赖信息文件:lucene-spatial-extras-7.3.1.pom; 包含翻译后的API文档:lucene-spatial-extras-7.3.1-javadoc-API文档-中文(简体)-英语-对照版....
赠送源代码:lucene-memory-6.6.0-sources.jar; 赠送Maven依赖信息文件:lucene-memory-6.6.0.pom; 包含翻译后的API文档:lucene-memory-6.6.0-javadoc-API文档-中文(简体)版.zip; Maven坐标:org.apache.lucene:...
赠送源代码:lucene-suggest-7.7.0-sources.jar; 赠送Maven依赖信息文件:lucene-suggest-7.7.0.pom; 包含翻译后的API文档:lucene-suggest-7.7.0-javadoc-API文档-中文(简体)版.zip; Maven坐标:org.apache....
赠送源代码:lucene-analyzers-smartcn-7.7.0-sources.jar; 赠送Maven依赖信息文件:lucene-analyzers-smartcn-7.7.0.pom; 包含翻译后的API文档:lucene-analyzers-smartcn-7.7.0-javadoc-API文档-中文(简体)-英语...