Lucene是什么?
lucene是一个用于全文检索和搜索的开源Java代码库,由Apache基金会提供支持。
lucene项目官网地址是: lucene.apache.org, lucene项目下的软件有:
- Lucene Core, our flagship sub-project, provides Java-based indexing and search technology, as well as spellchecking, hit highlighting and advanced analysis/tokenization capabilities.
- SolrTM is a high performance search server built using Lucene Core, with XML/HTTP and JSON/Python/Ruby APIs, hit highlighting, faceted search, caching, replication, and a web admin interface.
- PyLucene is a Python port of the Core project.
Lucene vs. Solr: Lucene是一个程序库,Solr是一个完整的程序。

全文索引(Full-text Retrieval)是什么?
Lucene软件包分析
Lucene软件包的发布形式是一个Jar包。可以直接从官网下载,也可以利用Maven添加依赖。
Jar包文件主要分成以下五类:
package: org.apache.lucene.document
这个包提供了一些为封装要索引的文档所需要的类,比如
Document,Field。这样,每个文档被封装成一个Document对象。package: org.apache.lucene.analysis
这个包的主要功能是对文档进行分词,因为文档在建立索引之前必须要进行分词,所以这个包可以看成是为建立索引做准备工作。
package: org.apache.lucene.index
这个包提供了一些类来协助创建索引以及对创建好的索引进行更新。这里有两个基础类:
IndexWriter和IndexReader,其中IndexWriter是用于创建索引并添加文档到索引的,IndexReader是用来删除索引中的文档的????package: org.apache.lucene.search
这个包提供了对建立好的索引上进行搜索所需要的类。比如
IndexSearcher和Hits,IndexSearcher定义了在指定的索引上进行搜索的方法,Hits用来保存搜索得到的结果。
注:参考的文章比较早(年2006),而现在的Lucene在不断的更新下部分代码已经不同了,比如IndexSearcher的实现已经不一样了。具体还要查找Lucene 对应版本的文档说明。
Lucene的入门使用
思路:假设我们的电脑目录中有许多的文本文档,我们需要查找哪些文档含有某个关键词。为了实现这个功能,我们首先利用Lucene对这个目录中的文档建立索引,然后在建立好的索引中搜索我们所要查找的文档。
1. 建立索引
为了对文档进行索引,Lucene提供了5个基础的类,他们分别是: Document, Field, IndexWriter, Analyzer, Directory。下面分别介绍这5个类的用途:
Document
Document是 用来描述文档的,这里的文档可以指一个HTML页面,一封邮件,或者是一个文本文件。一个Document对象由多个Filed ( 域 ) 组成。可以把一个Document对象想象成关系数据库中的一个记录(一行),而每个Field对象就是记录的一个字段。
Field
Field对象是用来描述一个Document的某个属性的,比如一封电子邮件的标题和内容可以分别用两个Field描述:Field(Title), Field(Content)。
Analyzer
在对文档建立索引之前,需要对文档内容进行分词处理,这部分工作由Analyzer来完成。Analyzer是一个抽象类,有多种实现,比如
StandardAnalyzer。针对不同的语言要选用合适的Analyzer分词。Analyzer把分词后的内容交给IndexWriter来建立索引。IndexWriter
IndexWriter是Lucene用来创建索引的一个核心类,他的作用是将一个个Document对象加入到索引中来。
Directory
这个类表示Lucene的索引的存放的位置,这是一个抽象类,他有两个实现:
FSDirectory, 表示存放在文件系统(File System)中的索引的位置(需传入Path)。RAMDirecoty,表示存在在内存(RAM)中的索引的位置,需要其它参数。
下面开始建立索引。
清单1. 对文本文件建立索引
1 | package com.gthncz.lucene_demo1; |
在Lucene 8版本中,IndexWriter的构造函数需要两个参数,第一个是index存放的Directory对象(FSDirectory or RAMDirectory),第二个是IndexWriterConfig的对象,用于指定使用哪个分词器来分词。接着遍历整个data目录,为每一个文本文档创建一个Document对象,保存path和content Field。最后使用indexWriter将Document对象添加到索引中。
接下来在建立好的索引上进行搜索。
2. 搜索文档
参考搜索文档
我们利用上面建立好的索引,搜索包含某个关键词或短语的文档。Lucene提供了几个基础类来完成这个过程,他们分别是IndexSearcher, Term, Query, TermQuery, Hits. 下面分别介绍这几个类的功能。
Query
这个类的目的是把用户输入的查询字符串封装成Lucene能够识别的Query。这是一个抽象类,具有多种实现,如:
TermQuery,BooleanQuery,PrefixQuery。Term
Term是搜索的基本单位,一个Term对象有两个String类型的域组成。构造一个Term对象由两个部分完成:Term term = new Term(“FieldName”, “queryWord); 其中第一个参数代表Document的那个域,第二个参数代表查询关键词。
TermQuery
TermQuery是Query的一个实现,也是Lucene支持的最基本的一个查询类。生成一个TermQuery由如下语句生成:TermQuery termQuery = new TermQuery(new Term(“fieldName”, “queryWord”)); 他的构造函数只接受一个Term对象作为参数。
IndexSearcher
IndexSearcher是用来在建立好的index上进行搜索的。它只能以只读的方式打开一个索引,所以可以有多个IndexSearcher的实例在一个index上进行操作。
Hits
Hits是用来保存搜索结果的。
参考的文章是比较老的,Lucene 8有部分代码已经修改,因此添加其它几个类。
IndexReader
IndexReader是一个抽象类,提供了一个获取索引时间点视角(point-in-time view)的接口。在Lucene 8中IndexSearcher的构造函数只接受一个IndexReader对象。他有两种不同类型的IndexReader:LeafReader:这种的索引不由几个sub-reader构成,他们是原子的(atomic)。他支持检索存储的Field, doc values, terms, 和 postings.CompositeReader:这类Reader的实例(例如DirectoryReader)只能用于从底层的LeafReader获取存储的field,但是他不能直接检索postings。如果需要那样做,可用通过CompositeReader.getSequentialSubReaders获取sub-readers。
Index在文件系统时的IndexReader通常DirectoryReader的静态方法构造:
DirectoryReader.open(org.apache.lucene.store.Directory)。DirectoryReader实现的是CompositeReader接口,不能直接获取postings。为了效率,在这个API中documents通常由document numbers代替,每个在索引中的文档都有一个唯一的non-negative interger指定。这些document numbers是暂时性的(ephemeral),他们可能在index有改变时(添加或删除document)改变。因此与Clients的Sessions不应该依赖于这个数字。
注:IndexReader的instance是完全线程安全的(completely thread safe),意味着多线程可以并发的调用它的任何方法。如果你的应用需要外部的同步(synchronization),你不应该同步IndexReader实例。
DirectoryReader
DirectoryReader是CompositeReader的一个实现。他通常由静态方法open()构造。Collector
Collector的原始意义是用于收集一个查询的原始数据(raw results),并且实现排序(sorting)或者自定义结果过滤(custom result filtering), collation等。Lucene’s core collectors起源于
Collector和SimpleCollector. 简单起见你可以使用其中的一个类,或者使用子类例如TopDocCollector而不是直接实现Collector。下面是常用的几个子类:TopDocsCollectoris an abstract base class that assumes you will retrieve the top N docs, according to some criteria, after collection is done.TopScoreDocCollectoris a concrete subclassTopDocsCollectorand sorts according to score + docID. This is used internally by theIndexSearchersearch methods that do not take an explicitSort. It is likely the most frequently used collector.TopFieldCollectorsubclassesTopDocsCollectorand sorts according to a specifiedSortobject (sort by field). This is used internally by theIndexSearchersearch methods that take an explicitSort.TimeLimitingCollector, which wraps any other Collector and aborts the search if it’s taken too much time.PositiveScoresOnlyCollectorwraps any other Collector and prevents collection of hits whose score is <= 0.0
TopScoreDocCollector
TopScoreDocCollector实现了搜集top-scoring hits,返回一个TopDocs. 这个类对象用在IndexSearcher方法的基于TopDocs的search方法中。Hits按照score降序(descending)并且按照docID升序(ascending)(当他们的score相等时)。这个类通常使用静态方法TopScoreDocCollector.create(numHits, totalHitsThreshold)来创建实例。注:
Float.NaN和Float.NEGATIVE_INFINITT并不是一个合理的score。这个collector不能正确的搜集这样score的hits.
清单2:在建立好的索引上进行搜索
1 | package com.gthncz.lucene_demo1; |
跑了下没有效果,因为这里的数据集是中文的,存入每个Document的是一整段文字而不是TokenStream,因此在Indexing之前需要进行Analyzer来分词。
接下来我们讨论各种类型的能在analysis过程中使用到的Analyzer objects以及其他相关的objects。理解这个分析过程并且理解analyzer如何工作将有助于理解Lucene为Document建立索引的新视角。下面是一些我们将用到的对象:
Token
Token表示一篇文章中具有相关细节(如它的属性)的text或者word。(position,start ofsset, end offset, token type and its position increment).TokenStream
TokenStream是一个分析过程的处理结果并且它包含多个Token。他是一个抽象类。Analyzer
Analyzer是一个抽象类,是每一种类型的Analyzer的基础类。WhitespaceAnalyzer
WhitespaceAnalyzer利用空格(whitespace)分割一篇文章中的text。SimpleAnalyzer
SimpleAnalyzer利用非字母字符(non-letter characters)分割一篇文章的text,并且将字母转化为lowercase。StopAnalyzer
StopAnalyzer和SimpleAnalyzer类似,并且将一些常用的词语移除,比如‘a’, ‘an’, ‘the’等。StandardAnalyzer
StandardAnalyzer是最复杂的一种分析器,并且可以处理一些names, email address等。它将每个token转换为小写字符,并且将公共词语( common words )和标点符号( punctuations )移除。
但是上述是自带的分词器,由于是外国人写的,,,因此没有中文分词。为此,还需另寻他法。
Ik-Anlyzer是一款不错的中文分词器,项目地址为: ik-anlyzer-solr. Google 官方fork地址为: ik-analyzer. Copy的简介如下:
IK Analyzer是一个开源的,基于java语言开发的轻量级的中文分词工具包。从2006年12月推出1.0版开始, IKAnalyzer已经推出了4个大版本。最初,它是以开源项目Luence为应用主体的,结合词典分词和文法分析算法的中文分词组件。从3.0版本开始,IK发展为面向Java的公用分词组件,独立于Lucene项目,同时提供了对Lucene的默认优化实现。在2012版本中,IK实现了简单的分词歧义排除算法,标志着IK分词器从单纯的词典分词向模拟语义分词衍化。*
使用maven将ik-analyzer加入项目作为依赖:
1 | <!-- Maven仓库地址 --> |
其中,可能需要修改配置文件IKAnalyzer.cfg.xml或者stopword.dic, ext.dic,这里需要在jar包内替换,利用jar -uf jar-file <folder-name>/new-file。
简单的IKAnalyzerDemo:
1 | package com.gthncz; |
在上述的Indexer.java文件中,只需要将new StandardAnalyzer();替换成 new IKAnalyzer();即可,如下:
1 | // Analyzer luceneAnalyzer = new StandardAnalyzer(); |
执行结果如下:(搜索词为童话)
1 | Indexing file /home/gt/Documents/python/NLP/DMSC/复仇者联盟2_小陈同学_2015-11-08.txt |
总结
在这边文章中,我学习了Lucene的入门使用,包括如何创建索引,如何利用索引搜索,如何利用Analyzer进行分词。
下一步学习目标:
- 学习lucene内部创建索引的逻辑
- 学习lucene内部利用索引搜索文档,如何评分
- 学习分词器内部分词方法