# 查询
本文记录 ES document 的几种查询操作。
# 简单查询
ES 支持通过 GET 请求进行简单查询。
- 相关 API :
GET /<index>/_doc/_search # 查询指定索引下的文档 GET /<index>/_search # 可以省略 _doc/ GET /_search # 查询所有索引下的文档 GET /_search?q=name:Leo # 可以在 URL 的查询字符串中加入 q 字段及查询参数
- 相关 API :
对 ES 发出查询请求之后,收到的响应内容格式如下:
[[email protected] ~]# curl -X GET 127.0.0.1:9200/student/_search?pretty { "took" : 14, # 本次查询的耗时,单位为 ms "timed_out" : false, # 该请求的处理过程是否超时 "_shards" : { "total" : 1, # 总共查询了多少个分片 "successful" : 1, # 有多少个分片成功了 "skipped" : 0, # 有多少个分片跳过了 "failed" : 0 # 有多少个分片失败了 }, "hits" : { "total" : { "value" : 8, # 查询条件匹配的文档数量(但实际返回的文档数可能受限制,更小) "relation" : "eq" # 这些文档与查询条件的关系 }, "max_score" : 1.0, # 这些文档的最大相关性 "hits" : [ # hits 字典中的 hits 列表包含了 ES 实际返回给客户端的文档 { "_index" : "student", "_type" : "_doc", "_id" : "ZhzMiXABzduhqPWX7mUX", "_score" : 1.0, # 一个正浮点数,表示该文档与查询条件的相关性 "_source" : { "name" : "Leo", "age" : 10, "interests" : [ "sports", "music" ] } }, ...
- ES 默认将 hits 列表中的各个文档按 _score 的值进行排序。
# DSL 查询
ES 提供了一种 DSL 查询语法,可以实现复杂的查询。
- 它需要在请求报文 body 中加入 JSON 格式的查询参数。
- 相关 API :
GET /<index>/_search [request_body] GET /_search [request_body] POST /<index>/_delete_by_query [request_body] # 删除与 DSL 查询条件匹配的文档 ?proceed=proceed # 如果遇到文档 version conflict ,默认动作为 abort ,建议改为 proceed ?scroll_size=1000 # 每个 batch 大小,默认为 1000 。增加该值可以些许提高删除速度 ?wait_for_completion=false # 如果操作耗时过久,建议改为后台执行的 task POST /<index>/_update_by_query [request_body] # 修改与 DSL 查询条件匹配的文档
- 可以使用 GET 或 POST 方法。
- 如果省略 request_body ,则会匹配所有文档,相当于简单查询。
例:查询并修改文档
POST /my-index-1/_update_by_query { "query": { "wildcard": { "ip":{ "value": "10.0.0.*" } } }, "script": { "source": "ctx._source['hostname'] = 'CentOS'; ctx._source['release'] = '7'" } }
# match
:用于根据字段的值查询文档。
- 例:
GET /student/_search { "query" : { "match" : { "name" : { # 指定查询的目标字段 "query": "Leo" # 指定查询字符串,找出目标字段包含 query_string 的文档 # "operator": "or", # 设置当 query_string 被分词之后,几个词之间的逻辑关系。可以取值为 or 或 and # "fuzziness": 0, # "max_expansions": 50, } } }, # "_source": { # 让返回的文档中只包含指定的字段 # "includes": [ # "filed1", # "filed2" # ], # "excludes": [ # "filed3" # ] # } }
- 没有其它查询参数时,可以简写为:
"match" : { "name" : "Leo" }
- 查询的字段可以是以下形式:
"age" : 10 # 字段的值可以是 string、number、boolean 等类型 "age" : "10" # number 类型的值也可以写作 string 形式 "host.os.platform" : "centos" # 字段名可以通过 . 引用子字段
- 没有其它查询参数时,可以简写为:
- 常见的几种 match 子句:
"match" : { "name" : "A B" } # 分词之后,查询存在这些词语之一的文档。这里是查询 name 包含 A 或 B 的文档
"match_phrase" : { "name" : "A B" } # 分词之后,查询同时存在这些词语的文档,且词语的先后顺序一致
"match_all" : { } # 匹配所有文档
"multi_match" : { "fields": [ "name", "nickname", "*_name" ] # 指定多个字段进行 match 查询 "query": "Leo", }
- 如果 query_string 包含保留字符
+ - = && || > < ! ( ) { } [ ] ^ " ~ * ? : \ /
,则需要用\
转义。采用 JSON 格式时需要用\\
转义。< >
总数不支持转义。
# term
:用于进行精确查询。
- 例:
GET /_search { "query": { "term": { "name": { "value": "Leo" # 字段值必须等于它 # "boost": 1.0, # 控制相关性分数 _score 。默认取值为 1.0 ,取值小于 1 会降低得分,取值大于 1 会增加得分 } } } }
# exists
:用于查询存在指定字段的文档。
- 例:
GET /_search { "query": { "exists": { "field": "name" } } }
# range
:用于查询字段值在指定范围内的文档。
- 例:
GET /_search { "query": { "range": { "age": { "gt": 10, # 大于 "lt": 20, # 小于 # "gte" : 20, # 大于等于 # "lte" : 20, # 小于等于 } } } }
# prefix
:用于进行前缀查询。
- 只能查询字符串类型的字段。
- 例:
GET /_search { "query": { "prefix": { "name": { "value": "Leo" # 字段值需要以该前缀开头 } } } }
# wildcard
:用于进行通配符查询。
- 可以使用通配符 ? 和 * 。
- 例:
GET /_search { "query": { "wildcard": { "name": { "value": "Leo*" # 字段值需要匹配该 pattern } } } }
- 用于查询的 pattern 应该避免以通配符开头,否则会大幅增加查询的开销。
# regexp
:用于进行正则查询。
- 例:
GET /_search { "query": { "regexp": { "name": { "value": "Leo.*" } } } }
- text 类型的字段在存储时会被分词,因此需要某个词语匹配正则表达式。
# fuzzy
:用于进行模糊查询。
- 模糊查询时,会将查询的值修改几个字符,生成一些变体,然后分别尝试 match 查询。
- 修改的字符数称为编辑距离。
- 每个字符区分大小写。
- 比如编辑距离为 1 时, "Leo" 可以生成以下变体:
leo # 替换一个字符 Le # 移除一个字符 Lego # 在任意位置插入一个字符 Loe # 交换两个相邻字符的位置
- 例:
GET /_search { "query": { "fuzzy": { "name": { "value": "Leo" # 字段值需要与它模糊匹配 # "fuzziness": "AUTO", # 允许的最大编辑距离 # "max_expansions": 50, # 允许的最大变体数 } } } }
# sort
:用于控制文档的排序。
- 例:
GET /_search { "query": { "match_all": {} # 匹配所有文档 }, "sort": [{ # 返回的文档按 age 的值升序排列 "age": "asc" }], "from": 5, # 返回从编号 5 开始的文档(从 0 开始编号) "size": 10, # 返回文档的最大数量(不能超过 max_result_window ),默认为 10 。比如查询命中 1000 个文档,只返回 10 个文档 # "track_total_hits": 10000 # ES v7.0 新增的选项,默认当查询命中 10000 个文档时就返回。将该选项设置为 true ,则会返回所有命中的文档 }
# filter
:用于添加过滤器,使 ES 只返回过滤后的文档。
- 例:
GET /_search { "query": { "bool": { "must": { "match_all": {} }, "filter": { "match": { "name": "Leo" } } } } }
query
参数中的查询子句称为“查询上下文”,filter
参数中的查询子句称为 “过滤器上下文” 。- ES 只会返回同时匹配 “查询上下文” 和 “过滤器上下文” 的文档,不过文档的 _score 的值只由 “查询上下文” 决定,不受 “过滤器上下文” 影响。
# bool
:用于进行布尔查询。
- 例:
GET /_search { "query": { "bool": { "must": [{ "range": { "age": { "gte": 10, "lte": 20 } } }, { "bool": { "should": [{ "match": { "name": "A" } }, { "match": { "name": "B" } } ], "minimum_should_match": 1 } } ], "must_not": [{ "match": { "age": "12" } }] } } }
- match、exists、wildcard 等查询子句一次只能查询一个字段,查询多个字段时需要通过 bool 查询组合起来。
- bool 查询中可以组合使用以下查询子句:
must
子句是一个列表,文档必须符合其中列出的所有条件。must_not
子句是一个列表,文档必须不符合其中列出的所有条件。should
子句是一个列表,文档应该符合其中列出的条件。不能写在与 must 同一层级的位置,否则会失效。filter
子句
minimum_should_match
表示文档至少应该符合should
列表中的几个条件。- 默认值为 1 。
- 可以设置成任意正整数,比如 10 。不过实际生效的值不会超过
should
条件总数 n 。 - 可以设置成任意负整数,比如 -2 ,这表示实际生效的值等于 n-2 。
- 可以设置成百分比格式,比如 75% 、-25% ,这会向下取整。
# 聚合查询
:用于在查询时进行一些额外操作。
- 聚合操作分为三类:
- Metric
- Bucket
- Pipeline
- 聚合查询中可以包含多个聚合操作。
- Metric、Bucket 聚合中支持声明嵌套的子聚合查询。但 Pipeline 聚合不支持。
# Metric
:指标聚合。用于统计 sum、avg、max、min 等指标。
- 例:统计 avg响应示例:
GET /_search { # "query": {}, # "size": 0, # 可以指定 size 为 0 ,使其不返回 query 的查询结果,只返回聚合结果 "aggs": { # 启用聚合查询 "my_agg_1": { # 声明一个聚合操作,自定义名称 "avg": { "field": "log.offset" } } } }
{ "took" : 1, "hits" : { ... }, "aggregations" : { "my_agg_1" : { "value" : 433553217 } } }
# Bucket
:桶聚合。基于字段值等条件,将查询到的文档分为多个组,称为桶。
- 例:terms 聚合,是根据指定字段的非重复值来分组
GET /_search { "aggs": { "my_agg_1": { "terms": { "field": "product", # "size": 10, # 限制最多返回的 bucket 数,默认为 10 # "min_doc_count": 1, # 每个 bucket 至少包含的文档数,不满足则不返回 # "order": { "_count": "asc" }, # bucket 的排序方式 # "show_term_doc_count_error": true # 是否在聚合结果中显示 sum_other_doc_count ,表示除了当前 bucket 以外的文档总数,包括分组失败的、bucket 排名超过 size 的 } } } }
# Pipeline
:管道聚合。用于处理其它聚合操作的输出。
- 例:avg_bucket 聚合,是统计一些 bucket 的平均值
POST /_search { "aggs": { "my_agg_1": { "date_histogram": { "field": "date", "calendar_interval": "month" }, "aggs": { "my_agg_2": { "sum": { "field": "price" } } } }, "my_agg_3": { "avg_bucket": { "buckets_path": "my_agg_1>my_agg_2" # 指定目标 bucket 的路径 } } } }