MyBlog


Lucene

<p>[TOC]</p> <h1>简介</h1> <p>Lucene 是一个开源的全文检索引擎工具包,最初由Doug Cutting 开发。早在1997 年,资深全文检索专家Doug Cutting 用一个周末的时间, 使用Java 语言创作了一个文本搜索的开源函数库,目的是为各种中小型应用软件加入全文检索功能。不久之后,Lucene 诞生了,2000年Lucene 成为Apache 开源社区的一个子项目。随着Lucene 被人们熟知,越来越多的用户和研发人员加入其中,完善并壮大项目的发展,Lucene 已成为最受欢迎的具有完整的查询引擎和索引引擎的全文检索库。</p> <h1>特点</h1> <h2>稳定,索引性能高</h2> <p>现代硬盘上每小时能够索引150GB 以上的数据。</p> <p>对内存的要求小—只需要1 MB 的堆内存。</p> <p>增量索引和批量索引一样快。</p> <p>索引的大小约为索引文本大小的20%􃀜30%。</p> <h2>高效、准确、高性能的搜索算法</h2> <p>搜索排名—最好的结果显示在最前面。</p> <p>• 许多强大的查询类型:短语查询、通配符查询、近似查询、范围查询等。</p> <p>• 对字段级别搜索(如标题,作者,内容)。</p> <p>• 可以对任意字段排序。</p> <p>• 支持搜索多个索引并合并搜索结果。</p> <p>• 支持更新操作和查询操作同时进行。</p> <p>• 灵活的切面、高亮、join 和group by 功能。</p> <p>• 速度快,内存效率高,容错性好。</p> <p>• 可选排序模型,包括向量空间模型和BM25 模型。</p> <p>• 可配置存储引擎。</p> <h2>跨平台解决方案</h2> <p>•作为Apache 开源许可,在商业软件和开放程序中都可以使用Lucene。</p> <p>• 100%纯Java 编写。</p> <p>• 对多种语言提供接口。</p> <h1>架构</h1> <p>主要可以分为三部分</p> <p>1,信息收集</p> <p>2,信息处理,也就是分词创建索引的过程</p> <p>3,用户查询</p> <p>如下图:</p> <p><img src="https://www.showdoc.cc/server/api/common/visitfile/sign/13cb31bd048aeef70951c5b24175f1ad?showdoc=.jpg" alt="" /></p> <h1>Lucene 索引</h1> <p>索引文档就是把文档变成索引这种数据结构的过程。文档是Lucene 索引的基本单位,比文档更小的单</p> <p>位是字段,字段是文档的一部分,每个字段由3 部分组成:名称(name) 、类型(type) 和取值</p> <p>(value)。</p> <h2>字段类型</h2> <h3>TextField</h3> <p>TextField 会把该字段的内容索引并词条化,但是不保存词向量。比如,包含整篇文档内容的body </p> <p>字段,常常使用TextField 类型进行索引。</p> <h3>StringField</h3> <p>StringField 只会对该字段的内容索引,但是并不词条化,也不保存词向量。字符串的值会</p> <p>被索引为一个单独的词项。比如,有个字段是国家名称,字段名为“country” ,以国家“阿</p> <p>尔吉利亚”为例,只索引不词条化是最合适的。</p> <h3>IntPoint</h3> <p>IntPoint 适合索引值为int 类型的字段</p> <h3>LongPoint</h3> <p>类似于IntPoint 。适合索引值为long的字段</p> <h3>SortedDocValuesField</h3> <p>存储值为文本内容的DocValue 字段,SortedDocValuesField 适合索引字段值为文本内容并</p> <p>且需要按值进行<strong>排序</strong>的字段。</p> <h3>NumericDocValuesField</h3> <p>存储单个数值类型的DocValues 字段,主要包括(int,long , float , double ) 。</p> <h3>SortedNumericDocValuesField</h3> <p>存储数值类型的有序数组列表的DocValues 字段。</p> <h3>StoredField</h3> <p>storedField 适合索引只需要保存字段值不进行其他操作的字段。</p> <blockquote> <p>注:DocValues 是Lucene 4.X 版本以后新增的重要特性,我们都知道,Lucene 是使用经</p> <p>典的倒排索引的模式来达到快速检索的目的,简单地说,就是建立词项和文档id 的关系映射,</p> <p>在搜索时,通过类似hash 算法来快速定位到一个搜索关键词,然后读取文档id 集合,这样搜</p> <p>索数据是非常高效快速的, 当然它也存在一定的缺陷。假如我们需要对数据做一些聚合操作,</p> <p>例如排序、分组时, Lucene 内部会遍历提取所有出现在文档集合的排序字段,然后再次构建</p> <p>一个最终的排好序的文档集合,这个过程全部维持在内存中操作,而且如果排序数据量巨大的</p> <p>话,非常容易造成内存溢出和性能缓慢。基于此,在lucene 4.X 之后出现了DocValues 这一新</p> <p>特性,DocValues 其实是Lucene 在构建索引时额外建立一个有序的基于document=&gt;field/value</p> <p>的映射列表。在构建索引时会对幵启docvalues 的字段额外构建一个已经排好序的文档到字段</p> <p>级别的一个列式存储映射,它减轻了在排序和分组时对内存的依赖,而且大大提升了这个过程</p> <p>的性能,当然它也会耗费一定的磁盘空间的性能,当然它也会耗费一定的磁盘空间。</p> </blockquote> <h1>分词系统</h1> <p>在Lucene中,索引和查询都是以词项为基本单位,词项是词条化的结果。在Lucene中,分词主要依靠Analyzer类解析实现。Lucene中提供的分词方法只要如下:</p> <h2>•StopAnalyzer(停用词分词器)</h2> <p>StopAnalyzer能过滤词汇中的特定字符串和词汇,并且完成大写转小写的功能。.</p> <h2>•StandardAnalyzer(标准分词器)</h2> <p>StandardAnalyzer根据空格和符号来完成分词,还可以完成数字、字母、E-mail地址、IP地址以及中文字符的分析处理,还可以支持过滤词表,用来代替StopAnalyzer能够实现的过滤功能。</p> <h2>•WhitespaceAnalyzer(空格分词)</h2> <p>WhitespaceAnalyzer使用空格作为间隔符的词汇分割分词器。处理词汇单元的时候,以空格字符作为分割符号。分词器不做词汇过滤,也不进行小写字符转换。实际中可以用来支持特定环境下的西文符号的处理。由于不完成单词过滤和小写字符转换功能,也不需要过滤词库支持。词汇分割策略上简单使用非英文字符作为分割符,不需要分词词库支持。</p> <h2>•SimpleAnalyzer(简单分词)</h2> <p>SimpleAnalyzer具备基本西文字符词汇分析的分词器,处理词汇单元时,以非字母字符作为分割符号。分词器不能做词汇的过滤,只进行词汇的分析和分割。输出的词汇单元完成小写字符转换,去掉标点符号等分割符。在全文检索系统开发中,通常用来支持西文符号的处理,不支持中文。由于不完成单词过滤功能,所以不需要过滤词库支持。词汇分割策略上简单使用非英文字符作为分割符,不需要分词词库的支持。</p> <h2>•CJKAnalyzer(二分法分词)</h2> <p>内部调用CJKTokenizer分词器,对中文进行分词,同时使用StopFilter过滤器完成过滤功能,可以实现中文的多元切分和停用词过滤。</p> <h2>•KeywordAnalyzer(关键词分词)</h2> <p>把整个输入作为一个单独词汇单元,方便特殊类型的文本进行索引和检索。针对邮政编码、地址等文本信息使用关键词分词器进行索引项建立非常方便。</p> <blockquote> <p>各种分词器的分词效果可以参考:<a href="https://github.com/839284824/practice-study/tree/master/src/main/java/middle/lucene">https://github.com/839284824/practice-study/tree/master/src/main/java/middle/lucene</a></p> </blockquote> <h1>创建索引</h1> <p>Lucene索引文档要依靠一个IndexWriter对象,创建IndexWriter需要提供两个参数,一个是IndexWriterConfig对象,该对象可以设置创建索引使用哪种分词器,另一个是索引的保存路径。 IndexWriter对象的addDocument()方法用于添加文档,该方法的参数为Document对象。IndexWriter对象一次可以添加多个文档,最后调用commit()方法生成索引</p> <blockquote> <p>代码参考:<a href="https://github.com/839284824/practice-study/tree/master/src/main/java/middle/lucene/index">https://github.com/839284824/practice-study/tree/master/src/main/java/middle/lucene/index</a></p> </blockquote> <h1>删除索引</h1> <p>删除与更新和新增一样,也是通过IndexWriter*对象来操作的,IndexWrite对象的deleteDocuments()方法用于实现索引的删除,updateDocument()方法用于实现索引的更新。</p> <blockquote> <p>directory=FSDirectory.open(indexPath) IndexWriterindexWriter=new ndexWriter(directoryicw); indexWriter.deleteDocuments(newTerm(field,key)); indexWriter.commit(); indexWriter.close(); System,out.printIn(&quot;删除完成!n);`</p> </blockquote> <p>除此之外,IndexWriter还提供了以下方法: •DeleteDocuments(Queryquery):根据Query条件来删除单个或多个Document。 •DeleteDocuments(Query[]queries):根据Query条件来删除单个或多个Document •DeleteDocuments(Termterm):根据Term来删除单个或多个Document •DeleteDocuments(Term[]terms):根据Term来删除单个或多个Document。 •DeleteAll():删除所有的Document。 使用IndexWriter进行Document删除操作时,文档并不会立即被删除,而是把这个删除动作缓存起来,当IndexWriter.CommitO或IndexWriter.CloseO时,删除操作才会被真正执行。</p> <h1>更新索引</h1> <p>索引更新操作实质上是先删除索引,再重新建立新的文档。</p> <blockquote> <p>indexWriter.updateDocument(newTerm(&quot;title&quot;,&quot;doc&quot;,&quot;北大&quot;)) indexWriter.commit(); indexWriter.close()`</p> </blockquote> <p>IndexWriter对象和Document对象,通过updateDocument()方法完成更新操作。Term对象用于定位文档,查找title中含有“北大”的文档,然后用新的文档替换原文档,这样就完成了索引的更新操作。</p> <h1>索引的查询</h1> <p>文档索引完成以后就可以对其进行搜索,当用户输入一个关键字,搜索引擎接收到后,并不是立刻就将它放入后台开始进行关键字的检索,而应当首先对这个关键字进行一定的分析和处理,使之成为一种后台可以理解的形式,只有这样,才能提高检索的效率,同时检索出更加有效的结果。</p> <h2>QueryParser</h2> <p>在Lucene中,处理用户输入的查询关键词其实就是构建Query对象的过程。Lucene搜索文档需要实例化一个IndexSearcher对象,IndexSearcher对象的search()方法完成搜索过程,Query对象作为search()方法的参数。搜索结果会保存在一个TopDocs类型的文档集合中,遍历TopDocs集合输出文档信息。</p> <blockquote> <p>IndexSearchersearcher=new工ndexSearcher(reader) AnalyzeranalyzerQueryParserparser=newQueryParser(field,analyzer) parser.setDefaultOperator(Operator.AND); Queryquery=parser.parse(&quot;农村学生&quot;);//查询关键词 TopDocstopDocs=searcher.search(query,10);</p> </blockquote> <p>QueryParser实际上就是一个解析用户输入的工具,可以通过扫描用户输入的字符串生成Query对象。当使用QueryParser构建用户Query时,要搜索的field和analyzer对象作为参数传入QueryParser类,告诉QueryParser在哪个字段内查找该关键字信息以及搜索时使用什么样的分词器。 QueryParser可以搜索单个字段,MultiFieldQueryParser则可以查询多个字段。 <code>String[]fields={"title",’’content"}</code> <code>Analyzeranalyzer=newIKAnalyzer6x(true)</code> <code>MultiFieldQueryParserparser=newMultiFieldQueryParser(fields,analyzer)</code></p> <h2>词项搜索(TermQuery)</h2> <p>TermQuery是最简单也是最常用的Query。TermQuery可以理解成为“词项搜索”,在搜索引擎中最基本的搜索就是在索引中搜索某一词条,而TermQuery就是用来完成这项工作的。 要使用TermQuery进行搜索首先需要构造一个Term对象 <code>Termterm=newTerm("title","美国)</code> 然后使用Term对象为参数来构造一个TermQuery对象 <code>QuerytermQuery=newTermQuery(term)</code></p> <h2>布尔搜索(BooleanQuery)</h2> <p>BooleanQuery也是实际开发过程中经常使用的一种Query查询,它其实是一个组合的Query,在使用时可以把各种Query对象添加进去并标明它们之间的逻辑关系。</p> <h2>范围搜索(RangeQuery)</h2> <p>RangeQuery表示在某范围内的搜索条件,实现从一个开始词条到一个结束词条的搜索功能,在查询时“开始词条”和“结束词条”可以包含在内也可以不被包含在内。 如:查询回复数在500-1000之间的文档 <code>QueryrangeQuery=IntPoint.newRangeQuery("reply",500,1000)</code></p> <h2>前缀搜索(PrefixQuery)</h2> <p>PrefkQuery就是使用前缀来进行查找的。通常情况下,首先定义一个词条Term。该词条包含要查找的字段名以及关键字的前缀,然后通过该词条构造一个PrefixQuery对象,就可以进行前缀查找了。查询包含以“学”开头的词项的文档,构造Query对象的代码如下: <code>Termterm=newTerm("title","学”)</code> <code>QueryprefixQuery=newPrefixQuery(term)</code></p> <h2>多关键字搜索(PhraseQuery)</h2> <p><code>PhraseQuery.Builderbuilder = new PhraseQuery.Builder();</code> <code>builder.add(newTerm("title","日本"),2);</code> <code>builder.add(newTerm("title","美国,’),3);</code> <code>PhraseQueryphraseQuery=builder.build();</code></p> <h2>模糊搜索(FuzzyQuery)</h2> <p><code>Term term =new Term("title","Tramp");</code> <code>FuzzyQuery fuzzyQuery = new FuzzyQuery(term);</code></p> <h2>通配符搜索(WildcardQuery)</h2> <p><code>WildcardQuery wildcardQuery = new WildcardQuery(newTerm(field,"学?")</code></p>

页面列表

ITEM_HTML