搜索引擎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部分了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM