我们使用Elasticsearch
的最终目的是从数据集中搜索想要的数据,并基于这些数据建立视图;Elasticsearch
提供了很简单易用的查询方式来获得数据,例如:
- 我的服务中有哪些请求处理耗时超过500ms的;
- 在我的集群中,上一周有哪些用户执行了
regsvr32.exe
; - 我的网站中哪些页面包含了特殊的文字或者段落;
Elasticsearch
总的来说提供了以下几种搜索方式:
- 精确值搜索:可以搜索精确的数值、日期、IP地址或字符串值,也可搜索这些值的范围。
- 全文搜索:对非结构化的文本数据使用全文查询,找到与查询词条最匹配的文档。
- 向量搜索:可以在Elasticsearch中存储向量,并使用近似最近邻(ANN)或K最近邻(kNN)搜索算法查找相似的向量,支持诸如语义搜索之类的应用场景。
那我们改如何构建Elasticsearch
的搜索请求语句呢?本文接下来就根据官方文档来阐述ES如何搜索数据的。
ES的Search选项
Elasticsearch
一个search可以由一个或者多个query组成,Elasticsearch
会返回匹配的documents,一个search也可以包含额外用于更好进行query的信息,例如限制返回的查询结果数;
ES本身支持的搜索选项有很多,我们可以通过Search APIs 来搜索和聚合数据;ES的一个search可以支持如下一些通用的选项:
Query DSL是Elasticsearch
提供的基本查询表达式,支持通过DSL(Domain Specific Language)领域特定语言,一种基于JSON的表达式语法来进行数据的查询;Query DSL提供了丰富多样的query类型,可以通过组合去获取我们想要的查询结果,query支持的类型包括:
- Compound queries复合查询:允许组合多个条件查询来生成匹配结果,例如Boolean 逻辑查询;
- Term-level queries词条级查询,基于Team的查询,用于过滤和精确匹配;
- Full text queries全文检索:类似搜索引擎,查询最匹配的文档;
- Geo坐标和spatial queries空间查询;
我们可以在search中通过aggs选项来对query结果或者直接对所有document进行聚合展示,例如:服务的平均响应时间;
在同一个请求中使用逗号分隔的值和类grep的索引模式来搜索多个数据流和索引。您甚至可以增加来自特定索引的搜索结果的权重,如下可以通过逗号分隔多个索引来在多个索引中进行同样的search;
1 | GET /love-music-idx,love-music-idx1/_search |
默认情况,针对一个search,最多返回top10的匹配的document;可以通过search的分页选项功能来增加或者减少搜索返回的结果数;分页支持的选项有:
选项 | 说明 |
---|---|
from | 跳过指定数量的hits的document,默认为0 |
size | 最大返回hits的document的数量,默认为10 |
search_after |
如下,从匹配的第5个document起,最多返回20个匹配结果:
1 | GET /_search |
默认,search的返回结果会包含hits的document的所有fields,即hits.hits._source
;我们可以显示的设置只返回hits document的部分fields,通过如下两个search的选项:
fields
选项:提取document和index mapping中的指定字段;推荐使用;_source
选项:
默认情况下,search的返结果会按照搜索的相关性分数即_score
来进行排序,有两种方式可以改变默认的排序行为:
- 我们也可以在搜索时,在search中通过
script_score
选项来定制自己的_score
计算公式;此种方式还是现在_score
进行排序; - 我们也可以通过按照document的指定fields进行对搜索结果的排序;
接下来我们就详细展开Elasticsearch
的一个search可以支持的通用的选项
Query DSL
Elasticsearch
提供了一个Query DSL(Domain Specific Language),直翻为:查询领域特定语言,是一种非常灵活又富有表现力的基于JSON的查询语言。 Elasticsearch 使用它可以以简单的 JSON 接口来展现 Lucene 功能的绝大部分。
DSL查询语句可以看作是有两种类型的子句组成的查询的AST(Abstract Syntax Tree)抽象语法树:分为两类:
- Leaf query clauses(叶子查询子句):用于查询特定字段的特定值,例如
match
,term
,range
查询,这些查询可以单独使用; - Compound query claused(复合查询子句):由一系列的叶子查询或者复合查询组成的,用以逻辑方式(
bool
,dis_max
)组合的多个查询或者改变默认的查询;
DSL查询语句的行为根据是否是query context或是filter context会有不同表现;官方文档 Allow expensive queries中列出来,比较耗时的查询类型;
Query and filter context
前面提到:DSL查询语句的行为根据是否是query context或是filter context会有不同表现,下面介绍查询和过滤器上下文相关的概念:
Relevance scores(相关性分数)
相关性分数表示查询和document的匹配程度,默认情况下,Elasticsearch
会根据查询结果的各个document的相关性分数进行排序;
相关性分数一个浮点数,在搜索结果中通过元数据字段_score
展示,该数值越大关联度越高,相关性分数数值范围在 0 到 1 之间,在 Elasticsearch 中,相关性分数是通过一种称为 TF/IDF(Term Frequency/Inverse Document Frequency)的算法计算的。TF/IDF 算法考虑了词频(term frequency)和逆文档频率(inverse document frequency)来计算文档与查询之间的相关性。
每一种类型的查询计算相关性分数都是不同的,(Query Context)和过滤器上下文(Filter Context)中相关性分数的计算方式是不同的。
如下测试:
1 | PUT my-index-000001/_doc/1 |
查询:
1 | GET my-index-000001/_search |
结果如下,可以看到hits
中,匹配的文档中返回了"_score": 0.5753642
,
1 | { |
Query context(查询上下文)
查询上下文中,查询子句回答的是:查询语句和文档的匹配度。除了判断document和查询是否匹配,查询子句还要计算出一个relevance socre相关性分数;
查询上下文的语法结构是在_search
语句中,查询参数以query
参数来标识,如下,可以参考Search API:
1 | GET /my-index-000001/_search |
如下,基于上面相关性分数的测试,再往Index中插入一个doc2,
1 | PUT my-index-000001/_doc/2 |
执行查询如下:
1 | GET my-index-000001/_search |
可以看到返回结果中每个doc都会计算相关新分数,并按分数进行排序返回:
1 | { |
Filter context(过滤器上下文)
过滤器上下文中,查询子句回答的是:这个文档是否匹配该查询子句。返回结果就是Yes or No,没有相关性分数的计算;过滤器上下文主要用于过滤结构化数据,比如:
- 某个
timestamp
是否落在2015年到2016年的范围内? - 某个
status
字段是否设置为"published"
?
Elasticsearch会自动缓存频繁使用的过滤器,以提高性能。
过滤器上下文生效的方式是在query
子句中通过传入filter
参数,例如:bool
查询中的filter
或者must_not
参数,constant_score
查询中的filter
参数,或者filter
聚合查询;
如下:
1 | GET my-index-000001/_search |
该查询由于使用了过滤器上下文,所以所有匹配的name
字段的所有document都不会计算相关性分数,如下是返回结果:
1 | { |
Compound queries
Compound queries复合查询:由一系列的叶子查询或者复合查询组成的,用以组合的多个查询或者改变默认的查询,或者切换query
成为filter
上下文。复合查询主要一下几类查询组成:
bool
query:这是组合多个叶子查询或复合查询的默认方式,通过must
、should
、must_not
、filter
等子句进行组合。must
和should
子句的分数会被合并,更多匹配会得到更高分数,而must_not
和filter
子句运行在过滤器上下文中。boosting
query:返回匹配positive
查询的文档,但会降低也同时匹配negative
查询的文档分数。constant_score
query:包装另一个查询,但在过滤器上下文中执行。所有匹配的文档会被赋予相同的常量_score
分dis_max
query:接受多个查询,返回匹配任何一个查询子句的文档。与bool
查询将所有匹配查询的分数合并不同,dis_max
只使用单个最佳匹配查询子句的分数。function_score
query:通过函数修改主查询返回的分数,以考虑流行度、最近度、距离或使用脚本实现的自定义算法等因素。
Boolean query
bool
查询是用于逻辑匹配要查询的文档,它使用一个或多个如下的bool
子句构建组成:
子句 | 描述 |
---|---|
must |
该子句(查询)必须出现在匹配的文档中,并会对分数产生贡献。 |
filter |
该子句(查询)必须出现在匹配的文档中。但与must 不同,查询的分数将被忽略。filter 子句在过滤器上下文中执行,这意味着忽略评分,并且子句会被考虑进行缓存。 |
should |
该子句(查询)应该出现在匹配的文档中,并会对分数产生贡献。 |
must_not |
该子句(查询)不得出现在匹配的文档中。子句在过滤器上下文中执行,这意味着忽略评分,并且子句会被考虑进行缓存。由于忽略评分,因此所有文档的分数都将返回0 。 |
bool
查询采用的策略是more-matches-is-better
,因此每个匹配的must或should子句的分数将被加在一起,为每个文档提供最终的_score分数。
我们下面通过一个bool
查询来看一下子句的功能:
1 | POST _search |
Boosting query
boosting
查询用于返回匹配positive
查询的文档,但会降低同时匹配negative
查询的文档的相关性分数。boosting
查询的设计目的是从搜索结果中降级指定的文档,体现了结果排序时不同因素的权重。
boosting
查询语句支持的顶级参数如下:
参数 | 描述 |
---|---|
positive |
(必需,查询对象) 你希望运行的查询。返回的文档必须匹配这个查询。 |
negative |
(必需,查询对象) 用于降低匹配文档相关性分数的查询。 |
negative_boost |
(必需,浮点数) 介于0和1.0之间的浮点数,用于降低匹配negative 查询的文档的相关性分数。 |
如下测试语句:
1 | GET my-index-000001/_search |
如下是返回结果:可见"name": "alias"
的doc的相关性分数被降低了0.5
1 | { |
Constant score query
包装另一个查询,但在过滤器上下文中执行。所有匹配的文档会被赋予相同的常量_score
分,使用方式为在query
语句中使用constant_score
子句来进行查询的包装,constant_score
子句包含的顶层参数为:
参数 | 描述 |
---|---|
filter |
(必需,查询对象) 你希望运行的过滤器查询。返回的文档必须匹配这个查询。 |
boost |
(可选,浮点数) 用作每个匹配 filter 查询的文档的常量相关性分数的浮点数。默认为 1.0。 |
如下测试:
1 | GET my-index-000001/_search |
测试结果如下:
1 | { |
Full text queries
全文检索允许你搜索已经被分析过的文本字段,例如电子邮件正文。查询字符串会使用与索引字段时相同的分析器进行处理。全文检索查询包含以下支持的查询子句:
查询 | 描述 |
---|---|
intervals |
一种全文本查询,允许精细控制匹配词条的顺序和邻近程度。 |
match |
执行全文本查询的标准查询,包括模糊匹配、短语或邻近查询。 |
match_bool_prefix |
创建一个布尔查询,每个词条作为term 查询匹配,除了最后一个词条作为prefix 查询匹配。 |
match_phrase |
类似于match 查询,但用于匹配精确短语或词邻近匹配。 |
match_phrase_prefix |
类似于match_phrase 查询,但对最后一个词执行通配符搜索。 |
multi_match |
match 查询的多字段版本。 |
combined_fields |
跨多个字段进行匹配,就像它们被索引到一个组合字段中一样。 |
query_string |
支持紧凑的Lucene查询字符串语法,允许在单个查询字符串中指定AND|OR|NOT条件和多字段搜索。仅供专家用户使用。 |
simple_query_string |
query_string 语法的一个更简单、更健壮的版本,适合直接暴露给用户。 |
Intervals query
Intervals间隔查询是Elasticsearch中一种特殊的全文本查询方式,它不仅关注词条本身是否匹配,更关注词条在文本中的相对位置和顺序。直白的理解这句话可能还是不清楚Intervals查询到底是干什么用的,我们来看一个官方的搜索用例:
1 | POST _search |
这个查询语句的意图为:匹配包含 "my favorite food"
紧接着 "hot water"
或"cold porridge"
的文档。下面是几个是否可以匹配的例子:
- 该查询语句可以匹配
my favorite food is cold porridge
; - 但是无法匹配
it's cold my favorite food is porridge
; - 无法匹配:
my food favorite is cold porridge
;如果第一个子间隔的"ordered" : false
的话,就可以匹配; - 无法匹配:
my food is cold porridge
,但是,如果第一个子间隔的"max_gaps" : 1
的话,就可以匹配;
Intervals查询的核心是定义一组规则对象,用于匹配文档中的词条,Intervals查询子句顶级参数为field
,表示搜索的字段名,field
内支持的规则如下:
规则 | 描述 |
---|---|
match |
精确匹配指定的词条或短语 |
prefix |
匹配以指定前缀开头的词条 |
wildcard |
匹配包含通配符模式的词条 |
fuzzy |
匹配与指定词条的编辑距离在指定范围内的词条 |
all_of |
所有子规则必须满足 |
any_of |
至少有一个子规则满足即可 |
通过灵活组合这些规则,可以构建出复杂的匹配模式,以满足对词条位置、顺序、间隔等方面的精确要求。例如一个规则可以要求“词条A必须紧跟着词条B,且它们之间的间隔不超过3个词条”。另一个规则可以是“匹配包含’lucene’前缀且编辑距离在2以内的任意词条”。
每个规则支持的参数可以详细参考官方手册,这里就不详细说明了;
Match query
match
是全文本查询的标准查询,包括模糊匹配、短语或邻近查询。通过匹配提供的text
,number
,date
,或者bool
值返回对应的doc,输入的text
在进行匹配前,会执行和doc中对应field的text
相同的分词处理。
match
查询的语法结构顶级参数为:field
,表示要搜索的doc的字段名,field
参数内部支持的顶级参数列表如下:
参数 | 描述 |
---|---|
query |
(必选)您希望在提供的<field> 中找到的文本、数字、布尔值或日期。match 查询会在执行搜索之前分析任何提供的文本。这意味着match 查询可以在text 字段中搜索分析后的词元,而不是精确词条。 |
analyzer |
(可选,字符串)用于将query 值中的文本转换为词元的分析器。默认为映射到<field> 的索引时分析器。如果未映射任何分析器,则使用索引的默认分析器。 |
auto_generate_synonyms_phrase_query |
(可选,布尔值)如果为true ,则会自动为多词条同义词创建短语查询。默认为true 。 |
boost |
(可选,浮点数)用于减少或增加查询的相关性分数的浮点数。默认为1.0 。 |
fuzziness |
(可选,字符串)模糊查询,允许的最大编辑距离。 |
max_expansions |
(可选,整数)查询将扩展到的最大词条数。默认为50 。 |
prefix_length |
(可选,整数)模糊匹配时保留不变的起始字符数。默认为0 。 |
fuzzy_transpositions |
(可选,布尔值)如果为true ,则模糊匹配的编辑包括两个相邻字符的转置(ab → ba)。默认为true 。 |
fuzzy_rewrite |
(可选,字符串)用于重写查询的方法。有关有效值和更多信息,请参阅rewrite 参数。 |
lenient |
(可选,布尔值)如果为true ,则忽略格式错误,例如为数字字段提供文本query 值。默认为false 。 |
operator |
(可选,字符串)用于解释query 值中文本的布尔逻辑。有效值为OR (默认)或AND 。 |
minimum_should_match |
(可选,字符串)文档必须匹配的最小子句数,才能返回。有关有效值和更多信息,请参阅minimum_should_match 参数。 |
zero_terms_query |
(可选,字符串)如果analyzer 删除所有词元(例如使用stop 过滤器时),是否返回任何文档。有效值为none (默认)或all 。 |
query短查询
match
查询field
参数的子参数query
是必须的,它是全文本查询的标准查询,为了降低复杂度,ES支持简化的短查询语法格式:可以将field
参数和query
参数合并一起只使用field
参数。如下查询description
字段包含guy
单词的文档,两个写法是对等的:
1 | GET my-index-000001/_search |
operator参数
match
查询是如何执行查询操作呢?其实它是一个bool类型的查询,match
查询会对输入的text
进行分析处理:包括分词,格式化等,然后通过operator
参数来对text
分析后的每个单词的查询执行bool操作,只有bool操作为真的时候,文档才被认为匹配。
operator
参数有两个值:or
(默认值)和and
,如下:只要description
字段包含任意单词beautify
或guy
,对应的doc就被认为是匹配的,
1 | GET my-index-000001/_search |
下面的match
查询要去description
字段必须同时包含单词beautify
或guy
,对应的doc才会被认为是匹配的:
1 | GET my-index-000001/_search |
fuzziness模糊查询
match
查询中的fuzziness
参数,允许基于被查询字段的类型执行基于相似度的模糊匹配。可以通过设置prefix_length
和max_expansions
来控制模糊过程。
下面是fuzziness
参数的值:
值 | 描述 |
---|---|
0 | 禁用模糊匹配,match匹配默认关闭。 |
1, 2 | 指定允许的最大Levenshtein编辑距离(或编辑次数)。 |
AUTO |
根据词条长度生成编辑距离。可选择指定低和高距离参数:AUTO:[low],[high] 。如果未指定,默认值为3和6,相当于AUTO:3,6 ,对应以下情况:- 长度0..2: 必须完全匹配 - 长度3..5: 允许1次编辑 - 长度>5: 允许2次编辑 |
如下测试:查询description
字段和"guyy"
相似的字段:
1 | GET my-index-000001/_search |
Elasticsearch中的模糊查询使用Levenshtein编辑距离来计算字符串之间的相似度。Levenshtein编辑距离是指将一个字符串转换成另一个字符串所需的最小编辑操作的次数,编辑操作包括:
- 替换一个字符;
- 插入一个字符;
- 删除一个字符
例如:将"book"
转换为"brook"
只需一次插入操作,编辑距离为1。将"book"
转换为"back"
需要一次替换操作,编辑距离也为1。
Match boolean prefix query
全文本查询支持match_bool_prefix
前缀匹配查询,match_bool_prefix
的执行逻辑是对输入的text
进行分析,然后将每个term(除了text
中的最后一个单词)构建出一个 bool
query,text
中的最后一个单词用于prefix
匹配。
全文本查询的match_bool_prefix
前缀匹配查询支持的参数列表和前面match
查询一致,只是不同的参数针对text
中的最后一个单词是否生效,如下:
minimum_should_match
和operator
参数,这两个参数的作用与match
查询中的一样,应用于构造的bool
查询上。fuzziness
、prefix_length
、max_expansions
、fuzzy_transpositions
和fuzzy_rewrite
这些参数可应用于除最后一个词项之外的所有词项构造的term
子查询上。
下面看一下示例:
1 | GET /_search |
等价于:
1 | GET /_search |
Match phrase query
全文本查询支持match_phrase
短语搜索的查询类型,针对输入的查询短语,会进行如下处理:
- 通过分析器进行分词处理,每个分词及其在原始字符串中的位置会被记录;
- 每个分词都会构建一个
term
查询,匹配的文档必须要严格有序的包含所有的词项;
如下就是搜索包含"handsome guy"
短语的文档:
1 | GET /_search |
其实这个短语查询和全文本查询中的另一类Intervals查询比较重合,intervals查询可以控制更细的力度。
match_phrase
短语搜索支持slop
参数,参数值解释如下:
- 0:这个默认值,当短语查询匹配词语时,它们必须按照查询中给定的顺序依次出现,而且它们之间不能有任何其他词语插入。
- 2:这是一个特殊值,如果短语颠倒了也就是所谓的 “transposed”(转置),那么这种情况下的
slop
为 2。所以slop
为 2时,表示允许搜索的短语颠倒,或者短语之间允许有两个词语的距离。
Match phrase prefix query
全文本查询支持match_phrase_prefix
短语前缀搜索的查询类型,是在match_phrase
短语搜索的查询类型的基础上扩展而来,支持最后一个term
的前缀匹配查询。
match_phrase_prefix
查询的语法结构和其他全文搜索的一样,顶级参数为:field
,表示要搜索的doc的字段名,field
参数内部支持的顶级参数列表如下:
参数 | 说明 |
---|---|
query |
(必需,string)要在指定字段中搜索的文本 |
analyzer |
(可选,string)用于将查询文本分词的分词器,默认使用字段映射的分词器 |
max_expansions |
(可选,整数)最后一个查询词项要扩展匹配的最大词条数,默认为50 |
slop |
(可选,整数)匹配词项间允许移动的最大位置距离,默认为0(紧邻),转置词的slop为2 |
zero_terms_query |
(可选,string)当分词器过滤掉所有词项时的处理方式,可取值none (默认,不返回文档)或all (返回所有文档,等同match_all 查询) |
match_phrase
短语搜索的语法结构也是一样,按道理应该在match_phrase
短语搜索中介绍语法结构。
Combined fields查询
全文本查询支持combined_fields
组合字段查询,之前介绍的查询都是针对doc的单个字段,ES也提供了多字段组合的查询,就像这些字段的内容被索引到一个组合字段中一样。combined_fields
查询过程是针对输入的字符串进行分词,然后每个分词都在所有给出的field
中进行搜索。
组合字段查询针对匹配需要跨越多个text
字段特别有用,例如查询某段文字是否出现在一个doc的标题,摘要或者正文中。组合字段查询针对多个text
字段,只要有一个字段符合匹配条件,就认为该doc是匹配的。
如下测试语句是搜索doc中的"description"
和"name"
字段,查看是否能够match
匹配"handsome person"
。
1 | GET /_search |
combined_fields
查询针对查询的文本也支持operator
参数,默认为”or”,如下是搜索doc中的"description"
和"name"
字段,能够match
匹配"handsome person"
中的所有单词。
combined_fields
查询支持的参数列表如下:
参数 | 说明 |
---|---|
fields |
(必需,字符串数组)要搜索的字段列表,支持通配符模式。只有text 类型的字段受支持,且必须使用相同的analyzer 分析器 |
query |
(必需,字符串)在指定字段中搜索的文本 |
auto_generate_synonyms_phrase_query |
(可选,布尔值)如果为true ,将自动为多词条同义词创建短语查询,默认为true |
operator |
(可选,字符串)解释查询文本的布尔逻辑,可取值or (默认)或and |
minimum_should_match |
(可选,字符串)文档被返回所需的最小匹配子句数,参考minimum_should_match 参数的有效值和更多信息 |
zero_terms_query |
(可选,字符串)当分词器过滤掉所有词项时的处理方式,可取值none (默认,不返回文档)或all (返回所有文档,等同match_all 查询) |
combined_fields
查询支持指定字段boost权重,如下,title
字段设置了boost=2
,那么如果每个出现在title
字段中的词条,会被认为出现了两次,最终提高其相关性分数。
1 | GET /_search |
Multi-match query
全文搜索支持multi_match
多字段匹配,在多个字段上执行相同的常规match
查询,并按照指定的方式合并每个字段的分数。
multi_match
匹配的格式如下,和combined_fields
组合字段查询使用很相似:
1 | GET /_search |
multi_match
匹配同时支持字段名的通配符匹配和字段名的权重调整。
multi_match
匹配支持type
参数,用于表示不同的查询类型,type
参数值包括如下:
类型 | 描述 |
---|---|
best_fields (默认) |
查找匹配任意字段的文档,但使用得分最高的单个字段的 _score 。详见 best_fields 。 |
most_fields |
查找匹配任意字段的文档,并将每个字段的 _score 合并。详见 most_fields 。 |
cross_fields |
将使用相同分析器的字段视为一个大字段。在任意字段中查找每个查询词条。详见 cross_fields 。 |
phrase |
在每个字段上执行 match_phrase 查询,并使用得分最高的单个字段的 _score 。详见 phrase 和 phrase_prefix 。 |
phrase_prefix |
在每个字段上执行 match_phrase_prefix 查询,并使用得分最高的单个字段的 _score 。详见 phrase 和 phrase_prefix 。 |
bool_prefix |
在每个字段上创建 match_bool_prefix 查询,并将每个字段的 _score 合并。详见 bool_prefix 。 |
这里我们会发现combined_fields
组合字段查询和持multi_match
多字段匹配有比较相似的地方,这里主要的差异如下:
方面 | combined_fields | multi_match |
---|---|---|
评分模型 | 视为组合字段,跨字段合并统计数据并计算综合相关性分数,尝试遵循 BM25F 模型 | 根据 type 使用最佳字段分数(best_fields )或合并所有字段分数(most_fields 等) |
查询执行方式 | 分析查询文本为词条序列,在每个字段上查找 | 也会先分析查询文本,但接着会根据 type 构建不同的字段级查询,如 match_phrase 等 |
查询类型的灵活性 | 只能执行 term/phrase 查询的组合 | 可通过 type 指定各种查询类型,如 bool 、phrase 、phrase_prefix 等 |
性能差异 | 只涉及分词和 term/phrase 查询,性能开销较小 | 需构建和执行多个复杂的字段查询,再合并分数,性能开销较大 |
总的来说,combined_fields
查询更侧重于跨字段的单词/短语匹配,而multi_match
查询则专注于灵活的跨字段查询类型组合。在相关性和性能之间做出权衡选择。如果需求简单,combined_fields
可能是更好的选择。
Query string query
Term-level查询
Term-level queries词条级查询,Term级别查询旨在针对结构化数据进行精确值匹配,例如日期范围、IP地址、价格或产品ID等。这类数据通常是经过某种方式编码或格式化的,要求查询时完全匹配存储在字段中的原始值。
和Full-text queries全文搜索的很大区别,它们不会分词,而是将搜索词直接与字段值进行比较,能够精确无误地查找到匹配项。对于keyword
字段,查询词也会根据normalizer
进行归一化以保持一致性。
基于Term的查询支持类型如下:
查询类型 | 描述 |
---|---|
exists query | 返回包含字段的任何索引值的文档。 |
fuzzy query | 返回包含与搜索词类似的术语的文档。Elasticsearch使用Levenshtein编辑距离来衡量相似性或模糊性。 |
ids query | 基于它们的文档ID返回文档。 |
prefix query | 返回在提供的字段中包含特定前缀的文档。 |
range query | 返回包含在提供的范围内的术语的文档。 |
regexp query | 返回包含与正则表达式匹配的术语的文档。 |
term query | 返回在提供的字段中包含精确术语的文档。 |
terms query | 返回在提供的字段中包含一个或多个精确术语的文档。 |
terms_set query | 返回在提供的字段中包含最少数量的精确术语的文档。您可以使用字段或脚本定义匹配术语的最小数量。 |
wildcard query | 返回包含与通配符模式匹配的术语的文档。 |
Exists query
基于term
的exists
查询用于匹配存在指定字段名的所有doc,注意这里是匹配字段名,而不是字段的值,如下:用于搜索所有包含"name"
字段的doc:
1 | GET /_search |
常见的一些使用exists
查询的典型场景包括:
- 检查某个字段是否为null或缺失;
- 将有某个字段的文档与没有的文档分开;
- 对于某些能区分有无的业务场景;快速检索出存在值的文档
如下搜索就是通过 boolean query 中的must_not
结合基于Term的exists
query用来发现哪些doc缺失了"user.id"
字段。
1 | GET /_search |
Fuzzy query
基于term
的fuzzy查询用于返回和搜索词相近的doc,和全文检索中的match
查询中的fuzziness
参数一样,使用Levenshtein编辑距离来衡量相似性或模糊性。这里在前面[fuzziness模糊查询]已经介绍了Levenshtein距离的概念,这里不再赘述了。
如下测试用例,匹配"user.id"
的值和"ki"
相似的文档:
1 | GET /_search |
基于term
的fuzzy查询的语法结构,"fuzzy"
参数后的顶级参数为字段名,字段名的顶级参数列表如下:
参数 | 描述 |
---|---|
value |
(必需,字符串) 要在指定字段中查找的词条值 |
fuzziness |
(可选,字符串) 允许的最大编辑距离,用于模糊匹配,详见 Fuzziness 的有效值和更多信息 |
max_expansions |
(可选,整数) 创建的最大变体数量,默认为 50 |
prefix_length |
(可选,整数) 在创建变体时保持不变的起始字符数,默认为 0 |
transpositions |
(可选,布尔) 是否包括相邻两个字符转置的编辑,如 ab 到 ba,默认为 true |
rewrite |
(可选,字符串) 重写查询的方法,有效值和更多信息见 rewrite 参数 |
那基于term
的fuzzy查询和全文检索中的match
查询中的fuzziness
查询有什么区别呢?
- 搜索方式不同:
term
查询的fuzzy
直接将查询词条与字段词条值进行比对。match
查询的fuzziness
先对查询语句分词,再对每个词条分别查找近似词条进行匹配。 - 适用场景不同:
term
查询的fuzzy
适用于已知存在的结构化数据,如ID、代码等关键词进行模糊匹配。match
查询的fuzziness
更多应用于全文本场景下的拼写错误容错。
IDs query
基于term
的IDs
查询用于搜索指定_id
字段的doc,比较简单,如下:
1 | GET /_search |
Prefix query
基于term
的prefix
前缀查询和全文本查询的match_bool_prefix
前缀匹配比较类似,但是区别还是比较大的,就像前面分析的fuzzy
查询一样,主要是搜索方式和适用场景的不同,这里不再赘述。
如下是基于term
的prefix
前缀查询的示例,查询"user.id"
字段以"ki"
前缀开始的document:
1 | GET /_search |
基于term
的prefix
前缀查询的语法格式,顶级参数是field
的名字,field
内的参数列表如下:
参数 | 描述 |
---|---|
value |
(必需,字符串) 要在指定字段中查找的词条值的开头字符 |
rewrite |
(可选,字符串) 用于重写查询的方法,有效值和更多信息见 rewrite 参数 |
case_insensitive |
(可选,布尔值) 从7.10.0版本开始,当设置为true时,允许以ASCII不区分大小写的方式匹配值与索引字段值。默认为false,区分大小写取决于字段映射的设置。 |
Range query
基于term
的range查询是针对结构化数据比较常见的一种查询方式。我们先看一个例子:
1 | GET /_search |
上面的例子是要所有doc中的age
字段执行范围查询,所有字段值在[10, 20]
范围的doc的相关性分数乘以2的权重系数。
range查询的语法结构如下:在field
字段名顶级参数下面的参数包括:
参数 | 描述 |
---|---|
gt |
(可选) 大于指定值 |
gte |
(可选) 大于等于指定值 |
lt |
(可选) 小于指定值 |
lte |
(可选) 小于等于指定值 |
format |
(可选,字符串) 用于转换查询中日期值的格式,覆盖字段映射中的格式设置 |
relation |
(可选,字符串) 指定范围查询如何匹配范围字段值,取值为INTERSECTS (默认,相交)、CONTAINS (包含)或WITHIN (被包含) |
time_zone |
(可选,字符串) 用于将查询中的日期值转换为UTC的时区或UTC偏移量,如+01:00 或America/Los_Angeles |
boost |
(可选,浮点数) 用于降低或提高查询相关性评分的系数,默认为1.0 |
当要查询field
类型参数是date
字段类型,我们可以使用 date math 在这四个参数中:gt
,gte
,lt
,lte
,如下查询是搜索timestamp
字段的日期在今天和昨天之间:
1 | GET /_search |
Term query
基于term
级别的term
查询用于精确的匹配字段值。
term
查询的语法结构为,term
参数下的顶级参数为field
名字,顶级参数为field
下的参数列表如下:
参数 | 含义 |
---|---|
value(值) | 在提供的字段中查找的项。必须与字段值完全匹配,包括空格和大小写。 |
boost(增强) | 用于调整查询的相关性得分的浮点数。默认值为1.0。可以增加或减少相关性得分。 |
case_insensitive | 允许在索引字段值时对ASCII进行不区分大小写匹配。默认为false。 |
需要注意的是:避免使用term
查询来搜索text
字段,因为ES默认会对text
字段进行分词处理,如果用term
查询,不会对输入进行分析处理,直接用输入整体和text
字段的分析后的每个分词进行匹配,这可能和预期的结果不一致,且效率较低。
如下:
1 | PUT my-index-000001/_doc/1 |
如果使用如下term
查询,结果会是匹配不到的:
1 | GET my-index-000001/_search |
参考Multi-fields和Dynamic mapping后知道,默认ES会针对JSON string
数据类型同时映射为text
主字段和keyword
子字段,所以如果想要term
查询返回结果,可以使用默认创建的"keyword"
子字段进行搜索:
1 | GET my-index-000001/_search |
或者改为通过match
查询来进行匹配,如下:
1 | GET my-index-000001/_search |
如果使用term
查询来搜索text
字段,可能和预期的结果不一致的还有一个点。ES默认会对text
字段进行分析处理,包括:分词,格式化等,所以如下term
搜索也是无法匹配的:
1 | GET my-index-000001/_search |
原因就是text
字段在分词后的各个单词会被格式化,包括统一转换为小写。所以上面的term
查询需要修改如下才能够匹配:
1 | GET my-index-000001/_search |
Terms query
基于term
级别的terms
查询用于精确的匹配多个字段值。terms
查询和term
查询很类似,除了可以支持搜索多个值,只有doc中的该字段精确匹配任意一个查询的值,该doc就被认为命中。
如下示例,搜索doc中"user.id"
字段值是"kimchy"
,或者 "elkbee"
:
1 | GET /_search |
terms
查询的顶级参数列表如下:
参数 | 描述 |
---|---|
<field> |
(可选,对象) 要搜索的字段。该参数的值是一个词条值数组,用于在指定字段中查找精确匹配项。要使文档被返回,至少一个词条值必须与字段值完全匹配,包括空格和大小写。 |
boost |
(可选,浮点数) 用于降低或提高查询相关性评分的系数,默认为 1.0。 |
terms
查询有一个很重要的特性是Terms lookup
,就是允许从另一个字段、索引或者外部源动态获取要匹配的词条值列表来进行搜索。针对terms loopup
查询的语法结构如下:
参数 | 描述 |
---|---|
index |
(必需,字符串) 从中获取字段值的索引名称。 |
id |
(必需,字符串) 从中获取字段值的文档ID。 |
path |
(必需,字符串) 要从中获取字段值的字段名称。Elasticsearch将使用这些值作为查询的搜索词条。如果字段值包括嵌套内部对象数组,可以使用点符号语法访问这些对象。 |
routing |
(可选,字符串) 从中获取词条值的文档的自定义路由值。如果在索引文档时提供了自定义路由值,则需要此参数。 |
如下示例,表示从"my-index-000002"
中的id
为2的document中读取"color"
字段的值,然后在"my-index-000001"
中进行terms
精确匹配。
1 | GET my-index-000001/_search?pretty |
Terms set query
基于term
级别的terms_set
查询用于精确的匹配多个字段值,且可以指定最小匹配值的个数。
terms_set
查询的语法结构为,terms_set
参数下的顶级参数为field
名字,顶级参数为field
下的参数列表如下:
参数 | 描述 |
---|---|
terms |
(必需,字符串数组) 词条数组。要使文档被返回,必须有指定数量的词条与字段值完全匹配,包括空格和大小写。所需的匹配词条数量由 minimum_should_match_field 或 minimum_should_match_script 参数定义。 |
minimum_should_match_field |
(可选,字符串) 包含要求匹配的词条数量的数值型字段,注意这里参数值,是另一个数值字段名。 |
minimum_should_match_script |
(可选,字符串) 包含要求匹配的词条数量的自定义脚本。脚本的参数和有效值,请参阅 Scripting。 |
如下示例:
1 | GET /job-candidates/_search |