Lucene的系统由基础结构封装、索引核心、对外接口三大部分组成,其中直接操作索引文件的索引核心又是系统的重点。
Lucene包结构功能列表
lucene-core-4.x.jar
org.apache.lucene.analysis | 语言分析器,主要用于切词,支持中文主要扩展此类 |
org.apache.lucene.document | 索引存储时的文档结构管理,类似于关系数据库的表结构 |
org.apache.lucene.index | 索引管理,包括索引建立,删除 |
org.apache.lucene.queryparser | 查询分析器,实现查询关键字之间的运算,如与、或、非 |
org.apache.lucene.store | 数据存储管理,主要包括底层的一些io操作 |
org.apache.lucene.search | 检索管理,根据查询条件检索到结果 |
org.apache.lucene.util | 一些公用类 |
Lucene功能强大,但从根本上说,主要包括两块: 一是文本内容经切词后索引入库; 二是根据查询条件返回结果;
查询逻辑
1.查询者输入查询条件,条件之间可以通过特定运算符进行运算,比如查询希望查询到与“中国”和“北京”相关的记录,但不希望结果中包括“海淀区中关村”,于是输入条件为“中国+北京-海淀区中关村”;
2.查询条件被传达到查询分析器中,分析器将将对“中国+北京-海淀区中关村”进行分析,首先分析器解析字符串的连接符,即这里的加号和减号,然后对每个词进行切词,一般最小的词元是两个汉字,则中国和北京两个词不必再切分,但对海淀区中关村需要切分,假设根据切词算法,把该词切分为“海淀区”和“中关村”两部分,则最后得到的查询条件可以表示为:“中国” AND “北京” AND NOT(“海淀区” AND “中关村”)。
3.查询器根据这个条件遍历索引树,得到查询结果,并返回结果集,返回的结果集类似于JDBC中的ResultSet。
4.将返回的结果集显示在查询结果页面,当点击某一条内容时,可以链接到原始网页,也可以打开全文检索库中存储的网页内容。
5.这就是查询的逻辑过程,需要说明的是,Lucene默认只支持英文,为了便于说明问题,以上查询过程采用中文举例,事实上,当Lucene被扩充支持中文后就是这么一个查询过程。
入库逻辑
1.入库者定义到库中文档的结构,比如需要把网站内容加载到全文检索库,让用户通过“站内检索”搜索到相关的网页内容。入库文档结构与关系型数据库中的表结构类 似,每个入库的文档由多个字段构成,假设这里需要入库的网站内容包括如下字段:文章标题、作者、发布时间、原文链接、正文内容(一般作为网页快照)。
2.包含N个字段的文档(DOCUMENT)在真正入库前需要经过切词(或分词)索引,切词的规则由语言分析器(ANALYZER)完成。
3.切分后的“单词”被注册到索引树上,供查询时用,另外也需要把其它不需要索引的内容入库,所有这些是文件操作均由STORAGE完成。
4.Lucene的索引树结构非常优秀,是Lucene的一大特色。
理解核心索引类
为了对文档进行索引,Lucene 提供了五个基础的类
public class IndexWriter
–org.apache.lucene.index.IndexWriter
public abstract class Directory
–org.apache.lucene.store.Directory
public abstract class Analyzer
–org.apache.lucene.analysis.Analyzer
public final class Document
–org.apache.lucene.document.Document
public final class Field
–org.apache.lucene.document.Field
IndexWriter
IndexWriter是在索引过程中的中心组件。
IndexWriter这个类创建一个新的索引并且添加文档到一个已有的索引中。你可以把IndexWriter想象成让你可以对索引进行写操作的对象,但是不能让你读取或搜索。
IndexWriter不是唯一的用来修改索引的类
Directory
Directory类代表一个Lucene索引的位置。它是一个抽象类.
其中的两个实现:
◦第一个是 FSDirectory,它表示一个存储在文件系统中的索引的位置。
◦第二个是 RAMDirectory,它表示一个存储在内存当中的索引的位置。
在我们的Indexer示例中,我们使用一个实际文件系统目录的路径传递给IndexWriter的构造函数来获得Directory的一个实例。IndexWriter然后使用Directory的一个具体实现FSDirectory,并在文件系统的一个目录中创建索引。
Analyzer
在一个文档被索引之前,首先需要对文档内容进行分词处理,并且而剔除一些冗余的词句(例如:a,the,they等),这部分工作就是由 Analyzer 来做的。
Analyzer 类是一个抽象类,它有多个实现。
针对不同的语言和应用需要选择适合的 Analyzer。Analyzer 把分词后的内容交给 IndexWriter 来建立索引。
Document
org.apache.lucene.document.Document
Document文档类似数据库中的一条记录,可以由好几个字段(Field)组成,并且字段可以套用不同的类型。
一个Field代表与这个文档相关的元数据。元数据如作者、标题、主题、修改日期等等,分别做为文档的字段索引和存储。
Document的方法:
◦void add(Fieldable field)添加一个字段(Field)到Document中
◦String get(String name)从文档中获得一个字段对应的文本
Field
org.apache.lucene.document.Field
Field 对象是用来描述一个文档的某个属性的,比如一封电子邮件的标题和内容可以用两个 Field 对象分别描述。
静态内部类
Field.Index Deprecated. This is here only to ease transition from the pre-4.0 APIs.
Field.TermVector Deprecated. This is here only to ease transition from the pre-4.0 APIs.
Field.Store 表示Field的存储方式
◦NO 原文不存储在索引文件中,搜索结果命中后,再根据其他附加属性如文件的Path,数据库的主键等,重新连接打开原文,适合原文内容较大的情况。
◦YES 索引文件本来只存储索引数据, 此设计将原文内容直接也存储在索引文件中,如文档的标题。
关于Field的重要说明
org.apache.lucene.document.StringField
◦A field that is indexed but not tokenized: the entire String value is indexed as a single token.
org.apache.lucene.document.TextField
◦A field that is indexed and tokenized, without term vectors. For example this would be used on a ‘body’ field, that contains the bulk of a document’s text.
org.apache.lucene.document.BinaryDocValuesField
◦The values are stored directly with no sharing, which is a good fit when the fields don’t share (many) values, such as a title field. If values may be shared and sorted it’s better to use SortedDocValuesField.
简单demo
public static void main(String[] args) throws Exception { //内存索引目录 Directory dir = new RAMDirectory(); //若用文件目录 //Directory dir = FSDirectory.open(new File("path")); Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_46); IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_46, analyzer); IndexWriter iwriter = new IndexWriter(dir, config); Document doc = new Document(); String title = "标题"; String content = "被索引的内容,内容"; doc.add(new Field("title", title, TextField.TYPE_STORED)); doc.add(new Field("content", content, TextField.TYPE_STORED)); iwriter.addDocument(doc); Document doc1 = new Document(); String title1 = "标题1"; String content1 = "被索引的内容,内容"; doc1.add(new Field("title", title1, TextField.TYPE_STORED)); doc1.add(new Field("content", content1, TextField.TYPE_STORED)); iwriter.addDocument(doc1); iwriter.close(); DirectoryReader ireader = DirectoryReader.open(dir); IndexSearcher isearcher = new IndexSearcher(ireader); QueryParser parser = new QueryParser(Version.LUCENE_46, "content", analyzer); Query query = parser.parse("内容"); ScoreDoc[] hits = isearcher.search(query, null, 1000).scoreDocs; System.out.println("查询结果数:" + hits.length); for (int i = 0; i < hits.length; i++) { Document hitDoc = isearcher.doc(hits[i].doc); System.out.println(hits[i] + "搜索的结果title" + hitDoc.get("title")); } ireader.close(); dir.close(); }
输出结果:
查询结果数:2 doc=0 score=0.3715843 shardIndex=-1搜索的结果title标题
doc=1 score=0.3715843 shardIndex=-1搜索的结果title标题1