前言
在 Elasticsearch
中,Term
查询和全文查询是两种完全不同的处理方式,在上一篇我们也简单对比了 Term
查询和全文查询中的 Phrase
中的区别,那么本文就彻底的来理清这两种查询之间的关系。
我们重新创建一个新的索引 index_002
,并插入以下数据
POST /_bulk
{"index":{"_index":"index_002"}}
{"id":"1","name":"lonely wolf","address":null,"count":1}
{"index":{"_index":"index_002"}}
{"id":"2","name":"lonely hello wolf","address":[],"count":3}
{"index":{"_index":"index_002"}}
{"id":"3","name":"lonely hello word wolf","address":"[广东]","count":1}
{"index":{"_index":"index_002"}}
{"id":"4","name":"Lonely Wolf","address":"['广东','深圳']","count":2}
{"index":{"_index":"index_002"}}
{"id":"5","name":"wolf","address":null,"count":1}
Term 查询
Term
查询一般表达的是最小单位查询,也就是说对我们传入的关键字会作为一个整体进行查询,而不会进行分词。
如下查询,满足条件的只有第一条数据,需要注意的是对 text
类型字段需要加上 .keyword
:
POST index_001/_search
{
"query": {
"term": {
"name.keyword": {
"value": "lonely wolf"
}
}
}
}
这里如果不加上 .keyword
则不会返回任何结果,这是因为 text
类型的字段会被倒排索引进行存储,倒排索引会利用分析器将文本进行分词,我们可以利用分词器来查看下分词结果:
POST /_analyze
{
"analyzer": "standard",
"text": ["lonely wolf"]
}
可以看到,lonely wolf
被分成了 lonely
和 wolf
两个单词,所以我们将 lonely wolf
作为一个进行查询自然是无法查询到结果的。
这里有个地方需要注意,如果我们存入的是大写单词,如 Lonely Wolf
,分词器也是一样的结果,也就是会将大写字母统一转化为小写进行存储,所以进行全文查询的时候也是无法查询出结果。
exists 查询
用来判定是否存在某一个字段,返回包含字段的任何索引值的文档。
GET index_002/_search
{
"query": {
"exists": {
"field": "address"
}
}
}
这里返回的结果就是第三条和第四条数据,像 null
值和空数组 []
不会被返回。
如果想要返回 null
值或者空数组 []
的数据,那么可以利用 bool
查询的 must_not
语句:
GET index_002/_search
{
"query": {
"bool": {
"must_not": [
{
"exists": {"field": "address"}
}
]
}
}
}
fuzzy 查询
用于近似查询,比如我们有时候在用百度搜索的时候,输错了字会被纠正:
一般情况下有一个单词错误的情况下,fuzzy
查询可以找到另一个近似的词来代替,主要有以下场景:
- 修改一个单词,如:
box
--->fox
。 - 移除一个单词,如:
black
-->lack
。 - 插入一个单词,如:
sic
-->sick
。 - 转换两个单词顺序,如:
act
-->cat
。
为了可以查询到这种近似的单词,fuzzy
查询需要创建一个所有近似词的集合,这样搜索的时候就可以采用精确查询找到近似的词来代替查询。
比如下面这个查询就可以查询出前面四条数据,同样的,value
修改为 loneyl
或 lonelyy
或 loneyle
都能查询出前面四条数据:
GET index_002/_search
{
"query": {
"fuzzy": {
"name": {
"value": "lonel"
}
}
}
}
ids 查询
通过文档 id
进行查询返回,这里的 id
为文档中的 _id
。
GET index_002/_search
{
"query": {
"ids": {
"values": ["id1","id2"]
}
}
}
prefix 查询
通过指定字段的前缀进行查询。
GET index_002/_search
{
"query": {
"prefix": {
"name": {
"value": "lo"
}
}
}
}
range 查询
通过范围进行查询。
GET index_002/_search
{
"query": {
"range": {
"id": {
"gte": 1,
"lte": 2
}
}
}
}
其中:
- gt:表示大于。
- gte:表示大于等于。
- lt:表示小于。
- lte:表示小于等于。
这种范围查询还可以用于日期的范围查询,此时将会对日期进行毫秒数转换后进行查询,如下面的例子就是查询昨天到今天的区间,而且可以通过 time_zone
指定时区:
GET _search
{
"query": {
"range" : {
"timestamp" : {
"gte" : "now-1d/d",
"lt" : "now/d"
}
}
}
}
regexp 查询
通过正则表达式进行查询。如下例子可以查询出 lon
开头的所有数据:
GET index_002/_search
{
"query": {
"regexp": {
"name": "lon.*"
}
}
}
term 查询
返回一个或者多个单词精确匹配的文档。
# 返回前面四条数据
GET index_002/_search
{
"query": {
"term": {
"name": {
"value": "lonely"
}
}
}
}
# 只返回第一条数据
GET index_002/_search
{
"query": {
"term": {
"name.keyword": {
"value": "lonely wolf"
}
}
}
}
terms 查询
terms
查询和 term
查询是一个含义,区别只是 terms
可以一次精确匹配多个词。
# 返回全部五条数据
GET index_002/_search
{
"query": {
"terms": {
"name": [
"lonely",
"wolf"
]
}
}
}
terms_set 查询
terms_set
查询和 terms
查询是一样的查询规则,不同的是 terms_set
查询可以定义匹配词项的数量,定义的数量只能从文档中的某一列中进行获取或者使用脚本进行配置:
# 这里只能查询第一和第三两条数据,因为 `Wolf` 中的首字母大写,无法被精确匹配上,count列不能是text类型
GET index_002/_search
{
"query": {
"terms_set": {
"name": {
"terms": [
"lonely",
"Wolf"
],
"minimum_should_match_field": "count"
}
}
}
}
type 查询
指定类型查询,type
类型在 7.0
版本已经标注为过期,8.0
版本已经被废弃。
wildcard 查询
通过通配符进行查询,这个可以理解为是简易版本的正则表达式查询:
GET index_002/_search
{
"query": {
"wildcard": {
"name": {
"value": "lone*"
}
}
}
}
全文查询
高级全文查询通常用于对全文字段 text
类型(比如电子邮件的正文)进行全文查询。全文查询在搜索和索引时,都会对字段进行分词处理,查询之前会先对输入的词进行分词处理,然后对每个词项进行查询,最后将结果进行合并,并根据算分结果将结果进行返回。
全文查询也包括很多种,在这里我们主要介绍 match
查询和 match_phrase
查询。
match 查询
match
查询是执行全文搜索的标准查询,包括模糊匹配选项。如下就是一个标准的 match
查询语句:
# 返回全部5条数据
POST index_002/_search
{
"query": {
"match": {
"name": "lonely wolf"
}
}
}
对比 term
查询:
# 没有满足条件的结果
POST index_002/_search
{
"query": {
"term": {
"name": "lonely wolf"
}
}
}
# 返回第一条数据
POST index_002/_search
{
"query": {
"term": {
"name.keyword": "lonely wolf"
}
}
}
根据上面几个查询的结果我们可以得出 term
查询和全文 match
查询的区别:
term
查询会将搜索关键字作为一个整体进行查询。match
查询会将搜索关键字进行分词,且分词后默认是or
的关系。
根据这两个结论,也可以很明显知道,一般不对 text
类型字段采用 term
查询,因为 text
类型字段会被分词索引,可能会导致无法被 term
查询匹配出结果。
再看下面这个例子,会返回第二和第三两条数据(分词后的搜索和顺序无关):
# 查询出最少匹配中3个词项的结果
POST index_002/_search
{
"query": {
"match": {
"name": {
"query": "hello wolf lonely",
"operator": "or",
"minimum_should_match": 3
}
}
}
}
match_phrase 查询
match_phrase
会将输入的搜索关键字作为一个短语进行查询,这点看来类似于 term
查询,但是 match_phrase
查询内嵌了一个参数 slot
用来定义短语中允许的空隙,默认是 0
表示中间不允许有其他词:
POST index_002/_search
{
"query": {
"match_phrase": {
"name": {
"query": "lonely wolf"
}
}
}
}
这条语句的结果就能查询出第一和第四条数据,注意,虽然第四条数据中的 lonely wolf
是大写字母开头,但是索引的时候会将其转为小写进行索引,所以也能查询出结果。
此时我们加入 slot=1
条件进行查询,表示允许短语之间存在一个间隙,所以此时能查询出第二条数据:
POST index_002/_search
{
"query": {
"match_phrase": {
"name": {
"query": "hello wolf lonely",
"slop": 1
}
}
}
}
总结
本文主要讲述了 Term
查询和全文查询中 match
查询的区别,总结起来主要有以下几点:
Term
查询对搜索关键字不会进行分词处理,而是作为一个整体进行查询。- 全文查询如
match
等查询,会对搜索关键字进行分词,并对每个词项进行搜索,默认or
的关系进行合并,并最终算法返回结果。 - 对
Text
类型字段,索引时会进行分词,大写字母会转成小写,所以如果用Term
或者match_phrase
查询时要注意因分词而对查询结果产生的影响。