lucene 按照匹配度排序_Lucene搜索详解

上篇说了Lucene索引,建好了索引,后面就是需要查询搜索了。

1e8933e4eec202ee1743947f56ca81ad.png

Lucene搜索代码示例

public class SearchBaseFlow {    public static void main(String[] args) throws IOException, ParseException {    // 使用的分词器    Analyzer analyzer = new IKAnalyzer4Lucene7(true);    // 索引存储目录    Directory directory = FSDirectory.open(Paths.get("f:/test/indextest"));    // 索引读取器    IndexReader indexReader = DirectoryReader.open(directory);    // 索引搜索器    IndexSearcher indexSearcher = new IndexSearcher(indexReader);    // 要搜索的字段    String filedName = "name";    // 查询生成器(解析输入生成Query查询对象)    QueryParser parser = new QueryParser(filedName, analyzer);    // 通过parse解析输入(分词),生成query对象    Query query = parser.parse("Thinkpad");    // 搜索,得到TopN的结果(结果中有命中总数,topN的scoreDocs(评分文档(文档id,评分)))    TopDocs topDocs = indexSearcher.search(query, 10);   //前10条    //获得总命中数    System.out.println(topDocs.totalHits);    // 遍历topN结果的scoreDocs,取出文档id对应的文档信息    for (ScoreDoc sdoc : topDocs.scoreDocs) {        // 根据文档id取存储的文档        Document hitDoc = indexSearcher.doc(sdoc.doc);        // 取文档的字段        System.out.println(hitDoc.get(filedName));    }    // 使用完毕,关闭、释放资源    indexReader.close();    directory.close();    }}

核心API图示:

a4e6f27dbf3880ae9baa7877dc137172.png

IndexReader 索引读取器

Open一个读取器,读取的是该时刻点的索引视图。如果后续索引发生改变,需重新open一个读取器。获得索引读取器的方式:

  • DirectoryReader.open(IndexWriter indexWriter);优先使用
  • DirectoryReader.open(Directory)
  • DirectoryReader.openIfChanged(DirectoryReader);共享当前reader资源重新打开一个(当索引变化时)

IndexReader分为两类:

bc498806adc8a5d5b62457755357e432.png
  • 叶子读取器:支持获取stored fields, doc values, terms(词项), and postings (词项对应的文档)
  • 复合读取器,多个读取器的复合。只可直接用它获取stored fields 。在内部通过CompositeReader.getSequentialSubReaders 得到里面的叶子读取器来获取其他数据。

DirectoryReader 是 复合读取器,IndexReader是线程安全的。

IndexSearcher 索引搜索器

应用通过调用它的search(Query,int)重载方法在一个IndexReader上实现搜索。出于性能的考虑,请使用一个IndexSearcher实例,除非索引发生变化。如索引更新了则通过DirectoryReader.openIfChanged(DirectoryReader) 取得新的读取器,再创建新的搜索器。IndexSearcher是线程安全的。

TopDocs 搜索命中的结果集 (Top-N)

569ee9ad6c3db3a80aa36bfecb210cb5.png

TopFieldDocs 按字段排序的搜索命中结果集

7bb73b95bc7dfa76f86f3d7a6e5607ea.png

ScoreDoc

f1733d70b543a144a411dfcd980aac12.png

Query 查询的表示。

它的可实例化子类有:

  • TermQuery
  • BooleanQuery
  • WildcardQuery
  • PhraseQuery
  • PrefixQuery
  • MultiPhraseQuery
  • FuzzyQuery
  • RegexpQuery
  • TermRangeQuery
  • PointRangeQuery
  • ConstantScoreQuery
  • DisjunctionMaxQuery
  • MatchAllDocsQuery

它们是lucene中的基本查询。我们可以用它们来创建查询。

0d96fa7f20ff4fd3fae2eda1fd84424f.png

TermQuery 词项查询

TermQuery tq = new TermQuery(new Term("fieldName", "term"));

词项查询,最基本、最常用的查询。用来查询指定字段包含指定词项的文档。

TermQuery tq = new TermQuery(new Term(“name", “thinkpad"));

BooleanQuery 布尔查询

搜索的条件往往是多个的,如要查询名称包含“电脑” 或 “thinkpad”的商品,就需要两个词项查询做或合并。布尔查询就是用来组合多个子查询的。每个子查询称为布尔字句 BooleanClause,布尔字句自身也可以是组合的。

组合关系支持如下四种:

  • Occur.SHOULD 或
  • Occur.MUST 且
  • Occur.MUST_NOT 且非
  • Occur.FILTER 同 MUST,但该字句不参与评分

布尔查询默认的最大字句数为1024,在将通配符查询这样的查询rewriter为布尔查询时,往往会产生很多的字句,可能抛出TooManyClauses 异常。可通过BooleanQuery.setMaxClauseCount(int)设置最大字句数。

BooleanQuery 布尔查询示例

// 布尔查询Query query1 = new TermQuery(new Term(filedName, "thinkpad"));Query query2 = new TermQuery(new Term("simpleIntro", "英特尔"));BooleanQuery.Builder booleanQueryBuilder = new BooleanQuery.Builder();booleanQueryBuilder.add(query1, Occur.SHOULD);booleanQueryBuilder.add(query2, Occur.MUST);BooleanQuery booleanQuery = booleanQueryBuilder.build();// 可像下一行这样写// BooleanQuery booleanQuery = new BooleanQuery.Builder()//     .add(query1, Occur.SHOULD).add(query2, Occur.MUST).build();

PhraseQuery 短语查询

最常用的查询,匹配特点序列的多个词项。PhraserQuery使用一个位置移动因子(slop)来决定任意两个词项的位置可最大移动多少个位置来进行匹配,默认为0。有两种方式来构建对象:

40723d54f8ba7d931ffff97e72179685.png
7831802db05350394b33f67df3d2368a.png

所有加入的词项都匹配才算匹配(即使是你在同一位置加入多个词项)。如果需要在同一位置匹配多个同义词中的一个,适合用MultiPhraseQuery。

PhraseQuery 短语查询示例

PhraseQuery phraseQuery1 = new PhraseQuery("name", "thinkpad",    "carbon");PhraseQuery phraseQuery2 = new PhraseQuery(1, "name", "thinkpad",    "carbon");PhraseQuery phraseQuery3 = new PhraseQuery("name", "笔记本电脑", "联想");PhraseQuery phraseQuery4 = new PhraseQuery.Builder()    .add(new Term("name", "笔记本电脑"), 4)    .add(new Term("name", "联想"), 5).build();// 这两句等同PhraseQuery phraseQuery5 = new PhraseQuery.Builder()    .add(new Term("name", "笔记本电脑"), 0)    .add(new Term("name", "联想"), 1).build();

PhraseQuery slop 移动因子说明

String name = "ThinkPad X1 Carbon 20KH0009CD/25CD 超极本轻薄笔记本电脑联想";

1、如果想用 “thinkpad carbon” 来匹配 name。因中间有 x1,则需要将thinkpad 向右移动1个位置。

2、如果想用 “carbon thinkpad” 来匹配 name。因中间有 x1,则需要将carbon 向右移动3个位置。

// String name = "ThinkPad X1 Carbon 20KH0009CD/25CD 超极本轻薄笔记本电脑联想";

// PhraseQuery 短语查询

PhraseQuery phraseQuery2 = new PhraseQuery(1, "name", "thinkpad","carbon");

// slop示例

PhraseQuery phraseQuery2Slop = new PhraseQuery(3, "name", "carbon", "thinkpad");

PhraseQuery phraseQuery3 = new PhraseQuery("name", "笔记本电脑", "联想");

// slop示例

PhraseQuery phraseQuery3Slop = new PhraseQuery(2, "name", "联想","笔记本电脑");

MultiPhraseQuery 多重短语查询

短语查询的一种更通用的用法,支持同位置多个词的OR匹配。通过里面的Builder来构建MultiPhraseQuery:

9116ecacb66e80a730306a8f88968459.png

MultiPhraseQuery 多重短语查询示例

// 4 MultiPhraseQuery 多重短语查询Term[] terms = new Term[2];terms[0] = new Term("name", "笔记本");terms[1] = new Term("name", "笔记本电脑");Term t = new Term("name", "联想");MultiPhraseQuery multiPhraseQuery = new MultiPhraseQuery.Builder()    .add(terms).add(t).build();// 对比 PhraseQuery在同位置加入多个词 ,同位置的多个词都需匹配,所以查不出。PhraseQuery pquery = new PhraseQuery.Builder().add(terms[0], 0)    .add(terms[1], 0).add(t, 1).build();

SpanNearQuery 临近查询(跨度查询)

用于更复杂的短语查询,可以指定词间位置的最大间隔跨度。通过组合一系列的SpanQuery 实例来进行查询,可以指定是否按顺序匹配、slop、gap。

ba93f09c6796e61044020d9537948372.png

SpanNearQuery 临近查询示例

// SpanNearQuery 临近查询SpanTermQuery tq1 = new SpanTermQuery(new Term("name", "thinkpad"));SpanTermQuery tq2 = new SpanTermQuery(new Term("name", "carbon"));SpanNearQuery spanNearQuery = new SpanNearQuery(    new SpanQuery[] { tq1, tq2 }, 1, true);// SpanNearQuery 临近查询 gap slop 使用SpanNearQuery.Builder spanNearQueryBuilder = SpanNearQuery    .newOrderedNearQuery("name");spanNearQueryBuilder.addClause(tq1).addGap(0).setSlop(1)    .addClause(tq2);SpanNearQuery spanNearQuery5 = spanNearQueryBuilder.build();

TermRangeQuery 词项范围查询

用于查询包含某个范围内的词项的文档,如以字母开头a到c的词项。词项在反向索引中是排序的,只需指定的开始词项、结束词项,就可以查询该范围的词项。

如果是做数值的范围查询则用 PointRangeQuery 。

eaed3f840daf9dbbd4cab507e68003c0.png

参数说明:

field – 字段

lowerTerm – 下边界词

upperTerm – 上边界词

includeLower –是否包含下边界.

includeUpper – 是否包含上边界.

// TermRangeQuery 词项范围查询

TermRangeQuery termRangeQuery                 = TermRangeQuery.newStringRange("name",  "carbon", "张三", false, true);

PrefixQuery, WildcardQuery, RegexpQuery

  • PrefixQuery 前缀查询

查询包含以xxx为前缀的词项的文档,是通配符查询,如 app,实际是 app*

  • WildcardQuery 通配符查询*表示0个或多个字符,?表示1个字符,是转义符。通配符查询可能会比较慢,不可以通配符开头(那样就是所有词项了)
  • RegexpQuery 正则表达式查询词项。符合某正则表达式

PrefixQuery, WildcardQuery, RegexpQuery 示例

// PrefixQuery 前缀查询PrefixQuery prefixQuery = new PrefixQuery(new Term("name", "think"));// WildcardQuery 通配符查询WildcardQuery wildcardQuery = new WildcardQuery(    new Term("name", "think*"));// WildcardQuery 通配符查询WildcardQuery wildcardQuery2 = new WildcardQuery(    new Term("name", "厉害了???"));// RegexpQuery 正则表达式查询RegexpQuery regexpQuery = new RegexpQuery(new Term("name", "厉害.{4}"));

FuzzyQuery 模糊查询

简单地与索引词项进行相近匹配,允许最大2个不同字符。常用于拼写错误的容错:如把 “thinkpad” 拼成 “thinkppd”或 “thinkd”,使用FuzzyQuery 仍可搜索到正确的结果。

// FuzzyQuery 模糊查询FuzzyQuery fuzzyQuery = new FuzzyQuery(new Term("name", "thind"));FuzzyQuery fuzzyQuery2 = new FuzzyQuery(new Term("name", "thinkd"), 2);FuzzyQuery fuzzyQuery3 = new FuzzyQuery(new Term("name", "thinkpaddd"));FuzzyQuery fuzzyQuery4 = new FuzzyQuery(new Term("name", "thinkdaddd"));

数值查询

前提:查询的数值字段必须索引。通过 IntPoint, LongPoint, FloatPoint, or DoublePoint 中的方法构建对应的查询。以IntPoint为例:

160edff8b24b70ec4d15c94a43f17099.png
// 精确值查询Query exactQuery = IntPoint.newExactQuery("price", 1999900);// 数值范围查询Query pointRangeQuery = IntPoint.newRangeQuery("price", 499900,1000000);// 集合查询Query setQuery = IntPoint.newSetQuery("price", 1999900, 1000000,2000000);

QueryParser 查询解析生成器

Lucene QueryPaser包中提供了两类查询解析器

1.传统的解析器​

QueryParser

MultiFieldQueryParser

2.基于新的 flexible 框架的解析器

StandardQueryParser​

传统解析器-单默认字段 QueryParser:

QueryParser parser = new QueryParser("defaultFiled", analyzer);//parser.setPhraseSlop(2);Query query = parser.parse("query String");

示例:

// 使用的分词器Analyzer analyzer = new IKAnalyzer4Lucene7(true);// 要搜索的默认字段String defaultFiledName = "name";// 查询生成器(解析输入生成Query查询对象)QueryParser parser = new QueryParser(defaultFiledName, analyzer);// 通过parse解析输入,生成query对象Query query1 = parser.parse(        "(name:"联想笔记本电脑" OR simpleIntro:英特尔) AND type:电脑 AND price:999900");

传统解析器-多默认字段 MultiFieldQueryParser:

// 传统查询解析器-多默认字段String[] multiDefaultFields = { "name", "type", "simpleIntro" };MultiFieldQueryParser multiFieldQueryParser = new MultiFieldQueryParser(        multiDefaultFields, analyzer);// 设置默认的组合操作,默认是 ORmultiFieldQueryParser.setDefaultOperator(Operator.OR);Query query4 = multiFieldQueryParser.parse("笔记本电脑 AND price:1999900");

新解析框架的标准解析器:StandardQueryParser:

StandardQueryParser queryParserHelper = new StandardQueryParser(analyzer);// 设置默认字段// queryParserHelper.setMultiFields(CharSequence[] fields);// queryParserHelper.setPhraseSlop(8);// Query query = queryParserHelper.parse("a AND b", "defaultField");Query query5 = queryParserHelper.parse(    "("联想笔记本电脑" OR simpleIntro:英特尔) AND type:电脑 AND price:1999900","name");

使用查询解析器前需考虑三点:

  1. 查询字符串应是由人输入的,而不应是你编程产生。如果你为了用查询解析器,而在你的应用中编程产生查询字符串,不可取,更应该直接使用基本查询API;
  2. 未分词的字段,应直接使用基本查询API加入到查询中,而不应使用查询解析器;
  3. 对于普通文本字段,使用查询解析器,而其他值字段:如 时间、数值,则应使用基本查询API

查询描述规则语法(查询解析语法):

Term 词项:

单个词项的表示: 电脑

短语的表示: "联想笔记本电脑"

Field 字段:

字段名:

示例: name:“联想笔记本电脑” AND type:电脑

如果name是默认字段,则可写成: “联想笔记本电脑” AND type:电脑

如果查询串是:type:电脑 计算机 手机

注意:只有第一个是type的值,后两个则是使用默认字段。

查询描述规则语法(查询解析语法):

Term Modifiers 词项修饰符:

统配符:

? 单个字符

* 0个或多个字符

示例:te?t test* te*t

注意:通配符不可用在开头。

模糊查询,词后加 ~

示例: roam~

模糊查询最大支持两个不同字符。

示例: roam~1

正则表达式: /xxxx/

示例: /[mb]oat/

临近查询,短语后加 ~移动值

示例: "jakarta apache"~10

查询描述规则语法(查询解析语法):

Term Modifiers 词项修饰符:

范围查询:

mod_date:[20020101 TO 20030101] 包含边界值

title:{Aida TO Carmen} 不包含边界值

词项加权,使该词项的相关性更高,通过 ^数值来指定加权因子,默认加权因子值是1

示例:如要搜索包含 jakarta apache 的文章,jakarta更相关,则:

jakarta^4 apache

短语也可以: "jakarta apache"^4 "Apache Lucene"

查询描述规则语法(查询解析语法):

Boolean 操作符

Lucene支持的布尔操作: AND, “+”, OR, NOT ,"-"

OR

"jakarta apache" jakarta

=

"jakarta apache" OR jakarta

NOT 非

"jakarta apache" NOT "Apache Lucene“

注意:NOT不可单项使用:

NOT “Apache Lucene“ 不可

AND

"jakarta apache" AND "Apache Lucene"

+ 必须包含

+jakarta lucene

- 同NOT

"jakarta apache" -"Apache Lucene“

查询描述规则语法(查询解析语法):

组合 ()

字句组合

(jakarta OR apache) AND website

字段组合

title:(+return +"pink panther")

转义

对语法字符: + - && || ! ( ) { } [ ] ^ “ ~ * ? : / 进行转义。

如要查询包含 (1+1):2

(1+1):2

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值