发布时间:2025-12-09 17:05:22 浏览次数:4
1 lucene简介
1.1 什么是lucene
项目地址: https://github.com/apache/lucene-solr
Lucene是一个全文搜索框架,而不是应用产品。它只是提供了一种工具让你能实现这些产品。
它的特点概述起来就是:全Java实现、开源、高性能、功能完整、易拓展,功能完整体现在对分词的支持、各种查询方式(前缀、模糊、正则等)、打分高亮、列式存储(DocValues)等等。
| org.apache.lucene | Top-level package. |
| org.apache.lucene.analysis | Text analysis. |
| org.apache.lucene.analysis.standard | Fast, general-purpose grammar-based tokenizer StandardTokenizer implements the Word Break rules from the Unicode Text Segmentation algorithm, as specified inUnicode Standard Annex #29. |
| org.apache.lucene.analysis.tokenattributes | General-purpose attributes for text analysis. |
| org.apache.lucene.codecs | Codecs API: API for customization of the encoding and structure of the index. |
| org.apache.lucene.codecs.blocktree | BlockTree terms dictionary. |
| org.apache.lucene.codecs.compressing | StoredFieldsFormat that allows cross-document and cross-field compression of stored fields. |
| org.apache.lucene.codecs.lucene50 | Components from the Lucene 5.0 index format See org.apache.lucene.codecs.lucene50 for an overview of the index format. |
| org.apache.lucene.codecs.lucene60 | Components from the Lucene 6.0 index format. |
| org.apache.lucene.codecs.lucene62 | Components from the Lucene 6.2 index format See org.apache.lucene.codecs.lucene70 for an overview of the current index format. |
| org.apache.lucene.codecs.lucene70 | Lucene 7.0 file format. |
| org.apache.lucene.codecs.perfield | Postings format that can delegate to different formats per-field. |
| org.apache.lucene.document | The logical representation of a Document for indexing and searching. |
| org.apache.lucene.geo | Geospatial Utility Implementations for Lucene Core |
| org.apache.lucene.index | Code to maintain and access indices. |
| org.apache.lucene.search | Code to search indices. |
| org.apache.lucene.search.similarities | This package contains the various ranking models that can be used in Lucene. |
| org.apache.lucene.search.spans | The calculus of spans. |
| org.apache.lucene.store | Binary i/o API, used for all index data. |
| org.apache.lucene.util | Some utility classes. |
| org.apache.lucene.util.automaton | Finite-state automaton for regular expressions. |
| org.apache.lucene.util.bkd | Block KD-tree, implementing the generic spatial data structure described in this paper. |
| org.apache.lucene.util.fst | Finite state transducers |
| org.apache.lucene.util.graph | Utility classes for working with token streams as graphs. |
| org.apache.lucene.util.mutable | Comparable object wrappers |
| org.apache.lucene.util.packed | Packed integer arrays and streams. |
| Analyzer | An Analyzer builds TokenStreams, which analyze text. |
| Analyzer.ReuseStrategy | Strategy defining how TokenStreamComponents are reused per call to Analyzer.tokenStream(String, java.io.Reader). |
| Analyzer.TokenStreamComponents | This class encapsulates the outer components of a token stream. |
| AnalyzerWrapper | Extension to Analyzer suitable for Analyzers which wrap other Analyzers. |
| CachingTokenFilter | This class can be used if the token attributes of a TokenStream are intended to be consumed more than once. |
| CharacterUtils | Utility class to write tokenizers or token filters. |
| CharacterUtils.CharacterBuffer | A simple IO buffer to use with CharacterUtils.fill(CharacterBuffer, Reader). |
| CharArrayMap<V> | A simple class that stores key Strings as char[]'s in a hash table. |
| CharArraySet | A simple class that stores Strings as char[]'s in a hash table. |
| CharFilter | Subclasses of CharFilter can be chained to filter a Reader They can be used as Reader with additional offset correction. |
| DelegatingAnalyzerWrapper | An analyzer wrapper, that doesn't allow to wrap components or readers. |
| FilteringTokenFilter | Abstract base class for TokenFilters that may remove tokens. |
| LowerCaseFilter | Normalizes token text to lower case. |
| StopFilter | Removes stop words from a token stream. |
| StopwordAnalyzerBase | Base class for Analyzers that need to make use of stopword sets. |
| TokenFilter | A TokenFilter is a TokenStream whose input is another TokenStream. |
| Tokenizer | A Tokenizer is a TokenStream whose input is a Reader. |
| TokenStream | A TokenStream enumerates the sequence of tokens, either from Fields of a Document or from query text. |
| TokenStreamToAutomaton | Consumes a TokenStream and creates an Automaton where the transition labels are UTF8 bytes (or Unicode code points if unicodeArcs is true) from the TermToBytesRefAttribute. |
| WordlistLoader | Loader for text files that represent a list of stopwords. |
1.2 lucene能做什么
要回答这个问题,先要了解lucene的本质。实际上lucene的功能很单一,说到底,就是你给它若干个字符串,然后它为你提供一个全文搜索服务,告诉你你要搜索的关键词出现在哪里。知道了这个本质,你就可以发挥想象做任何符合这个条件的事情了。你可以把站内新闻都索引了,做个资料库;你可以把一个数据库表的若干个字段索引起来,那就不用再担心因为“%like%”而锁表了;你也可以写个自己的搜索引擎……
样例:
Analyzer analyzer = new StandardAnalyzer();// Store the index in memory:Directory directory = new RAMDirectory();// To store an index on disk, use this instead://Directory directory = FSDirectory.open("/tmp/testindex");IndexWriterConfig config = new IndexWriterConfig(analyzer);IndexWriter iwriter = new IndexWriter(directory, config);Document doc = new Document();String text = "This is the text to be indexed.";doc.add(new Field("fieldname", text, TextField.TYPE_STORED));iwriter.addDocument(doc);iwriter.close();// Now search the index:DirectoryReader ireader = DirectoryReader.open(directory);IndexSearcher isearcher = new IndexSearcher(ireader);// Parse a simple query that searches for "text":QueryParser parser = new QueryParser("fieldname", analyzer);Query query = parser.parse("text");ScoreDoc[] hits = isearcher.search(query, null, 1000).scoreDocs;assertEquals(1, hits.length);// Iterate through the results:for (int i = 0; i < hits.length; i++) {Document hitDoc = isearcher.doc(hits[i].doc);assertEquals("This is the text to be indexed.", hitDoc.get("fieldname"));}ireader.close();directory.close();
1.3 你该不该选择lucene
下面给出一些测试数据,如果你觉得可以接受,那么可以选择。
测试一:250万记录,300M左右文本,生成索引380M左右,800线程下平均处理时间300ms。
测试二:37000记录,索引数据库中的两个varchar字段,索引文件2.6M,800线程下平均处理时间1.5ms。
2 lucene的工作方式
lucene提供的服务实际包含两部分:一入一出。所谓入是写入,即将你提供的源(本质是字符串)写入索引或者将其从索引中删除;所谓出是读出,即向用户提供全文搜索服务,让用户可以通过关键词定位源。
2.1写入流程
源字符串首先经过analyzer处理,包括:分词,分成一个个单词;去除stopword(可选)。
将源中需要的信息加入Document的各个Field中,并把需要索引的Field索引起来,把需要存储的Field存储起来。
将索引写入存储器,存储器可以是内存或磁盘。
2.2读出流程
用户提供搜索关键词,经过analyzer处理。
对处理后的关键词搜索索引找出对应的Document。
用户根据需要从找到的Document中提取需要的Field。
3 一些需要知道的概念
lucene用到一些概念,了解它们的含义,有利于下面的讲解。
3.1 analyzer
Analyzer 是分析器,它的作用是把一个字符串按某种规则划分成一个个词语,并去除其中的无效词语,这里说的无效词语是指英文中的“of”、 “the”,中文中的 “的”、“地”等词语,这些词语在文章中大量出现,但是本身不包含什么关键信息,去掉有利于缩小索引文件、提高效率、提高命中率。
分词的规则千变万化,但目的只有一个:按语义划分。这点在英文中比较容易实现,因为英文本身就是以单词为单位的,已经用空格分开;而中文则必须以某种方法将连成一片的句子划分成一个个词语。具体划分方法下面再详细介绍,这里只需了解分析器的概念即可。
3.2 document
用户提供的源是一条条记录,它们可以是文本文件、字符串或者数据库表的一条记录等等。一条记录经过索引之后,就是以一个Document的形式存储在索引文件中的。用户进行搜索,也是以Document列表的形式返回。
3.3 field
一个Document可以包含多个信息域,例如一篇文章可以包含“标题”、“正文”、“最后修改时间”等信息域,这些信息域就是通过Field在Document中存储的。
Field有两个属性可选:存储和索引。通过存储属性你可以控制是否对这个Field进行存储;通过索引属性你可以控制是否对该Field进行索引。这看起来似乎有些废话,事实上对这两个属性的正确组合很重要,下面举例说明:
还是以刚才的文章为例子,我们需要对标题和正文进行全文搜索,所以我们要把索引属性设置为真,同时我们希望能直接从搜索结果中提取文章标题,所以我们把标题域的存储属性设置为真,但是由于正文域太大了,我们为了缩小索引文件大小,将正文域的存储属性设置为假,当需要时再直接读取文件;我们只是希望能从搜索解果中提取最后修改时间,不需要对它进行搜索,所以我们把最后修改时间域的存储属性设置为真,索引属性设置为假。上面的三个域涵盖了两个属性的三种组合,还有一种全为假的没有用到,事实上Field不允许你那么设置,因为既不存储又不索引的域是没有意义的。
3.4 term
term是搜索的最小单位,它表示文档的一个词语,term由两部分组成:它表示的词语和这个词语所出现的field。
3.5 tocken
tocken是term的一次出现,它包含trem文本和相应的起止偏移,以及一个类型字符串。一句话中可以出现多次相同的词语,它们都用同一个term表示,但是用不同的tocken,每个tocken标记该词语出现的地方。
3.6 segment
添加索引时并不是每个document都马上添加到同一个索引文件,它们首先被写入到不同的小文件,然后再合并成一个大索引文件,这里每个小文件都是一个segment。
4 lucene的结构
lucene包括core和sandbox两部分,其中core是lucene稳定的核心部分,sandbox包含了一些附加功能,例如highlighter、各种分析器。
Lucene core有七个包:analysis,document,index,queryParser,search,store,util。
4.1 analysis
Analysis包含一些内建的分析器,例如按空白字符分词的WhitespaceAnalyzer,添加了stopwrod过滤的StopAnalyzer,最常用的StandardAnalyzer。
4.2 document
Document包含文档的数据结构,例如Document类定义了存储文档的数据结构,Field类定义了Document的一个域。
4.3 index
Index 包含了索引的读写类,例如对索引文件的segment进行写、合并、优化的IndexWriter类和对索引进行读取和删除操作的 IndexReader类,这里要注意的是不要被IndexReader这个名字误导,以为它是索引文件的读取类,实际上删除索引也是由它完成, IndexWriter只关心如何将索引写入一个个segment,并将它们合并优化;IndexReader则关注索引文件中各个文档的组织形式。
4.4 queryParser
QueryParser 包含了解析查询语句的类,lucene的查询语句和sql语句有点类似,有各种保留字,按照一定的语法可以组成各种查询。 Lucene有很多种 Query类,它们都继承自Query,执行各种特殊的查询,QueryParser的作用就是解析查询语句,按顺序调用各种 Query类查找出结果。
4.5 search
Search包含了从索引中搜索结果的各种类,例如刚才说的各种Query类,包括TermQuery、BooleanQuery等就在这个包里。
4.6 store
Store包含了索引的存储类,例如Directory定义了索引文件的存储结构,FSDirectory为存储在文件中的索引,RAMDirectory为存储在内存中的索引,MmapDirectory为使用内存映射的索引。
4.7 util
Util包含一些公共工具类,例如时间和字符串之间的转换工具。
5 如何建索引
5.1 最简单的能完成索引的代码片断
下面我们分析一下这段代码。
首先我们创建了一个writer,并指定存放索引的目录为“/data/index”,使用的分析器为StandardAnalyzer,第三个参数说明如果已经有索引文件在索引目录下,我们将覆盖它们。
然后我们新建一个document。
我们向document添加一个field,名字是“title”,内容是“lucene introduction”,对它进行存储并索引。
再添加一个名字是“content”的field,内容是“lucene works well”,也是存储并索引。
然后我们将这个文档添加到索引中,如果有多个文档,可以重复上面的操作,创建document并添加。
添加完所有document,我们对索引进行优化,优化主要是将多个segment合并到一个,有利于提高索引速度。
随后将writer关闭,这点很重要。
对,创建索引就这么简单!
当然你可能修改上面的代码获得更具个性化的服务。
5.2 将索引直接写在内存
你需要首先创建一个RAMDirectory,并将其传给writer,代码如下:
5.3 索引文本文件
如果你想把纯文本文件索引起来,而不想自己将它们读入字符串创建field,你可以用下面的代码创建field:
Field field = new Field("content", new FileReader(file));
这里的file就是该文本文件。该构造函数实际上是读去文件内容,并对其进行索引,但不存储。
6 如何维护索引
索引的维护操作都是由IndexReader类提供。
6.1 如何删除索引
lucene提供了两种从索引中删除document的方法,一种是
void deleteDocument(int docNum)
这种方法是根据document在索引中的编号来删除,每个document加进索引后都会有个唯一编号,所以根据编号删除是一种精确删除,但是这个编号是索引的内部结构,一般我们不会知道某个文件的编号到底是几,所以用处不大。另一种是
void deleteDocuments(Term term)
这种方法实际上是首先根据参数term执行一个搜索操作,然后把搜索到的结果批量删除了。我们可以通过这个方法提供一个严格的查询条件,达到删除指定document的目的。
下面给出一个例子:
6.2 如何更新索引
lucene并没有提供专门的索引更新方法,我们需要先将相应的document删除,然后再将新的document加入索引。例如:
Lucene自带多种分词器,其中对中文分词支持比较好的是smartcn。
在演示smartcn中文分词器之前,先来看看Lucene标准分词器对中文分词的效果。需要的jar为\lucene-5.5.5\core\下的lucene-core-5.5.5.jar和\lucene-5.5.5\analysis\common\下的lucene-analyzers-common-5.5.5.jar。新建测试类TestLucene04:
package net.xxpsw.demo.lucene.test;import org.apache.lucene.analysis.Analyzer;import org.apache.lucene.analysis.TokenStream;import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;public class TestLucene04 {private void print(Analyzer analyzer) throws Exception {String text = "Lucene自带多种分词器,其中对中文分词支持比较好的是smartcn。";TokenStream tokenStream = analyzer.tokenStream("content", text);CharTermAttribute attribute = tokenStream.addAttribute(CharTermAttribute.class);tokenStream.reset();while (tokenStream.incrementToken()) {System.out.println(new String(attribute.toString()));}}}
增加测试方法testStandardAnalyzer:
/*** @Description: 测试标准分词器* @throws Exception*/@Testpublic void testStandardAnalyzer() throws Exception {StandardAnalyzer standardAnalyzer = new StandardAnalyzer();print(standardAnalyzer);}
执行测试,控制台打印结果如下:
由上可见,标准分词器对中文的分词结果是分成了单个汉字,而并没有识别常用的汉语词组。
引入analysis\smartcn\下的lucene-analyzers-smartcn-xx.jar,新增测试方法testSmartChineseAnalyzer:
/*** @Description: 测试中文分词器* @throws Exception*/@Testpublic void testSmartChineseAnalyzer() throws Exception {SmartChineseAnalyzer smartChineseAnalyzer = new SmartChineseAnalyzer();print(smartChineseAnalyzer);}
执行测试方法,控制台打印结果如下:
从上述打印结果中可以看到,“多种”、“分词”、“其中”、“中文”、“支持”、“比较”等中文词组都被顺利的识别出来。
进入SmartChineseAnalyzer源码中可以看到如下代码:
查看this(true):
public SmartChineseAnalyzer(boolean useDefaultStopWords) {stopWords = useDefaultStopWords ? DefaultSetHolder.DEFAULT_STOP_SET: CharArraySet.EMPTY_SET;}
查看DefaultSetHolder.DEFAULT_STOP_SET:
private static class DefaultSetHolder {static final CharArraySet DEFAULT_STOP_SET;static {try {DEFAULT_STOP_SET = loadDefaultStopWordSet();} catch (IOException ex) {// default set should always be present as it is part of the// distribution (JAR)throw new RuntimeException("Unable to load default stopword set");}}static CharArraySet loadDefaultStopWordSet() throws IOException {// make sure it is unmodifiable as we expose it in the outer classreturn CharArraySet.unmodifiableSet(WordlistLoader.getWordSet(IOUtils.getDecodingReader(SmartChineseAnalyzer.class, DEFAULT_STOPWORD_FILE,StandardCharsets.UTF_8), STOPWORD_FILE_COMMENT));}}
查看DEFAULT_STOPWORD_FILE:
private static final String DEFAULT_STOPWORD_FILE = "stopwords.txt";可见SmartChineseAnalyzer的空构造函数实际上默认加载了一个停用词列表文件stopwords.txt,该文件在jar包中的位置如下:
该文件中包含了53个需要停用的标点符号,分别是,.`-_=?'|"(){}[]<>*#&^$@!~:;+/\《》—-,。、:;!·?“”)(【】[]●及中文空格。
SmartChineseAnalyzer还支持自定义停用词,新建测试方法testMySmartChineseAnalyzer:
/*** @Description: 测试自定义停用词* @throws Exception*/@Testpublic void testMySmartChineseAnalyzer() throws Exception {CharArraySet charArraySet = new CharArraySet(0, true);// 系统默认停用词Iterator<Object> iterator = SmartChineseAnalyzer.getDefaultStopSet().iterator();while (iterator.hasNext()) {charArraySet.add(iterator.next());}// 自定义停用词String[] myStopWords = { "对", "的", "是", "其中" };for (String stopWord : myStopWords) {charArraySet.add(stopWord);}SmartChineseAnalyzer smartChineseAnalyzer = new SmartChineseAnalyzer(charArraySet);print(smartChineseAnalyzer);}}
执行测试方法,控制台打印如下:
对比测试方法testSmartChineseAnalyzer可以看出,"对", "的", "是", "其中"等中文词已被停用而不再出现。
=============================================================================================
谈谈架构:
Lucene最初由鼎鼎大名Doug Cutting开发,2000年开源,现在也是开源全文检索方案的不二选择,它的特点概述起来就是:全Java实现、开源、高性能、功能完整、易拓展,功能完整体现在对分词的支持、各种查询方式(前缀、模糊、正则等)、打分高亮、列式存储(DocValues)等等。
而且Lucene虽已发展10余年,但仍保持着一个活跃的开发度,以适应着日益增长的数据分析需求,最新的6.0版本里引入block k-d trees,全面提升了数字类型和地理位置信息的检索性能,另基于Lucene的Solr和ElasticSearch分布式检索分析系统也发展地如火如荼,ElasticSearch也在我们项目中有所应用。
Lucene整体使用如图所示:
结合代码说明一下四个步骤:
IndexWriter iw=new IndexWriter();//创建IndexWriterDocument doc=new Document( new StringField("name", "Donald Trump", Field.Store.YES)); //构建索引文档iw.addDocument(doc); //做索引库IndexReader reader = DirectoryReader.open(FSDirectory.open(new File(index)));IndexSearcher searcher = new IndexSearcher(reader); //打开索引Query query = parser.parse("name:trump");//解析查询TopDocs results =searcher.search(query, 100);//检索并取回前100个文档号for(ScoreDoc hit:results.hits){Document doc=searcher .doc(hit.doc)//真正取文档}使用起来很简单,但只有知道这背后的原理,才能更好地用好Lucene,后面将介绍通用检索原理和Lucene的实现细节。
全文检索技术由来已久,绝大多数都基于倒排索引来做,曾经也有过一些其他方案如文件指纹。倒排索引,顾名思义,它相反于一篇文章包含了哪些词,它从词出发,记载了这个词在哪些文档中出现过,由两部分组成——词典和倒排表。
其中词典结构尤为重要,有很多种词典结构,各有各的优缺点,最简单如排序数组,通过二分查找来检索数据,更快的有哈希表,磁盘查找有B树、B+树,但一个能支持TB级数据的倒排索引结构需要在时间和空间上有个平衡,下图列了一些常见词典的优缺点:
其中可用的有:B+树、跳跃表、FST
B+树:
mysql的InnoDB B+数结构
理论基础:平衡多路查找树
优点:外存索引、可更新
缺点:空间大、速度不够快
优点:结构简单、跳跃间隔、级数可控,Lucene3.0之前使用的也是跳跃表结构,后换成了FST,但跳跃表在Lucene其他地方还有应用如倒排表合并和文档号索引。
缺点:模糊查询支持不好
Lucene现在使用的索引结构
理论基础: 《Direct construction of minimal acyclic subsequential transducers》,通过输入有序字符串构建最小有向无环图。
优点:内存占用率低,压缩率一般在3倍~20倍之间、模糊查询支持好、查询快
缺点:结构复杂、输入要求有序、更新不易
Lucene里有个FST的实现,从对外接口上看,它跟Map结构很相似,有查找,有迭代:
100万数据性能测试:
| 构建时间(ms) | 185 | 500 | 1512 |
| 查询所有key(ms) | 106 | 218 | 890 |
可以看出,FST性能基本跟HaspMap差距不大,但FST有个不可比拟的优势就是占用内存小,只有HashMap10分之一左右,这对大数据规模检索是至关重要的,毕竟速度再快放不进内存也是没用的。
因此一个合格的词典结构要求有:
1. 查询速度。
2. 内存占用。
3. 内存+磁盘结合。
后面我们将解析Lucene索引结构,重点从Lucene的FST实现特点来阐述这三点。
*(本文对Lucene的原理介绍都是基于4.10.3)*
下面详细介绍各部分结构:
索引结构
Lucene现在采用的数据结构为FST,它的特点就是:
1、词查找复杂度为O(len(str))
2、共享前缀、节省空间
3、内存存放前缀索引、磁盘存放后缀词块
这跟我们前面说到的词典结构三要素是一致的:1. 查询速度。2. 内存占用。3. 内存+磁盘结合。我们往索引库里插入四个单词abd、abe、acf、acg,看看它的索引文件内容。
tip部分,每列一个FST索引,所以会有多个FST,每个FST存放前缀和后缀块指针,这里前缀就为a、ab、ac。tim里面存放后缀块和词的其他信息如倒排表指针、TFDF等,doc文件里就为每个单词的倒排表。
所以它的检索过程分为三个步骤:
1. 内存加载tip文件,通过FST匹配前缀找到后缀词块位置。
2. 根据词块位置,读取磁盘中tim文件中后缀块并找到后缀和相应的倒排表位置信息。
3. 根据倒排表位置去doc文件中加载倒排表。
这里就会有两个问题,第一就是前缀如何计算,第二就是后缀如何写磁盘并通过FST定位,下面将描述下Lucene构建FST过程:
已知FST要求输入有序,所以Lucene会将解析出来的文档单词预先排序,然后构建FST,我们假设输入为abd,abd,acf,acg,那么整个构建过程如下:
倒排表结构
倒排表就是文档号集合,但怎么存,怎么取也有很多讲究,Lucene现使用的倒排表结构叫Frame of reference,它主要有两个特点:
1. 数据压缩,可以看下图怎么将6个数字从原先的24bytes压缩到7bytes。
2. 跳跃表加速合并,因为布尔查询时,and 和or 操作都需要合并倒排表,这时就需要快速定位相同文档号,所以利用跳跃表来进行相同文档号查找。
这部分可参考ElasticSearch的一篇博客,里面有一些性能测试:
ElasticSearch 倒排表
正向文件
正向文件指的就是原始文档,Lucene对原始文档也提供了存储功能,它存储特点就是分块+压缩,fdt文件就是存放原始文档的文件,它占了索引库90%的磁盘空间,fdx文件为索引文件,通过文档号(自增数字)快速得到文档位置,它们的文件结构如下:
fnm中为元信息存放了各列类型、列名、存储方式等信息。
fdt为文档值,里面一个chunk就是一个块,Lucene索引文档时,先缓存文档,缓存大于16KB时,就会把文档压缩存储。一个chunk包含了该chunk起始文档、多少个文档、压缩后的文档内容。
fdx为文档号索引,倒排表存放的时文档号,通过fdx才能快速定位到文档位置即chunk位置,它的索引结构比较简单,就是跳跃表结构,首先它会把1024个chunk归为一个block,每个block记载了起始文档值,block就相当于一级跳表。
所以查找文档,就分为三步:
第一步二分查找block,定位属于哪个block。
第二步就是根据从block里根据每个chunk的起始文档号,找到属于哪个chunk和chunk位置。
第三步就是去加载fdt的chunk,找到文档。这里还有一个细节就是存放chunk起始文档值和chunk位置不是简单的数组,而是采用了平均值压缩法。所以第N个chunk的起始文档值由 DocBase + AvgChunkDocs * n + DocBaseDeltas[n]恢复而来,而第N个chunk再fdt中的位置由 StartPointerBase + AvgChunkSize * n + StartPointerDeltas[n]恢复而来。
从上面分析可以看出,lucene对原始文件的存放是行是存储,并且为了提高空间利用率,是多文档一起压缩,因此取文档时需要读入和解压额外文档,因此取文档过程非常依赖随机IO,以及lucene虽然提供了取特定列,但从存储结构可以看出,并不会减少取文档时间。
Lucene总的来说是:
在Lucene in action中,Lucene 的构架和过程如下图,
说明Lucene 是有索引和搜索的两个过程,包含索引创建,索引,搜索三个要点。
让我们更细一些看Lucene的各组件:
那么如何应用这些组件呢?
让我们再详细到对Lucene API 的调用实现索引和搜索过程。
然而当进入Lucene的源代码后,发现Lucene有很多包,关系错综复杂。
然而通过下图,我们不难发现,Lucene的各源码模块,都是对普通索引和搜索过程的一种实现。
此图是上一节介绍的全文检索的流程对应的Lucene实现的包结构。(参照http://www.lucene.com.cn/about.htm 中文章《开放源代码的全文检索引擎Lucene》)
转载于: http://m.2cto.com/kf/201701/584148.html