网站首页 > 开源技术 正文
上篇说了Lucene索引,建好了索引,后面就是需要查询搜索了。
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图示:
IndexReader 索引读取器
Open一个读取器,读取的是该时刻点的索引视图。如果后续索引发生改变,需重新open一个读取器。获得索引读取器的方式:
- DirectoryReader.open(IndexWriter indexWriter);优先使用
- DirectoryReader.open(Directory)
- DirectoryReader.openIfChanged(DirectoryReader);共享当前reader资源重新打开一个(当索引变化时)
IndexReader分为两类:
- 叶子读取器:支持获取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)
TopFieldDocs 按字段排序的搜索命中结果集
ScoreDoc
Query 查询的表示。
它的可实例化子类有:
- TermQuery
- BooleanQuery
- WildcardQuery
- PhraseQuery
- PrefixQuery
- MultiPhraseQuery
- FuzzyQuery
- RegexpQuery
- TermRangeQuery
- PointRangeQuery
- ConstantScoreQuery
- DisjunctionMaxQuery
- MatchAllDocsQuery
它们是lucene中的基本查询。我们可以用它们来创建查询。
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。有两种方式来构建对象:
所有加入的词项都匹配才算匹配(即使是你在同一位置加入多个词项)。如果需要在同一位置匹配多个同义词中的一个,适合用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:
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。
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 。
参数说明:
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为例:
// 精确值查询
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);
// 设置默认的组合操作,默认是 OR
multiFieldQueryParser.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");
使用查询解析器前需考虑三点:
- 查询字符串应是由人输入的,而不应是你编程产生。如果你为了用查询解析器,而在你的应用中编程产生查询字符串,不可取,更应该直接使用基本查询API;
- 未分词的字段,应直接使用基本查询API加入到查询中,而不应使用查询解析器;
- 对于普通文本字段,使用查询解析器,而其他值字段:如 时间、数值,则应使用基本查询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
猜你喜欢
- 2024-10-23 ES 基本知识(es基本介绍)
- 2024-10-23 maven打包jar包时如何打包本地jar文件
- 2024-10-23 记录自己搭建solr配置中文分词的过程供大家参考
- 2024-10-23 springboot整合websocket、solr(springboot整合rocketMQ)
- 2024-10-23 ElasticSearch安装ik分词插件(elasticsearch 安装ik分词器)
- 2024-10-23 12K的码农怎样蜕变为30k的架构师?找准方向,拒绝迷茫
- 2024-10-23 Lucene就是这么简单(好儿子今天妈妈就是你的女人了)
- 2024-10-23 5分钟带你了解Lucene全文索引(lucene索引原理)
- 2024-10-23 在.net core中进行中文分词方法(.net core hangfire)
- 2024-10-23 Elasticsearch-通过外网访问加入kibana,head「002」
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- jdk (81)
- putty (66)
- rufus (78)
- 内网穿透 (89)
- okhttp (70)
- powertoys (74)
- windowsterminal (81)
- netcat (65)
- ghostscript (65)
- veracrypt (65)
- asp.netcore (70)
- wrk (67)
- aspose.words (80)
- itk (80)
- ajaxfileupload.js (66)
- sqlhelper (67)
- express.js (67)
- phpmailer (67)
- xjar (70)
- redisclient (78)
- wakeonlan (66)
- tinygo (85)
- startbbs (72)
- webftp (82)
- vsvim (79)
本文暂时没有评论,来添加一个吧(●'◡'●)