搜索引擎lucene实现--二半吊子的论调之Document和Field
本来打算先写写analysis包,因为那个组件包是基础。但写着写着就觉得没有入口的说明,就跳到那一部分实在对不起自己和各位的理解。于是咱就先看看Document和Field,这两个用于索引和查询的数据结构。
我们大多数人用过数据库,知道一个表里面的一行。如粗糙的下图所示:
这个是关系型数据库的典型存储方式。我们在进行数据查询的时候,也是提供字段值或者是模式等条件。那么lucene作为一个全文检索的解决方案,从表面来看,也是类似于关系型数据库方式。它为我们提供了
统一的数据索引接口(IndexWriter)和查询接口(IndexSearcher)。在我们这次要说明的Document和Field,我们也可以对应理解成关系型数据库的一个数据行和字段。于是再对应一下,可以将IndexWriter理解
成SQL里面的insert语句,而IndexSearcher对应成select语句。表面上就是这样的,最多是使用方式不同。
有了这么一层考虑之后,再来理解Document和field,我想(嗯,是我想象的)应该比较容易了。
在《体系结构》的例子中,有如下代码:
doc.add(new Field("fieldname", text, TextField.TYPE_STORED));
这表示啥意思,从上面的对比说明来看,就是在构造一行,往行里面添加列值(Field)。
Ok,我们有了点感觉之后再来看看org.apache.lucene.document包。
这里都是从lucene4.0里面拉出来的,跟3.x里面的一些用法有比较大的区别。(各位用3.x的同学,要注意api上的变化)
如果我们就是要建立针对学生成绩表里面姓名和成绩的全文检索(这里应该考虑一下,到底什么是全文检索?这里的全文是什么意思?所谓的“全”,到底是哪个范围里面的“全”?这些都有助于各位同学建立有针对性的搜索系统,而不仅仅是把数据库里面的查询搬到lucene里面),那应该如何构造用于建立索引的Document数据?
构建一个Document:(So Easy!)
Document doc = new Document();
接下来,往Document中存放各个字段:
doc.add(new Field("name", text, TextField.TYPE_STORED));//这种方式还是从3.x一直用过来的
3.x里面的那个持久的Field构造函数:
Field(String name, String value, Field.Store store, Field.Index index, Field.TermVector termVector)
已经被弃用了。我第一次使用这个东西也被迷惑一小下。怎么要有这么多变量来说明这个字段的属性。尤其是,加入一个属性,我肯定是想要建立索引的(大部分情况下,针对文本内容来说)。
现在4.0里面推荐如下方式:
doc.add(new TextField("name", text, Store.YES));
由原来的Field分割成如下些个子类:
DoubleField, FloatField, IntField, LongField, StoredField, StringField, TextField ……
请特别注意上面的省略号,里面还有很多,跟特定类型对应Field子类。鉴于篇幅和勤快程度的考虑,我就不多列了。
有了以上几个类,我们在add field时,应该更加明确了,就不会像以前只是笼统的区分Text和Number。
而且在新api里面,Field.Index枚举类型被弃用了,其替代品是FieldType,这个类里有关于本字段数据处理的各种元数据,字段如下:
private boolean indexed;
private boolean stored;
private boolean tokenized = true;
private boolean storeTermVectors;
private boolean storeTermVectorOffsets;
private boolean storeTermVectorPositions;
private boolean storeTermVectorPayloads;
private boolean omitNorms;
…………
从这个重构方式里,我们也能看到lucene越来越成熟了,元数据划分归类也更加细腻,而不是像以前那样,让新用户来决定各种显而易见的属性。(请把这句话当成废话,但是请考虑一下其重构原则)
言归正传,加完姓名的field之后,我们接着添加成绩字段:
doc.add(new FloatField(“score”, 59.9F, Field.Store.YES);
通过这个构造函数(FloatField(String name, float value, Field.Store stored)),我们可以创建一个名叫score,值为59.9,且要保存到索引文件的FloatField。这里面也要特别说明一下。
请看这个构造函数的源码:
public FloatField(String name, float value, Store stored) {
super(name, stored == Store.YES ? TYPE_STORED : TYPE_NOT_STORED);
fieldsData = Float.valueOf(value);
}
FloatField将Field.Store转换成了TYPE_STORED : TYPE_NOT_STORED(显而易见),这两个对象都是FieldType实例。再来看看源码(以TYPE_STORED为例):
static {
TYPE_STORED.setIndexed(true);
TYPE_STORED.setTokenized(true);
TYPE_STORED.setOmitNorms(true);
TYPE_STORED.setIndexOptions(IndexOptions.DOCS_ONLY);
TYPE_STORED.setNumericType(FieldType.NumericType.FLOAT);
TYPE_STORED.setStored(true);
TYPE_STORED.freeze();
}
这里要特别注意下,TYPE_STORED.setNumericType(FieldType.NumericType.FLOAT);这句说明我们可以在搜索时,针对score这个字段排序。
于是,我们也就要考虑一下,我们的字段类型的关联问题,各位同样可以跟数据库表关联起来理解。
文本类型(TextField):是一大块需要经过分词的文本;
字符串类型(StringField):是一个不需要分词,而直接用于索引的字符串;
数字类型(LongField
, FloatField
, DoubleField
,IntField):首先是一个不变的属性值,这类字段还有一个主要用途,就是可以用于对搜索的返回结果集排序或是按范围查询。
DocValues类型(类名中含有DocValues字样的Field子类):这些个类可以理解成一个专门服务于Document,且可以用于排序,范围查询等方面的字段。
有了上面的说明,各位在使用时,尤其是在搜索时,对于字段是否可以排序,是否可按范围查询等问题,也就有答案了。
到这里,对document包的说明也应该是差不多了。但是数据库里面还有date类型的字段,可惜lucene里面没有对date的直接支持。但是各位都知道date可以变成long型的字段。这样各位就可以针对时间进行存储,排序,或是按范围查询了。
ok,这次就到这里,要接下去完成还是二二半吊子的analysis部分了。
posted on 2012-11-25 15:59 eric_chen 阅读(5024) 评论(2) 编辑 收藏 举报