搜索引擎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分割成如下些个子类:

 DoubleFieldFloatFieldIntFieldLongFieldStoredFieldStringField, 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, DoubleFieldIntField):首先是一个不变的属性值,这类字段还有一个主要用途,就是可以用于对搜索的返回结果集排序或是按范围查询。

DocValues类型(类名中含有DocValues字样的Field子类):这些个类可以理解成一个专门服务于Document,且可以用于排序,范围查询等方面的字段。

有了上面的说明,各位在使用时,尤其是在搜索时,对于字段是否可以排序,是否可按范围查询等问题,也就有答案了。

到这里,对document包的说明也应该是差不多了。但是数据库里面还有date类型的字段,可惜lucene里面没有对date的直接支持。但是各位都知道date可以变成long型的字段。这样各位就可以针对时间进行存储,排序,或是按范围查询了。

ok,这次就到这里,要接下去完成还是二二半吊子的analysis部分了。

posted on 2012-11-25 15:59  eric_chen  阅读(5004)  评论(2编辑  收藏  举报