Elasticsearch深入搜索之全文搜索及JavaAPI使用
一、基于词项与基于全文
所有查询会或多或少的执行相关度计算,但不是所有查询都有分析阶段。 和一些特殊的完全不会对文本进行操作的查询(如 bool
或 function_score
)不同,文本查询可以划分成两大家族:
- 1.基于词项的查询
-
如
term
或fuzzy
这样的底层查询不需要分析阶段,它们对单个词项进行操作。用term
查询词项Foo
只要在倒排索引中查找 准确词项 ,并且用 TF/IDF 算法为每个包含该词项的文档计算相关度评分_score
。记住
term
查询只对倒排索引的词项精确匹配,这点很重要,它不会对词的多样性进行处理(如,foo
或FOO
)。这里,无须考虑词项是如何存入索引的。如果是将["Foo","Bar"]
索引存入一个不分析的(not_analyzed
)包含精确值的字段,或者将Foo Bar
索引到一个带有whitespace
空格分析器的字段,两者的结果都会是在倒排索引中有Foo
和Bar
这两个词。 - 2.基于全文的查询
-
像
match
或query_string
这样的查询是高层查询,它们了解字段映射的信息:- 如果查询
日期(date)
或整数(integer)
字段,它们会将查询字符串分别作为日期或整数对待。 - 如果查询一个(
not_analyzed
)未分析的精确值字符串字段, 它们会将整个查询字符串作为单个词项对待。 - 但如果要查询一个(
analyzed
)已分析的全文字段, 它们会先将查询字符串传递到一个合适的分析器,然后生成一个供查询的词项列表。
一旦组成了词项列表,这个查询会对每个词项逐一执行底层的查询,再将结果合并,然后为每个文档生成一个最终的相关度评分。
我们将会在随后章节中详细讨论这个过程。
- 如果查询
我们很少直接使用基于词项的搜索,通常情况下都是对全文进行查询,而非单个词项,这只需要简单的执行一个高层全文查询(进而在高层查询内部会以基于词项的底层查询完成搜索)。
当我们想要查询一个具有精确值的 not_analyzed
未分析字段之前, 需要考虑,是否真的采用评分查询,或者非评分查询会更好。
单词项查询通常可以用是、非这种二元问题表示,所以更适合用过滤, 而且这样做可以有效利用缓存:
GET /_search
{
"query": {
"constant_score": {
"filter": {
"term": { "gender": "female" }
}
}
}
}
二、匹配查询
匹配查询 match
是个 核心 查询。无论需要查询什么字段, match
查询都应该会是首选的查询方式。 它是一个高级 全文查询 ,这表示它既能处理全文字段,又能处理精确字段。
1.先添加索引数据
DELETE /my_index PUT /my_index
{ "settings": { "number_of_shards": 1 }} //设置主分片个数,默认是5个,主分片副本数(number_of_replicas)默认1个 POST /my_index/my_type/_bulk
{ "index": { "_id": 1 }}
{ "title": "The quick brown fox" }
{ "index": { "_id": 2 }}
{ "title": "The quick brown fox jumps over the lazy dog" }
{ "index": { "_id": 3 }}
{ "title": "The quick brown fox jumps over the quick dog" }
{ "index": { "_id": 4 }}
{ "title": "Brown fox brown dog" }
GET /my_index/my_type/_search
{
"query": {
"match": {
"title": "QUICK!"
}
}
} 查询结果:
{
"took": 19,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 3,
"max_score": 0.42327404,
"hits": [
{
"_index": "my_index",
"_type": "my_type",
"_id": "1",
"_score": 0.42327404,
"_source": {
"title": "The quick brown fox"
}
},
{
"_index": "my_index",
"_type": "my_type",
"_id": "3",
"_score": 0.42211798,
"_source": {
"title": "The quick brown fox jumps over the quick dog"
}
},
{
"_index": "my_index",
"_type": "my_type",
"_id": "2",
"_score": 0.2887157,
"_source": {
"title": "The quick brown fox jumps over the lazy dog"
}
}
]
}
}
Elasticsearch 执行上面这个 match
查询的步骤是:
检查字段类型 。
标题
title
字段是一个string
类型(analyzed
)已分析的全文字段,这意味着查询字符串本身也应该被分析。分析查询字符串 。
将查询的字符串
QUICK!
传入标准分析器中,输出的结果是单个项quick
。因为只有一个单词项,所以match
查询执行的是单个底层term
查询。查找匹配文档 。
用
term
查询在倒排索引中查找quick
然后获取一组包含该项的文档,本例的结果是文档:1、2 和 3 。为每个文档评分 。
用
term
查询计算每个文档相关度评分_score
,这是种将 词频(term frequency,即词quick
在相关文档的title
字段中出现的频率)和反向文档频率(inverse document frequency,即词quick
在所有文档的title
字段中出现的频率),以及字段的长度(即字段越短相关度越高)相结合的计算方式
结果分析:1.文档 1 最相关,因为它的 title
字段更短,即 quick
占据内容的一大部分。
2.文档 3 比 文档 2 更具相关性,因为在文档 2 中 quick
出现了两次。
单词匹配JavaAPI应用
MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("title","QUICK!");
三、多词查询
1.
GET /my_index/my_type/_search
{
"query": {
"match": {
"title": "BROWN DOG!"
}
}
} 查询结果:
{
"took": 5,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 4,
"max_score": 0.58571666,
"hits": [
{
"_index": "my_index",
"_type": "my_type",
"_id": "4",
"_score": 0.58571666,
"_source": {
"title": "Brown fox brown dog"
}
},
{
"_index": "my_index",
"_type": "my_type",
"_id": "2",
"_score": 0.37400126,
"_source": {
"title": "The quick brown fox jumps over the lazy dog"
}
},
{
"_index": "my_index",
"_type": "my_type",
"_id": "3",
"_score": 0.37400126,
"_source": {
"title": "The quick brown fox jumps over the quick dog"
}
},
{
"_index": "my_index",
"_type": "my_type",
"_id": "1",
"_score": 0.12503365,
"_source": {
"title": "The quick brown fox"
}
}
]
}
}
查询结果分析:
因为 match
查询必须查找两个词( ["brown","dog"]
),它在内部实际上先执行两次 term
查询,然后将两次查询的结果合并作为最终结果输出。为了做到这点,它将两个 term
查询包入一个 bool
查询中。
注:即任何文档只要 title
字段里包含 指定词项中的至少一个词 就能匹配,被匹配的词项越多,文档就越相关。
2.提高精度
GET /my_index/my_type/_search
{
"query": {
"match": {
"title": {
"query": "BROWN DOG!",
"operator": "and"
}
}
}
} 查询结果:
{
"took": 1,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 3,
"max_score": 0.58571666,
"hits": [
{
"_index": "my_index",
"_type": "my_type",
"_id": "4",
"_score": 0.58571666,
"_source": {
"title": "Brown fox brown dog"
}
},
{
"_index": "my_index",
"_type": "my_type",
"_id": "2",
"_score": 0.37400126,
"_source": {
"title": "The quick brown fox jumps over the lazy dog"
}
},
{
"_index": "my_index",
"_type": "my_type",
"_id": "3",
"_score": 0.37400126,
"_source": {
"title": "The quick brown fox jumps over the quick dog"
}
}
]
}
}
MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("title","BROWN DOG!").operator(Operator.AND); //AND必须全部包含,OR至少包含一个
使用 operator
操作符参数查询,and参数指必须包含查询的所有词才能被查询出来。
3.控制精度
GET /my_index/my_type/_search
{
"query": {
"match": {
"title": {
"query": "quick brown dog",
"minimum_should_match": "75%"
}
}
}
} 查询结果:
{
"took": 1,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 4,
"max_score": 0.7961193,
"hits": [
{
"_index": "my_index",
"_type": "my_type",
"_id": "3",
"_score": 0.7961193,
"_source": {
"title": "The quick brown fox jumps over the quick dog"
}
},
{
"_index": "my_index",
"_type": "my_type",
"_id": "2",
"_score": 0.662717,
"_source": {
"title": "The quick brown fox jumps over the lazy dog"
}
},
{
"_index": "my_index",
"_type": "my_type",
"_id": "4",
"_score": 0.58571666,
"_source": {
"title": "Brown fox brown dog"
}
},
{
"_index": "my_index",
"_type": "my_type",
"_id": "1",
"_score": 0.54830766,
"_source": {
"title": "The quick brown fox"
}
}
]
}
}
MatchQueryBuilder matchQueryBuilder1 = QueryBuilders.matchQuery("title","quick brown dog").minimumShouldMatch("75%");
查询结果分析:
当给定百分比的时候, minimum_should_match
会做合适的事情:在之前三词项的示例中, 75%
会自动被截断成 66.6%
,即三个里面两个词。无论这个值设置成什么,至少包含一个词项的文档才会被认为是匹配的。
参数 minimum_should_match
的设置非常灵活,可以根据用户输入词项的数目应用不同的规则。完整的信息参考文档https://www.elastic.co/guide/en/elasticsearch/reference/5.6/query-dsl-minimum-should-match.html#query-dsl-minimum-should-match
四、组合查询
1.在组合过滤中,我们讨论过如何使用 bool
过滤器通过 and
、 or
和 not
逻辑组合将多个过滤器进行组合。在查询中, bool
查询有类似的功能,只有一个重要的区别。
过滤器做二元判断:文档是否应该出现在结果中?但查询更精妙,它除了决定一个文档是否应该被包括在结果中,还会计算文档的 相关程度 。
与过滤器一样, bool
查询也可以接受 must
、 must_not
和 should
参数下的多个查询语句。比如:
GET /my_index/my_type/_search
{
"query": {
"bool": {
"must": { "match": { "title": "quick" }},
"must_not": { "match": { "title": "lazy" }},
"should": [
{ "match": { "title": "brown" }},
{ "match": { "title": "dog" }}
]
}
}
} 查询结果:
{
"took": 19,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 2,
"max_score": 0.7961192,
"hits": [
{
"_index": "my_index",
"_type": "my_type",
"_id": "3",
"_score": 0.7961192,
"_source": {
"title": "The quick brown fox jumps over the quick dog"
}
},
{
"_index": "my_index",
"_type": "my_type",
"_id": "1",
"_score": 0.54830766,
"_source": {
"title": "The quick brown fox"
}
}
]
}
}
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
boolQueryBuilder.must(QueryBuilders.matchQuery("title","quick"));
boolQueryBuilder.mustNot(QueryBuilders.matchQuery("title","lazy"));
boolQueryBuilder.should(QueryBuilders.matchQuery("title","brown"));
boolQueryBuilder.should(QueryBuilders.matchQuery("title","dog"));
查询结果分析:
查询结果返回 title
字段包含词项 quick
但不包含 lazy
的任意文档。目前为止,这与 bool
过滤器的工作方式非常相似。
区别就在于两个 should
语句,也就是说:一个文档不必包含 brown
或 dog
这两个词项,但如果一旦包含,我们就认为它们 更相关
评分计算:
bool
查询会为每个文档计算相关度评分 _score
, 再将所有匹配的 must
和 should
语句的分数 _score
求和,最后除以 must
和 should
语句的总数。
must_not
语句不会影响评分; 它的作用只是将不相关的文档排除。
2.控制精度
GET /my_index/my_type/_search
{
"query": {
"bool": {
"should": [
{ "match": { "title": "brown" }},
{ "match": { "title": "fox" }},
{ "match": { "title": "dog" }}
],
"minimum_should_match": 2
}
}
} 查询结果:
{
"took": 1,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 4,
"max_score": 0.71075034,
"hits": [
{
"_index": "my_index",
"_type": "my_type",
"_id": "4",
"_score": 0.71075034,
"_source": {
"title": "Brown fox brown dog"
}
},
{
"_index": "my_index",
"_type": "my_type",
"_id": "2",
"_score": 0.45928687,
"_source": {
"title": "The quick brown fox jumps over the lazy dog"
}
},
{
"_index": "my_index",
"_type": "my_type",
"_id": "3",
"_score": 0.45928687,
"_source": {
"title": "The quick brown fox jumps over the quick dog"
}
},
{
"_index": "my_index",
"_type": "my_type",
"_id": "1",
"_score": 0.2500673,
"_source": {
"title": "The quick brown fox"
}
}
]
}
}
查询结果分析:
通过 minimum_should_match
参数控制需要匹配的 should
语句的数量, 它既可以是一个绝对的数字,又可以是个百分比;
五、如何使用布尔匹配
1.多词match查询只是简单地将生成的 term
查询包裹 在一个 bool
查询中。如果使用默认的 or
操作符,每个 term
查询都被当作 should
语句,这样就要求必须至少匹配一条语句。以下两个查询是等价的:
{
"match": { "title": "brown fox"}
}
{
"bool": {
"should": [
{ "term": { "title": "brown" }},
{ "term": { "title": "fox" }}
]
}
}
2.如果使用 and
操作符,所有的 term
查询都被当作 must
语句,所以 所有(all) 语句都必须匹配。以下两个查询是等价的:
{
"match": {
"title": {
"query": "brown fox",
"operator": "and"
}
}
}
{
"bool": {
"must": [
{ "term": { "title": "brown" }},
{ "term": { "title": "fox" }}
]
}
}
3.如果指定参数 minimum_should_match
,它可以通过 bool
查询直接传递,使以下两个查询等价:
{
"match": {
"title": {
"query": "quick brown fox",
"minimum_should_match": "75%"
}
}
}
{
"bool": {
"should": [
{ "term": { "title": "brown" }},
{ "term": { "title": "fox" }},
{ "term": { "title": "quick" }}
],
"minimum_should_match": 2
}
}
六、查询语句提升权重
1.一个简单的 bool
查询 允许我们写出如下这种非常复杂的逻辑:
GET /_search
{
"query": {
"bool": {
"must": {
"match": {
"content": {
"query": "full text search",
"operator": "and"
}
}
},
"should": [
{ "match": { "content": "Elasticsearch" }},
{ "match": { "content": "Lucene" }}
]
}
}
}
注:(1)content
字段必须包含 full
、 text
和 search
所有三个词。 (2)如果content字段包含Elasticsearch或者Lucene,文档会获得更高的评分_scorce.
我们可以通过指定 boost
来控制任何查询语句的相对的权重, boost
的默认值为 1
,大于 1
会提升一个语句的相对权重。所以下面重写之前的查询
GET /_search
{
"query": {
"bool": {
"must": {
"match": {
"content": {
"query": "full text search",
"operator": "and"
}
}
},
"should": [
{ "match": {
"content": {
"query": "Elasticsearch",
"boost": 3
}
}},
{ "match": {
"content": {
"query": "Lucene",
"boost": 2
}
}}
]
}
}
}
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
boolQueryBuilder.must(QueryBuilders.matchQuery("content","full text search").operator(Operator.AND));
boolQueryBuilder.should(QueryBuilders.matchQuery("content","Elasticsearch").boost(3));
boolQueryBuilder.should(QueryBuilders.matchQuery("content","Lucene").boost(2));
注:(1)这些语句使用默认的 boost
值 1
。 (2)这条语句更为重要,因为它有最高的 boost
值。 (3)这条语句比使用默认值的更重要,但它的重要性不及 Elasticsearch
语句。
boost
参数被用来提升一个语句的相对权重( boost
值大于 1
)或降低相对权重( boost
值处于 0
到 1
之间),但是这种提升或降低并不是线性的,换句话说,如果一个 boost
值为 2
,并不能获得两倍的评分 _score
。
七、控制分析
查询只能查找倒排索引表中真实存在的项, 所以保证文档在索引时与查询字符串在搜索时应用相同的分析过程非常重要,这样查询的项才能够匹配倒排索引中的项。
尽管是在说 文档 ,不过分析器可以由每个字段决定。 每个字段都可以有不同的分析器,既可以通过配置为字段指定分析器,也可以使用更高层的类型(type)、索引(index)或节点(node)的默认配置。在索引时,一个字段值是根据配置或默认分析器分析的。
在之前的IK分词器和拼音分词器结合应用文章中有介绍,指定不同字段分词器分析,在此就不再说了
Elasticsearch深入搜索之全文搜索及JavaAPI使用的更多相关文章
- elasticsearch配合mysql实现全文搜索
之前用了sphinx,发现很多东西很久都没更新过了,之前只是知道有elasticsearch这个东西,还以为是java才能用,所以一直没有去了解过,也许sphinx慢慢会被淘汰了吧. 前置条件:需要安 ...
- ElasticSearch利用IK实现全文搜索
要做到中文全文检索还需要按照中文分词库 ,这里就使用 IK来设置 安装中文分词库 相关命令: whereis elasticsearch 找到目录 进入 到/usr/elasticsearch/bin ...
- 全文搜索-介绍-elasticsearch-definitive-guide翻译
全文搜索 我们通过前文的简单样例,已经了解了结构化数据的条件搜索:如今.让我们来了解全文搜索-- 如何通过匹配全部域的文本找到最相关的文章. 关于全文搜索有两个最重要的方面: 相似度计算 通过TF/I ...
- 全文搜索之 Elasticsearch
概述 Elasticsearch (ES)是一个基于 Lucene 的开源搜索引擎,它不但稳定.可靠.快速,而且也具有良好的水平扩展能力,是专门为分布式环境设计的. 特性 安装方便:没有其他依赖,下载 ...
- Elasticsearch全文搜索——adout
现在尝试下稍微高级点儿的全文搜索——一项传统数据库确实很难搞定的任务. 搜索下所有喜欢攀岩(rock climbing)的雇员: curl -XGET 'localhost:9200/megacorp ...
- 在 Laravel 项目中使用 Elasticsearch 做引擎,scout 全文搜索(小白出品, 绝对白话)
项目中需要搜索, 所以从零开始学习大家都在用的搜索神器 elasiticsearch. 刚开始 google 的时候, 搜到好多经验贴和视频(中文的, 英文的), 但是由于是第一次接触, 一点概念都没 ...
- 使用ElasticSearch服务从MySQL同步数据实现搜索即时提示与全文搜索功能
最近用了几天时间为公司项目集成了全文搜索引擎,项目初步目标是用于搜索框的即时提示.数据需要从MySQL中同步过来,因为数据不小,因此需要考虑初次同步后进行持续的增量同步.这里用到的开源服务就是Elas ...
- ASP.NET Web API + Elasticsearch 6.x 快速做个全文搜索
最近想做个全文搜索,设想用 ASP.NET Web API + Elasticsearch 6.x 来实现. 网上搜了下 Elasticsearch 的资料,大部分是讲 linux 平台下如何用 ja ...
- ElasticSearch 2 (14) - 深入搜索系列之全文搜索
ElasticSearch 2 (14) - 深入搜索系列之全文搜索 摘要 在看过结构化搜索之后,我们看看怎样在全文字段中查找相关度最高的文档. 全文搜索两个最重要的方面是: 相关(relevance ...
随机推荐
- ES6躬行记(1)——let和const
古语云:“纸上得来终觉浅,绝知此事要躬行”.的确,不管看了多少本书,如果自己不实践,那么就很难领会其中的精髓.自己研读过许多ES6相关的书籍和资料,平时工作中也会用到,但在用到时经常需要上搜索引擎中查 ...
- Linux文本编辑器vim
目录 1.vim 的工作模式 2.插入命令 3.定位命令 4.删除命令 5.复制和剪切命令 6.替换和取消命令 7.搜索和搜索替换命令 8.保存和退出命令 9.vim 高级操作 10.总结 通过前面几 ...
- OpenResty api网关设计
本文讲述 OpenResty api网关设计,主要涉及api网关介绍.openresty api网关 请求路由(路由判断.路由重写.服务判断.限流).授权验证(统一认证).动态Upstream 以及这 ...
- Angular2入门:TypeScript的类 - 参数属性:定义和初始化类成员
- 翻译:last_value()函数(已提交到MariaDB官方手册)
本文为mariadb官方手册:LAST_VALUE()的译文. 原文:https://mariadb.com/kb/en/last_value/我提交到MariaDB官方手册的译文:https://m ...
- python安装Jieba中文分词组件并测试
python安装Jieba中文分词组件 1.下载http://pypi.python.org/pypi/jieba/ 2.解压到解压到python目录下: 3.“win+R”进入cmd:依次输入如下代 ...
- WinForm DataGridView 绑定泛型List(List<T>)/ArrayList不显示的原因和解决
背景:无意间遇到了一个不大不小的问题,希望对一些遇到的人有所帮助! 一.问题 WinForm DataGridView 绑定泛型List (List<T>)/ArrayList不显示,UI ...
- 从零开始学安全(二十一)●PHPSPL异常
- [android] 采用服务执行长期后台的操作
服务:在后台长期运行的没有界面的组件 新建一个类PhoneService类,继承系统的Service类 清单文件中 进行配置 新建一个节点<service>,设置名称android:nam ...
- 慕课网maven多环境配置
profile 下面的节点,是把profile 标签内容复制几份,并且需要把<activation> 去掉.在idea右侧 maven button 中会出现选择 节点. 接着打包命令和打 ...