关于luncene 内层的研究
(飞刀和雨)
这是很久以前做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
能模糊匹配英文单词,这个功能非常有用,大小写敏感。可以使用其构造方法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"
当然,你也可以通过你自己的接口去来改变上述默认的值。