這是很久以前做lucene 時的總結,現在已經不用去研究那些代碼,但還是分享出來給大家以幫助。謝謝
1. 從Index開始
無論哪種搜索引擎,都會需要自建一個index,所有的搜詞準確率及快速性很大程度上取決于這類的問題。因此在建索引文件的時候,我們首先要弄清楚lucene建索引的接口及各類參數。
Field是lucene的重要組成部分,其引出好些接口
Filed Interf |
Name,string |
store |
index |
token |
StoreTermVector |
Keyword |
Y |
Y |
Y |
N |
(N) |
UnIndexed |
Y |
Y |
N |
N |
N |
Text |
Y |
* |
(Y) |
(Y) |
N |
UnStored |
Y |
(N) |
(Y) |
(Y) |
* |
Text |
Y |
(Y) |
(Y) |
(Y) |
Y |
*()表示多態隱藏參數,當storeTermVector為true, Index必須保證true.
對于Keyword來說String 可以是Date型的,具體會通過DateField里dateToString來實現其功能。
下面是Field構造所帶的6個參數。頭兩個是必須。
* @param name The name of the field
* @param string The string to process
* @param store true if the field should store the string
* @param index true if the field should be indexed
* @param token true if the field should be tokenized
* @param storeTermVector true if we should store the Term Vector info
這里就是Field所存儲幾個選項,這些在建索引的時候會有一定的作用
Document: Documents是Index和search的單元,也就是Field的集合和組成index的內容,提供加入add(), 去除remove(), 及getField()getFields()的方法。
Document 包括1個參數boost 默認為1.0f,其作用 (參考cap: java搜索引擎lucene學習筆記) luncene對Document和Field提供了一個可以設置的Boost參數, 這個參數的用處是告訴lucene, 某些記錄更重要,在搜索的時候優先考慮他們。 lucene默認的boost參數是1.0, 如果你覺得這個field重要,你可以把boost設置為1.5, 1.2....等。這個boost不會直接和Document,Field存放在Index一起,因此在帶Index的時候IndexReader.Documenthit()和Hits.hit() 返回中會不同。
IndexWrite (參考lucene Doc) 主要用于創建和維護index. 構造函數中第三個參數用來控制index文件是不是重建(T)還是添加(F)。 其他方面 addDocument()用于添加document,當Index結束添加時close()必須寫上。 如果document不再隨意添加,那么可以在index關閉時調用optimize()來對Index進行優化。下面來討論一下IndexWrite里的基本方法
我們先可以用個簡單的程序來建個空Index
IndexWriter writer = new IndexWriter(String|Directory|File, Anaylzer, true|false);
writer.close();
會形成一個索引,這里千萬注意,建索引的指定目錄下的所有文件都會被不可恢復刪除,做索引的文件和索引不要指定在一個目錄下。
addDocument() 用來把把document添加到Index里,而addIndexes三()可以把幾個子索引集合起來,做成一個總索引。addIndexes()會自動調用optimize()所有使用時不需要再加, 但close()需要手動加上
Directory一個抽象的標示index文件地址的類,有兩個實現:FSDirectory和RAMDirectory, 前者是基于文件系統,后者使用內存。 在Lucene Doc里Directory 還提供一種通過jdbc基于數據庫內的索引,目前沒有提供實現方法,估計后續版本的會出。RAM的方式通過stream方式讀到buffer里,速度非常快,但根據我做程序時的經驗,在六千條紀錄的以上,時常出現java的memory low現象.所以大型的索引,采取FS是必須的。FSDirectory的構造函數是私有的,只有通過FSDirectory.GetDirectoty(File|String, boolean)來獲得,而RAMDirectory卻可以直接構造或者從FSDirectory來得到。
2. Search 這里似乎是lucene的精華,
Searchable是Search的底層接口, lucene提供了單索引,多索引以及遠程的索引3 種方式。
Query是Search的基本單元,實現各種搜索的方法,下面我簡單的列一下他們的作用,
TermQuery,
最基本的Query,用TermQuery(new Term(Str filed, Str Text));就可以構造, TermQuery把查詢條件視為一個key, 要求和查詢內容完全匹配,比如Field.Keyword類型就可以使用TermQuery。
PhraseQuery
表示可用于非嚴格語句的查詢,匹配包含的指定連續Term, 比如"one five"可以匹配"one two three four five"等, PhraseQuery提供了一個重要的setSlop()參數, 這個參數主要用于設置phrase query中詞之間的允許間隔數目,在默認情況下slop的值是0, 就相當于TermQuery的精確匹配, 通過設置slop參數(比如"one five"匹配"one two three four five"就需要slop=3,如果slop=2就無法得到結果。這里我們可以認為slope是單詞移動得次數,可以左移或者右移。這里特別提醒,PhraseQuery不保證前后單詞的次序,在上面的例子中,"two one"就需要2個slop,也就是認為one 向左邊移動2位, 就是能夠匹配的”one two”如果是“five three one” 就需要slope=6才能匹配。還有一點要注意,就是如果碰到stopword,(會在stopAnalyzer中細說),則stopword不用把slop算在內。
BooleanQuery
是一個組合的Query, 可以把各種Query添加進去(主要是TernQuery和PhraseQuery)并標明他們的邏輯關系,添加條件用public void add(Query query, boolean required, boolean prohibited)方法, 后兩個boolean變量分別表示不匹配子Query將不匹配booleanQuery和匹配子Query將不匹配booleanQuery。估計類似google的(+,-)功能。這兩個參數不允許同時為true, 否則報錯。但兩個參數可以都為false,而且必須保證匹配至少一個子query才能用來匹配booleanQuery。 一個BooleanQuery中可以添加多個Query, 但不能超過setMaxClauseCount(int)的值(默認1024個),否則拋出TooManyClauses錯誤. BooleanQuery可以完成各種邏輯的組合,如and, or 和not的組合。
RangeQuery
RangeQuery表示一個范圍的搜索條件,RangeQuery query = new RangeQuery(begin, end, included);最后一個boolean值表示是否包含邊界條件本身, begin和end必須滿足至少有一個不為null及兩者都在同一個field. 這里的Range是以String的compareTo (Str)進行比較。所以熟悉j2se的應該很容易確定Range的范圍。
PrefixQuery
表示匹配是以指定字符串開頭的匹配查詢, 可以用于Keyword形式的查詢。一般的在suggestion里對于single word可以使用的,也可用于查詢網絡結構目錄樹的數目。
PhrasePrefixQuery
由于PhraseQuery不能很靈活的適應各種的phrase的匹配。比如要搜索”Sony Cam*”, 先可以把add(Term)把Sony放在Term.,然后把使用IndexReader.Terms(Term)匹配以Cam為前綴的詞,最后使用PhrasePrefixQuery.add(Term[] terms)把兩者加在Query中。這里slope和phraseQuery雷同,仍然起著對phrase的定位作用,addTerm(Term[] terms)內使用ArrayList來保存Term數據,而Phrase使用的是Vector.
WildcardQuery
WildcardQuery和FuzzyQuery是繼承MultiTermQuery的,這是他們區別的其他的幾種常規Query. Query包含自身全部的匹配,Term由FilteredTermEnum提供,而MultiQuery則是不完全自身的匹配。Term的提供者也不同。
WildcardQuery主要使用?和*來表示一個或多個字母的匹配,值得注意的是,在wildcard中,empty對于?和*也是匹配的,且Query的開頭不允許用使用?和*.
注:當使用WildcardQuery時,搜索性能會有很大的下降
FuzzyQuery
能模糊匹配英文單詞,這個功能非常有用,大小寫敏感??梢允褂闷錁嬙旆椒?/span>FuzzyQuery(Term term, float minimumSimilarity, int prefixLength)
方法,還提供2種默認的0.5相似度,和0的前綴狀態。相似度比較時要減去非比較的前綴。然后再比。
例如,用”soni”匹配”sony”設置相似度前綴為0,則相似度為75%,如果前綴為1,則相似度為66.7%.只要高于最小相似度,便能找到。如果詞長度不一致,則以Query減去前綴的為準,例如設前綴為1,用”della”來匹配”dell”,相似度75%, 如果”del”來匹配”dell”則相似度只有50%
3.Analyse
下面是幾種analyse的分詞舉例。
"The quick brown fox jumped over the lazy dogs"
WhitespaceAnalyzer:
[The] [quick] [brown] [fox] [jumped] [over] [the] [lazy] [dogs]
SimpleAnalyzer:
[the] [quick] [brown] [fox] [jumped] [over] [the] [lazy] [dogs]
StopAnalyzer:
[quick] [brown] [fox] [jumped] [over] [lazy] [dogs]
StandardAnalyzer:
[quick] [brown] [fox] [jumped] [over] [lazy] [dogs]
"XY&Z Corporation - xyz@example.com"
WhitespaceAnalyzer:
[XY&Z] [Corporation] [-] [xyz@example.com]
SimpleAnalyzer:
[xy] [z] [corporation] [xyz] [example] [com]
StopAnalyzer:
[xy] [z] [corporation] [xyz] [example] [com]
StandardAnalyzer:
[xy&z] [corporation] [xyz@example.com]
Token
Token其實就是一個Field里面包含的term,一般的token就是獨立的word.
TokenStream對token做依次的列舉
它有2個子類,Tokenizer和TokenFilter,他們的子類如圖所示
幾種analyzer的區別
WhitespaceAnalyzer 由空格進行分詞
SimpleAnalyzer 由非字母的處分詞,并置小寫
StopAnalyzer 由非字母處分詞,置小寫,去除Stopword
StandardAnalyzer 由特定的語法進行分詞,包括e-mail,addresses, acronyms, Chinese-Japanese-Korean characters, alphanumerics, and more; 置小寫,去除Stopword。
StopWord 可以根據內部的代碼可以得到 有以下的一些
"a",
"an", "and", "are", "as", "at", "be", "but", "by", "for", "if", "in",
"into", "is", "it", "no", "not", "of", "on", "or", "s", "such", "t",
"that", "the", "their", "then", "there", "these", "they", "this", "to",
"was", "will", "with"
當然,你也可以通過你自己的接口去來改變上述默認的值。