Lucene学习笔记

一、      Lucene简介

    Lucene 是一个基于 Java 的全文信息检索工具包,它不是一个完整的搜索应用程序,而是为你的应用程序提供索引和搜索功能的一个开源框架。Lucene 目前是 Apache Jakarta 家族中的一个开源项目。也是目前最为流行的基于 Java 开源全文检索工具包。

目前已经有很多应用程序的搜索功能是基于 Lucene 的,比如 Eclipse 的帮助系统的搜索功能。Lucene 能够为文本类型的数据建立索引,所以你只要能把你要索引的数据格式转化的文本的,Lucene 就能对你的文档进行索引和搜索。比如你要对一些 HTML 文档,PDF 文档进行索引的话你就首先需要把 HTML 文档和 PDF 文档转化成文本格式的,然后将转化后的内容交给 Lucene 进行索引,然后把创建好的索引文件保存到磁盘或者内存中,最后根据用户输入的查询条件在索引文件上进行查询。不指定要索引的文档的格式也使 Lucene 能够几乎适用于所有的搜索应用程序。

 

1.       搜索应用程序和 Lucene 之间的关系


 

全文检索

全文检索(Full-Text Retrieval)是指以文本作为检索对象,找出含有指定词汇的文本。全面、准确和快速是衡量全文检索系统的关键指标。

关于全文检索,我们要知道:

1.只处理文本。

2.不处理语义。

3.搜索时英文不区分大小写。

4. 结果列表有相关度排序。

在信息检索工具中,全文检索是最具通用性和实用性的。

 

全文检索不同于数据库搜索

全文检索不同于数据库的SQL查询。(他们所解决的问题不一样,解决的方案也不一样,所以不应进行对比)。在数据库中的搜索就是使用SQL,如:SELECT * FROM t WHERE content like ‘%ant%’。这样会有如下问题:

1.         匹配效果:如搜索ant会搜索出planting。这样就会搜出很多无关的信息。

2.         相关度排序:查出的结果没有相关度排序,不知道我想要的结果在哪一页。我们在使用百度搜索时,一般不需要翻页,为什么?因为百度做了相关度排序:为每一条结果打一个分数,这条结果越符合搜索条件,得分就越高,叫做相关度得分,结果列表会按照这个分数由高到低排列,所以第1页的结果就是我们最想要的结果。

3.          全文检索的速度大大快于SQLlike搜索的速度。这是因为查询方式不同造成的,以查字典举例:数据库的like就是一页一页的翻,一行一行的找,而全文检索是先查目录,得到结果所在的页码,再直接翻到这一页。

 

二、      关于索引

索引库结构——倒排序索引

我们需要对文档进行预处理,建立一种便于检索的数据结构,以此来提高信息检索的速度,这种数据结构就是索引。目前广泛使用的一种索引方式是倒排序索引 

倒排序索引的原理就如同查字典。要先查目录,得到数据对应的页码,在直接翻到指定的页码。不是在文章中找词,而是从目录中找词所在的文章。这需要在索引库中生成一个词汇表(目录),在词汇表中的每一个条记录都是类似于à所在文档的编号列表的结构,记录了每一个出现过的单词,和单词出现的地方(哪些文档)。查询时先查词汇表,得到文档的编号,再直接取出相应的文档。

把数据转成指定格式放到索引库中的操作叫做建立索引。建立索引时,在把数据存到索引库后,再更新词汇表。进行搜索时,先从检索词汇表开始,然后找到相对应的文档。如果查询中仅包含一个关键词,则在词汇表中找到该单词,并取出他对应的文档就可以了。如果查询中包含多个关键词,则需要将各个单词检索出的记录进行合并再取出相应的文档记录。

如果词汇表中有一个词传智播客对应的文档编号列表为“1”。现在又有添加了一个包含传智播客的文档,则词汇表中的传智播客词后对应的编号列表变成了“1,2”。因为关键词的数量受实际语言的限制,所以不用担心词汇表会变的很大。

索引文件的维护

维护倒排索引有三个操作:添加、删除和更新文档。但是更新操作需要较高的代价。因为文档修改后(即使是很小的修改),就可能会造成文档中的很多的关键词的位置都发生了变化,这就需要频繁的读取和修改记录,这种代价是相当高的。因此,一般不进行真正的更新操作,而是使用先删除,再创建的方式代替更新操作。

 

建立索引的执行过程(StoreIndex

在建立索引时,先要把文档存到索引库中,还要更新词汇表。

我们做的操作:

1.         把数据对象转成相应的Document,其中的属性转为Field

2.         调用工具IndexWriteraddDocument(doc),把Document添加到索引库中。

Lucene做的操作:

1.         Lucene做的操作:把文档存到索引库中,并自动指定一个内部编号,用来唯一标识这条数据。内部编号类似于这条数据的地址,在索引库内部的数据进行调整后,这个编号就可能会改变,同时词汇表中引用的编号也会做相应改变,以保证正确。但我们如果在外面引用了这个编号,前后两次去取,得到的可能不是同一个文档!所以内部编号最好只在内部用。

2.         Lucene做的操作:更新词汇表。把文本中的词找出并放到词汇表中,建立与文档的对应关系。要把哪些词放到词汇表中呢,也就是文本中包含哪些词呢?这就用到了一个叫做Analyzer(分词器)的工具。他的作用是把一段文本中的词按规则取出所包含的所有词。对应的是Analyzer类,这是一个抽象类,切分词的具体规则是由子类实现的,所以对于不同的语言(规则),要用不同的分词器。

在把对象的属性转为Field时,相关代码为:doc.add(newField("title", article.getTitle(), Store.YES, Index.ANALYZED))。第三与第四个参数的意思为:

枚举类型

枚举常量

说明

Store

NO

不存储属性的值

YES

存储属性的值

Index

NO

不建立索引

ANALYZED

分词后建立索引

NOT_ANALYZED

不分词,把整个内容作为一个词建立索引

说明:Store是影响搜索出的结果中是否有指定属性的原始内容。Index是影响是否可以从这个属性中查询(No),或是查询时可以查其中的某些词(ANALYZED),还是要把整个内容作为一个词进行查询(NOT_ANALYZED)。

 

合并索引库Directory.addIndexes()

索引的优化IndexWriter.optimize()3.5版本以前)

                            IndexWriter.forceMerge(intMaxNum)(3.5版本以后)

 

索引设置的一些建议:

1.         尽量减少不必要的存储

2.         不需要检索的内容不要建立索引

3.         非文本格式需要提前转化

4.         需要整体存放的内容不要分词

5.         NumericUtilsDateTools.如果属性的类型不是字符串,则要先进转换 :如果是数字类型,使用NumericUtils。如果是日期类型,则使用DataTools

三、      关于分词

分词器结构

在创建索引时会用到分词器,在使用字符串搜索时也会用到分词器,这两个地方要使用同一个分词器,否则可能会搜索不出结果。

分词器的一般工作流程:

1.         切分关键词

2.         去除停用词

3.         对于英文单词,把所有字母转为小写(搜索时不区分大小写)

说明:有的分词器还对英文进行形态还原,就是去除单词词尾的形态变化,将其还原为词的原形。这样做可以搜索出更多有意义的结果。如搜索sutdent时,也可以搜索出students,这是很有用的。

停用词

有些词在文本中出现的频率非常高,但是对文本所携带的信息基本不产生影响,例如英文的“aantheof”,或中文的的、了、着,以及各种标点符号等,这样的词称为停用词(stop word)。文本经过分词之后,停用词通常被过滤掉,不会被进行索引。在检索的时候,用户的查询中如果含有停用词,检索系统也会将其过滤掉(因为用户输入的查询字符串也要进行分词处理)。排除停用词可以加快建立索引的速度,减小索引库文件的大小。

中文分词器

中文的分词比较复杂,因为不是一个字就是一个词,而且一个词在另外一个地方就可能不是一个词,如在帽子和服装中,和服就不是一个词。对于中文分词,通常有三种方式:单字分词、二分法分词、词典分词。

单字分词:就是按照中文一个字一个字地进行分词。如:我们是中国人,效果:。(StandardAnalyzer就是这样)。

二分法分词:按两个字进行切分。如:我们是中国人,效果:我们们是是中中国国人。(CJKAnalyzer就是这样)。

词库分词:按某种算法构造词,然后去匹配已建好的词库集合,如果匹配到就切分出来成为词语。通常词库分词被认为是最理想的中文分词算法。如:我们是中国人,效果为:我们中国人。(使用极易分词的MMAnalyzer。可以使用极易分词,或者是庖丁分词分词器、 IKAnalyzer)。

其他的中文分词器有:

1.         极易分词:MMAnalyzer,最后版本是1.5.3,更新时间是2007-12-05,不支持Lucene3.0

2.         庖丁分词:PaodingAnalzyer,最后版本是2.0.4,更新时间是2008-06-03,不支持Lucene3.0

 中文分词器使用IKAnalyzer,主页:http://www.oschina.net/p/ikanalyzer

实现了以词典为基础的正反向全切分,以及正反向最大匹配切分两种方法。IKAnalyzer是第三方实现的分词器,继承自LuceneAnalyzer类,针对中文文本进行处理。具体的使用方式参见其文档。

注意:扩展的词库与停止词文件要是UTF-8的编码,并且在要文件头部加一空行。

四、      关于搜索

从索引库中搜索的执行过程(QueryParserTopDocsScoreDoc

在进行搜索时,先在词汇表中查找,得到符合条件的文档编号列表。再根据文档编号真正的去取出数据(Document)。

 

1.         把要查询字符串转为Query对象。这就像在Hibernate中使用HQL查询时,也要先调用Session.createQuery(hql)转成 HibernateQuery对象一样。把查询字符串转换成Query是使用QueryParser或使用 MultiFieldQueryParser。查询字符串也要先经过Analyzer(分词器)。要求搜索时使用的Analyzer要与建立索引时使用的 Analzyer要一致,否则可能搜不出正确的结果。

2.         调用IndexSearcher.search(),进行查询,得到结果。此方法返回值为TopDocs,是包含结果的多个信息的一个对象。其中有 totalHits 代表决记录数,ScoreDoc的数组。ScoreDoc是代表一个结果的相关度得分与文档编号等信息的对象。

3.         取出要用到的数据列表。调用IndexSearcher.doc(scoreDoc.doc)以取出指定编号对应的Document数据。在分页时要用到:一次只取一页的数据。

 

通过QueryParser解析用户的查询字符串进行搜索(QueryParserMultiFieldQueryParser

1QueryParser处理用户输入的查询条件。把用户输入的非格式化检索词转化成后台检索可以理解的Query对象。使用的构造方法为:QueryParser(Version matchVersion,String f, Analyzer a)

2.MultiFieldQueryParserQueryParser的子类。与父类相比,MultiFieldQueryParser可以在多个属性中搜索。使用的构造方法为:MultiFieldQueryParser(VersionmatchVersion, String[] fields, Analyzer analyzer)

 

通过构建各种Query对象进行查询(Query的子类)

Query:抽象类,必须通过一系列子类来表述检索的具体需求。

 TermQuery

关键词查询

NumericRangeQuery

范围查询。使用静态方法构造实例:

newIntRange(finalString field,Integer min, Integer max, final boolean minInclusive,final boolean maxInclusive)

newLongRange(finalString field,Long min, Long max, final boolean minInclusive,final boolean maxInclusive)

newFloatRange(finalString field,Float min, Float max, final boolean minInclusive,final boolean maxInclusive)

newDoubleRange(finalString field,Double min, Double max, final boolean minInclusive,final boolean maxInclusive)

WildcardQuery

通配符查询

PhraseQuery

短语查询

public void add(Term term, int position)

public void setSlop(int s)

 

add( new Term(“name”, “lucene”, 1);

add(newTerm(“name”, “教程”, 3);

代表搜索的是“Lucene ?教程,?表示中间隔一个词。

setSlop(2);

代表这两个词中间可以最多隔2个词

BooleanQuery

public void add(Query query, Occur occur)

Occur 用于表示布尔查询子句关系的类,包括:

Occur.MUSTOccur.MUST_NOTOccur.SHOULD

1.         MUSTMUST:取得连个查询子句的交集。

2.         MUSTMUST_NOT:包含MUST并且查询结果中不包含MUST_NOT的检索结果。

3.         SHOULDSHOULD,表示关系,最终检索结果为所有检索子句的并集。

4.          

使用时注意:

1.         单独使用MUST_NOT:无意义,检索无结果。(也不报错)

2.         MUST_NOTMUST_NOT:无意义,检索无结果。(也不报错)

3.         单独使用SHOULD:结果相当于MUST

4.         SHOULDMUST_NOT:此时SHOULD相当于MUST,结果同MUSTMUST_NOT

5.         MUSTSHOULD:此时SHOULD无意义,结果为MUST子句的检索结果。

 

五、      关于排序

通过改变文档Boost值来改变排序结果。Boost是指索引建立过程中,给整篇文档或者文档的某一特定属性设定的权值因子,在检索时,优先返回分数高的。通过Document对象的setBoost()方法和Field对象的setBoost()方法,可以分别为DocumentField指定 Boost参数。不同在于前者对文档中每一个域都修改了参数,而后者只针对指定域进行修改。默认情值为1F,一般不做修改。

 

通过使用Sort对象定制排序。Sort支持的排序功能以文档当中的域为单位,通过这种方法,可以实现一个或者多个不同域的多形式的值排序。时间类型的属性采用STRING常量。

 

按相关度排序

1,相关度得分是在查询时根据查询条件实进计算出来的

2,如果索引库索引不变,查询条件不变,查出的文档得分也不变

 

按指定的字段排序

If you want to be able to sortresults by a Field value, you must add it as a Field that is indexed but notanalyzed, using Field.Index.NOT_ANALYZED.

其它

六、      其它

过滤(Filter

使用Filter可以对搜索结果进行过滤以获得更小范围的结果。使用Filter对性能的影响很大(有可能会使查询慢上百倍),使用NumericRangeFilter。也可使用相应的查询实现一样的效果。

高亮(Highlight

需要的jar包为:

contrib\highlighter\lucene-highlighter-3.0.1.jar

contrib\memory\lucene-memory-3.0.1.jar

 

// 生成高亮器

Formatter formatter = newSimpleHTMLFormatter("<span class='kw'>", "</span>");

Scorer scorer = new QueryScorer(query);

Highlighter highlighter = newHighlighter(formatter, scorer);

highlighter.setTextFragmenter(newSimpleFragmenter(20));

 

// 使用高亮器:对content属性值进行摘要并高亮

String text = highlighter.getBestFragment(LuceneUtils.getAnalyzer(),"content", doc.get("content"));

// 如果进行高亮的属性值中没有要搜索的关键字,则返回null

if (text != null) {

   doc.getField("content").setValue(text);

}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值