7、ElasticSearch 高级查询语法Query DSL实战


1. ES高级查询Query DSL

ES中提供了一种强大的检索数据方式,这种检索方式称之为Query DSL(Domain Specified Language领域专用语言) , Query DSL是利用Rest API传递JSON格式的请求体(RequestBody)数据与ES进行交互,这种方式的丰富查询语法让ES检索变得更强大,更简洁。

官方文档:https://www.elastic.co/guide/en/elasticsearch/reference/8.14/query-dsl.html

基本语法:

GET //_search {json请求体数据}


示例数据准备

DELETE /employeePUT /employee{  "settings": {    "number_of_shards": 1,    "number_of_replicas": 1  },  "mappings": {    "properties": {      "name": {        "type""keyword"      },      "sex": {        "type""integer"      },      "age": {        "type""integer"      },      "address": {        "type""text",        "analyzer""ik_max_word",        "fields": {          "keyword": {            "type""keyword"          }        }      },      "remark": {        "type""text",        "analyzer""ik_smart",        "fields": {          "keyword": {            "type""keyword"          }        }      }    }  }}POST /employee/_bulk{"index":{"_index":"employee","_id":"1"}}{"name":"张三","sex":1,"age":25,"address":"广州天河公园","remark":"java developer"}{"index":{"_index":"employee","_id":"2"}}{"name":"李四","sex":1,"age":28,"address":"广州荔湾大厦","remark":"java assistant"}{"index":{"_index":"employee","_id":"3"}}{"name":"王五","sex":0,"age":26,"address":"广州白云山公园","remark":"php developer"}{"index":{"_index":"employee","_id":"4"}}{"name":"赵六","sex":0,"age":22,"address":"长沙橘子洲","remark":"python assistant"}{"index":{"_index":"employee","_id":"5"}}{"name":"张龙","sex":0,"age":19,"address":"长沙麓谷企业广场","remark":"java architect assistant"}{"index":{"_index":"employee","_id":"6"}}{"name":"赵虎","sex":1,"age":32,"address":"长沙麓谷兴工国际产业园","remark":"java architect"}



1.1 match_all ——匹配所有文档

match_all查询是一个特殊的查询类型,它用于匹配索引中的所有文档,而不考虑任何特定的查询条件。

基本语法

GET //_search{ "query": {    "match_all": {} }}



高级用法

例如,如果您想要返回索引中的前10个文档,并且按照文档的评分进行排序,您可以使用以下查询:

GET //_search{ "query": {    "match_all": {} }, "size": 10, "sort": [    {"_score": {"order""desc"}} ]}



_source的用法

#不查看源数据,仅查看元字段GET //_search{  "query": {    "match_all": {}  },  "_source"false}# 返回指定字段GET //_search{  "query": {    "match_all": {}  },  "_source": ["field1","field2"]}#只看以obj.开头的字段GET //_search{  "query": {    "match_all": {}  },  "_source""obj.*"}


示例

  • size返回指定条数
GET /employee/_search{  "query": {    "match_all": {}  },  "size": 3  }


  • from&size分页查询
GET /employee/_search{  "query": {    "match_all": {}  },  "from": 0,  "size": 5  }


  • sort指定字段排序
# 根据age排序GET /employee/_search{  "query": {    "match_all": {}  },  "sort": [    {      "age""desc"    }  ]}# 排序的同时进行分页GET /employee/_search{  "query": {    "match_all": {}  },  "sort": [    {      "age""desc"    }  ],  "from"2,  "size"5}


  • _source返回源数据
GET /employee/_search{  "query": {    "match_all": {}  },  "_source": ["name","address"]}


1.2 精确匹配

精确匹配是指的是搜索内容不经过文本分析直接用于文本匹配,这个过程类似于数据库的SQL查询,搜索的对象大多是索引的非text类型字段。此类检索主要应用于结构化数据,如ID、状态和标签等。

term——单字段精确匹配查询

term检索主要应用于单字段精准匹配的场景。在实战过程中,需要避免将term检索应用于text类型的检索。进一步说,term检索针对的是非text类型,用于text类型时并不会报错,但检索结果一般会达不到预期。

基本语法

在Elasticsearch 8.x中,term查询用于执行精确匹配查询,它适用于未经过分词处理的keyword字段类型。term查询的基本语法如下:

GET /{index_name}/_search{ "query": {    "term": {      "{field.keyword}": {        "value""your_exact_value"      }    } }}


这里的{index_name}是你要查询的索引名称,{field.keyword}是你要匹配的字段名称,.keyword后缀表示该字段是一个keyword类型,用于存储精确匹配的数据。"value"是你要精确匹配的值。

示例

对bool,日期,数字,结构化的文本可以利用term做精确匹配

# 查询姓名为张三的员工信息GET /employee/_search{  "query": {    "term": {      "name": {        "value""张三"      }    }  }}


注意:最好不要在term查询的字段中使用text字段,因为text字段会被分词,这样做既没有意义,还很有可能什么也查不到。

# 思考: 查询广州白云是否有数据,为什么?GET /employee/_search{  "query":{    "term": {      "address": {        "value": "广州白云"      }    }  }}# 采用term精确查询, 查询字段映射类型为keywordGET /employee/_search{  "query":{    "term": {      "address.keyword": {        "value": "广州白云山公园"      }    }  }}


term处理多值字段(数组)时,term查询是包含,不是等于。

POST /people/_bulk{"index":{"_id":1}}{"name":"小明","interest":["跑步","篮球"]}{"index":{"_id":2}}{"name":"小红","interest":["跳舞","画画"]}{"index":{"_id":3}}{"name":"小丽","interest":["跳舞","唱歌","跑步"]}POST /people/_search{  "query": {    "term": {      "interest.keyword": {        "value""跑步"      }    }  }}


在ES中,Term查询,对输入不做分词。会将输入作为一个整体,在倒排索引中查找准确的词项,并且使用相关度算分公式为每个包含该词项的文档进行相关度算分。

可以通过 Constant Score 将查询转换成一个 Filtering,避免算分,并利用缓存,提高性能。

  • 将Query 转成 Filter,忽略TF-IDF计算,避免相关性算分的开销
  • Filter可以有效利用缓存
GET /employee/_search{  "query": {    "constant_score": {      "filter": {        "term": {          "address.keyword": "广州白云山公园"        }      }    }  }}


terms——多值精确匹配

terms检索主要应用于多值精准匹配场景,它允许用户在单个查询中指定多个词条来进行精确匹配。这种查询方式适合从文档中查找包含多个特定值的字段,例如筛选出具有多个特定标签或状态的项目。而terms检索是针对未分析的字段进行精确匹配的,因此它在处理关键词、数字、日期等结构化数据时表现良好。

基本语法

在Elasticsearch 8.x中,进行多字段精确匹配时,可以使用terms查询。terms查询允许你指定一个字段,并匹配该字段中的多个精确值。

基本语法如下:

GET //_search{ "query": {    "terms": {      "": [        "value1",        "value2",        "value3",        ...      ]    } }}

是你想要查询的索引名称。

是你想要对其执行terms查询的字段名。

方括号内的值列表是你希望在查询中匹配的字段值。


示例

POST /employee/_search{  "query": {    "terms": {      "remark.keyword": ["java assistant""java architect"]    }  }}


range——范围查询

range检索是Elasticsearch中一种针对指定字段值在给定范围内的文档的检索类型。这种查询适合对数字、日期或其他可排序数据类型的字段进行范围筛选。range检索支持多种比较操作符,如大于(gt)、大于等于(gte)、小于(lt)和小于等于(lte)等,可以实现灵活的区间查询。

基本语法

在Elasticsearch 8.x版本中,range查询的基本语法如下:

GET //_search{ "query": {    "range": {      "": {        "gte": ,        "lte": ,        "gt": ,        "lt":       }    } }}

是你想要查询的索引名称。

是你想要对其执行range查询的字段名。

gte 表示大于或等于(Greater Than or Equal)。

lte 表示小于或等于(Less Than or Equal)。

gt 表示严格大于(Greater Than)。

lt 表示严格小于(Less Than)。

, , , 是指定的数值边界。



示例

  • 查询年龄在25到28的员工
POST /employee/_search{  "query": {    "range": {      "age": {        "gte": 25,        "lte": 28      }    }  }}


  • 日期范围查询

1) 生成测试数据

假设我们正在创建一个笔记应用,每条笔记都有一个创建日期。

PUT /notes{  "settings": {    "number_of_shards": 1,    "number_of_replicas": 0  },  "mappings": {    "properties": {      "title": {"type""text"},      "content": {"type""text"},      "created_at": {"type""date""format""yyyy-MM-dd HH:mm:ss"}    }  }}POST /notes/_bulk{"index":{"_id":"1"}}{"title":"Note 1","content":"This is the first note.","created_at":"2023-07-01 12:00:00"}{"index":{"_id":"2"}}{"title":"Note 2","content":"This is the second note.","created_at":"2023-07-05 15:30:00"}{"index":{"_id":"3"}}{"title":"Note 3","content":"This is the third note.","created_at":"2023-07-10 08:45:00"}{"index":{"_id":"4"}}{"title":"Note 4","content":"This is the fourth note.","created_at":"2023-07-15 20:15:00"}


2)使用range查询来查找在特定日期范围内的笔记。

假设我们想找出在2023年7月5日和2023年7月10日之间的所有笔记。

POST /notes/_search{  "query": {    "range": {      "created_at": {        "gte""2023-07-05 00:00:00",        "lte""2023-07-10 23:59:59"      }    }  }}


Elasticsearch支持日期数学表达式,允许在查询和聚合中使用相对时间点。以下是一些常见的日期数学表达式的示例和解释:

  • now:当前时间点。
  • now-1d:从当前时间点向前推1天的时间点。
  • now-1w:从当前时间点向前推1周的时间点。
  • now-1M:从当前时间点向前推1个月的时间点。
  • now-1y:从当前时间点向前推1年的时间点。
  • now+1h:从当前时间点向后推1小时的时间点。
POST /product/_bulk{"index":{"_id":1}}{"price":100,"date":"2023-01-01","productId":"XHDK-1293"}{"index":{"_id":2}}{"price":200,"date":"2022-01-01","productId":"KDKE-5421"}# 返回所有在当前时间点前两年内的产品文档GET /product/_search{  "query": {    "range": {      "date": {        "gte""now-2y"      }    }  }}


exists——是否存在查询

exists检索在Elasticsearch中用于筛选具有特定字段值的文档。这种查询类型适用于检查文档中是否存在某个字段,或者该字段是否包含非空值。通过使用exists检索,你可以有效地过滤掉缺少关键信息的文档,从而专注于包含所需数据的结果。应用场景包括但不限于数据完整性检查、查询特定属性的文档以及对可选字段进行筛选等。

基本语法

GET //_search{ "query": {    "exists": {      "field""missing_field"    } }}


示例

查询索引库中存在remark字段的文档

GET /employee/_search{  "query": {    "exists"    {      "field""remark"    }  }}


ids——根据一组id查询

IDs检索也是一种常用的Elasticsearch查询方法,它允许我们基于给定的ID组快速召回相关数据,从而实现高效的文档检索。

基本语法

在Elasticsearch 8.x中,ids查询用于返回具有指定ID列表的文档。这个查询是检索特定文档的有效方式,特别是当你已经知道具体的文档ID时。

基本语法如下:

GET //_search{ "query": {    "ids": {      "values": ["id1""id2""id3", ...]    } }}


示例

GET /employee/_search{  "query": {    "ids": {      "values": [1,2]    }  }}


prefix——前缀匹配

prefix会对分词后的term进行前缀搜索。

  • 它不会对要搜索的字符串分词
    ,传入的前缀就是想要查找的前缀
  • 默认状态下,前缀查询不做相关性分数计算,它只是将所有匹配的文档返回,然后赋予所有相关分数值为1。

prefix的原理:

需要遍历所有倒排索引,并比较每个词项是否以所搜索的前缀开头。

基本语法

在Elasticsearch 8.x中,prefix查询用于搜索那些在指定字段中以特定前缀开始的文档。这种查询通常用于自动补全或搜索功能,其中用户输入的搜索词可能是更长文本的一部分。

基本语法如下:

GET //_search{ "query": {    "prefix": {      "your_field_name": {        "value""your_prefix_string"      }    } }}


需要注意的是,这种查询方式仅适用于关键字类型(keyword)的字段。

示例

# 思考:能否查到数据?GET /employee/_search{  "query": {    "prefix": {      "address": {        "value": "广州白云山"      }    }  }}GET /employee/_search{  "query": {    "prefix": {      "address.keyword": {        "value": "广州白云山"      }    }  }}


wildcard——通配符匹配

wildcard检索是Elasticsearch中一种支持通配符匹配的查询类型,它允许在检索时使用通配符表达式来匹配文档的字段值。通配符包括两种。

  • 星号(*):表示零或多个字符,可用于匹配任意长度的字符串。
  • 问号(?):表示一个字符,用于匹配任意单个字符。

wildcard检索适用于对部分已知内容的文本字段进行模糊检索。例如,在文件名或产品型号等具有一定规律的字段中,使用通配符检索可以方便地找到满足特定模式的文档。

请注意,通配符查询可能会导致较高的计算负担,因此在实际应用中应谨慎使用,尤其是在涉及大量文档的情况下。

基本语法

基本语法如下:

GET //_search{ "query": {    "wildcard": {      "your_field_name": {        "value""your_search_pattern"      }    } }}


示例

GET /employee/_search{  "query": {    "wildcard": {      "address.keyword": {        "value""*州*公园"      }    }  }}


regexp——正则匹配查询

regexp检索是一种基于正则表达式的检索方法。虽然该检索方式的功能强大,但建议在非必要情况下避免使用,以保持查询性能的高效和稳定。

基本语法

在Elasticsearch 8.x中,regexp 查询用于在字段中执行正则表达式匹配。这个查询可以用来搜索满足特定模式的文本,并且比 wildcard 查询更加灵活和强大。

基本语法如下:

GET //_search{ "query": {    "regexp": {      "your_field_name": {        "value""your_search_pattern"      }    } }}


示例

GET /employee/_search{  "query": {    "regexp": {      "remark": {        "value""java.*"      }    }  }}

.* 表示在java后可以跟随任意数量的任意字符


fuzzy——支持编辑距离的模糊查询

fuzzy检索是一种强大的搜索功能,它能够在用户输入内容存在拼写错误或上下文不一致时,仍然返回与搜索词相似的文档。通过使用编辑距离算法来度量输入词与文档中词条的相似程度,模糊查询在保证搜索结果相关性的同时,有效地提高了搜索容错能力。

编辑距离是指从一个单词转换到另一个单词需要编辑单字符的次数。如中文集团到中威集团编辑距离就是1,只需要修改一个字符;如果fuzziness值在这里设置成2,会把编辑距离为2的东东集团也查出来。

基本语法

基本语法如下:

GET //_search{  "query": {    "fuzzy": {      "your_field": {        "value""search_term",        "fuzziness""AUTO",        "prefix_length": 1      }    }  }}


  • fuzziness参数用于编辑距离的设置,其默认值为AUTO,支持的数值为[0,1,2]。如果值设置越界会报错。
  • prefix_length: 搜索词的前缀长度,在此长度内不会应用模糊匹配。默认是0,即整个词都会被模糊匹配。

示例

GET /employee/_search{  "query": {    "fuzzy": {      "address": {        "value""白运山",        "fuzziness": 1        }    }  }}



term set——用于解决多值字段中的文档匹配问题

terms set检索是Elasticsearch中一种功能强大的检索类型,主要用于解决多值字段中的文档匹配问题,在处理具有多个属性、分类或标签的复杂数据时非常有用。

从应用场景来说,terms set检索在处理多值字段和特定匹配条件时具有很大的优势。它适用于标签系统、搜索引擎、电子商务系统、文档管理系统和技能匹配等场景。

基本语法

terms_set可以检索至少匹配一定数量给定词项的文档,其中匹配的数量可以是固定值,也可以是基于另一个字段的动态值

基本语法如下:

GET //_search{ "query": {    "terms_set": {      "": {        "terms": ["""", ...],        "minimum_should_match_field""" or        "minimum_should_match_script": {          "source""