Lucene的主要搜索API
一个简单的搜索应用主要包括索引和搜索两部分,在Lucene中,IndexSearcher类是用于对索引中文档进行搜索的核心类,它有几个重载的搜索方法,可以使用最常用的方法对特定的项进行搜索,一个项由一个字符串类型的域值和对应的域名构成。现将搜索相关API汇总如下
类 | 目的 |
---|---|
IndexSearcher | 搜索索引的核心类。所有搜索都通过IndexSearcher进行,它们会调用该类中重载的search方法 |
Query及其子类 | 封装某种查询类型的具体子类。Query实例将被传递给IndexSearcher的search方法 |
QueryParser | 将用户输入的可读的查询表达式处理成具体的Query对象 |
TopDocs | 保持由IndexSearcher.search()方法返回的具有较高评分的顶部文档 |
ScoreDoc | 提供对TopDocs中每条搜索结果的访问接口 |
IndexSearcher简介
首先看IndexSearcher的doc注释
Implements search over a single IndexReader.
Applications usually need only call the inherited search(Query, int) or search(Query, Filter, int) methods. For performance reasons, if your index is unchanging, you should share a single IndexSearcher instance across multiple searches instead of creating a new one per-search. If your index has changed and you wish to see the changes reflected in searching, you should use DirectoryReader.openIfChanged(DirectoryReader) to obtain a new reader and then create a new IndexSearcher from that. Also, for low-latency turnaround it’s best to use a near-real-time reader (DirectoryReader.open(IndexWriter)). Once you have a new IndexReader, it’s relatively cheap to create a new IndexSearcher from it.
NOTE: IndexSearcher instances are completely thread safe, meaning multiple threads can call any of its methods, concurrently. If your application requires external synchronization, you should not synchronize on the IndexSearcher instance; use your own (non-Lucene) objects instead.
从这段说明中得出如下几点
- 提供了对单个IndexReader的查询实现
- 通常应用程序只需要调用search(Query, int)或者search(Query, Filter, int)方法
- 如果你的索引不变,在多个搜索中应该采用共享一个IndexSearcher实例的方式
- 如果索引有变动,并且你希望在搜索中有所体现,那么应该使用DirectoryReader.openIfChanged(DirectoryReader)来获取新的reader,然后通过这个reader创建一个新的IndexSearcher
- 为了低延迟查询,最好使用近实时搜索(NRT),此时构建IndexSearcher需要使用DirectoryReader.open(IndexWriter),一旦你获取一个新的IndexReader,再去创建一个IndexSearcher所付出的代价要小的多
- IndexSearcher实例是完全线程安全的,这意味着多个线程可以并发调用任何方法。如果需要外部同步,无需对IndexSearcher实例进行同步
Lucene的多样化查询
- 通过项进行搜索-TermQuery类
- 在指定的项范围内搜索-TermRangeQuery类
- 通过字符串搜索-PrefixQuery类
- 组合查询-BooleanQuery类
- 通过短语搜索-PhraseQuery类
- 通配符查询-WildcardQuery类
- 搜索类似项-FuzzyQuery类
- 匹配所有文档-MatchAllDocsQuery类
- 不匹配文档-MatchNoDocsQuery类
- 解析查询表达式-QueryParser类
- 多短语查询-MultiPhraseQuery类
一些简单的查询示例如下
|
|
有关QueryParser的详细语法请参考这里。
Lucene的高级搜索技术
Lucene包含了一个建立在SpanQuery类基础上的整套查询体系,大致反映了Lucene的Query类体系。SpanQuery是指域中的起始语汇单元和终止语汇单元的位置。SpanQuery有一些常用的子类,如下所示
SpanQuery类型 | 描述 |
---|---|
FieldMaskingSpanQuery | 用于在多个域之间查询,即把另一个域看作某个域,从而看起来就像在同一个域里查询,因为 Lucene 默认某个条件只能作用在单个域上,不支持跨域查询,只能在同一个域里查询,所以有了 FieldMaskingSpanQuery |
SpanTermQuery | 和其它跨度查询类型结合使用,单独使用时相当于 TermQuery,唯一的区别就是使用 SpanTermQuery 可以得到 Term 的 Span 跨度信息 |
SpanNearQuery | 用来匹配两个 Term 之间的跨度的,即一个 Term 经过几个跨度可以到达另一个 Term,slop 为跨度因子,用来限制两个 Term 之间的最大跨度。还有一个 inOrder 参数,它用来设置是否允许进行倒序跨度,什么意思?即 TermA 到 TermB 不一定是从左到右去匹配也可以从右到左,而从右到左就是倒序,inOrder 为 true 即表示 order(顺序)很重要不能倒序去匹配必须正向去匹配,false 则反之。注意停用词不在 slop 统计范围内 |
SpanFirstQuery | 表示对出现在一个域中的[0, n]范围内的 term 项进行的匹配查询,关键是n指定了查询的 term 出现范围的上限 |
SpanContainingQuery | 返回在另一个范围内的查询匹配结果,big 和 little 的子句可以是任何 span 类型查询。在包含 little 匹配中从 big 匹配跨度返回。例如“a beautiful and boring world”,big 查询是 SpanNearQuery(SpanTermQuery(“beautiful”), SpanTermQuery(“world”)).setSlop(2),而 little 查询是 SpanTermQuery(“boring”),则该 Doc 命中,并从 big 匹配跨度返回,即 big 优先级高 |
SpanWithinQuery | 与 SpanContainingQuery 类似,只不过是从 little 匹配跨度返回,换句话说,SpanContainingQuery 是 big 查询优先级更高,而 SpanWithinQuery 是 little 查询优先级更高。也许英文说的更清晰,In clear, the SpanContainingQuery will keep matches that contain another Spans, while the SpanWithinQuery will keep matches that are contained within another Spans. |
SpanNotQuery | 使用场景是当使用 SpanNearQuery 时,如果两个 Term 从 TermA 到 TermB 有多种情况,即可能出现 TermA 或者 TermB 在索引中重复出现,则可能有多种情况,SpanNotQuery 就是用来限制 TermA 和 TermB 之间不存在 TermC,从而排除一些情况,实现更精确的控制 |
SpanOrQuery | 这个查询会嵌套一些子查询,子查询之间的逻辑关系为或 |
使用示例
|
|
禁用模糊查询和通配符查询
有时候,可能不希望用户使用模糊查询或者通配符查询,这时候可以通过实现自己的QueryParser达到禁用的目的。例如
|
|
多索引的搜索合并方法
如果存在多索引文件,需要如何搜索并合并搜索结果呢?此时需要使用MultiReader,通过该类的构造函数MultiReader(IndexReader… subReaders)可以接收多个IndexReader实例,而一个IndexReader实例对应一个索引文件目录,之后用该MultiReader实例初始化IndexSearcher。
注意,IndexReader实例是完全线程安全的,多线程可以调用其任何方法,而无需对IndexReader实例进行同步。如果你的应用需要外部同步操作,对你自己的对象进行同步,而不是Lucene的。
|
|
输出结果如下
|
|