大纲 版本说明 软件 版本 描述 ElasticSearch 7.4.2 ElasticSearch 的版本 Curl 7.29.0 Curl 的版本
ElasticSearch 导入数据 学习 ES 的时候,往往需要大量的测试数据,建议导入 ES 官方 GitHub 仓库中的 JSON 数据文件,这样方便后续学习 ES 的复杂查询。首先将 ES 官方提供的 JSON 数据保存到 accounts.json
文件里,或者直接从本站下载 数据文件,然后执行以下命令批量导入数据。
1 curl -X POST -H 'Content-Type:application/json' http://127.0.0.1:9200/bank/account/_bulk --data-binary @accounts.json
ElasticSearch 检索操作 Search API 的使用 ES 支持下述两种基本的检索方式,分别是 URI + 检索参数
与 URI + 请求体
。
第一种使用方式 使用 REST Request URI 发送检索参数(URI + 检索参数)。
检索示例 描述 GET bank/_search
检索 bank 索引下的所有信息,包括 type 和 docs GET bank/_search?q=*&sort=account_number:asc
请求参数方式检索
1 2 curl -X GET -H 'Content-Type:application/json' http://127.0.0.1:9200/bank/_search?q=*&sort=account_number:asc
第二种使用方式 使用 REST Request Body 来发送检索参数(URI + 请求体)。
1 2 curl -X POST -H 'Content-Type:application/json' http://127.0.0.1:9200/bank/_search -d '{"query":{"match_all":{}},"sort":[{"account_number":{"order":"asc"}}]}'
上述的 Curl 命令,POST 了一个 JSON 风格的查询请求体到 Search API,请求体的内容就是 Query DSL。值得一提的是,一旦搜索的结果被返回,Elasticsearch 就完成了这次请求,并且不会维护任何服务端的资源或者结果的 Cursor(游标)。
返回结果的说明 返回结果的字段 描述 took 执行搜索的耗时(毫秒) time_out 搜索是否超时 _shards 多少个分片被搜索了,以及统计了成功 / 失败的搜索分片 hits 搜索结果 hits.total 搜索结果统计 hits.hits 实际的搜索结果数组(默认只搜索前 10 条文档) hits.sort 结果的排序 Key(键),没有则按 score 排序 hits.score 相关性得分(全文检索用) max_score 相关性的最高得分(全文检索用)
Query DSL 的使用 Elasticsearch 提供了一个可以执行查询的 Json 风格的 DSL(Domain Specific Language - 领域特定语言),这个被称为 Query DSL。该查询语言非常全面,虽然刚开始使用的时候感觉有点复杂,但熟悉了之后会发现很实用。
语法格式 1 2 3 4 5 6 7 { QUERY_NAME: { ARGUMENT: VALUE, ARGUMENT: VALUE, ... } }
1 2 3 4 5 6 7 8 9 { QUERY_NAME: { FIELD_NAME: { ARGUMENT: VALUE, ARGUMENT: VALUE, ... } } }
简单查询 查询前 5 条记录,并按照 account_number
字段倒序排序 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 GET /bank/_search { "query" : { "match_all" : {} }, "from" : 0 , "size" : 5 , "sort" : [ { "account_number" : { "order" : "desc" } } ] }
参数名称 描述 query 定义如何查询 match_all 查询类型,代表查询所有的所有,ES 可以在 query
中组合非常多的查询类型以完成复杂查询 from 完成分页查询功能 size 完成分页查询功能 sort 排序,多字段排序,会在前序字段相等时使用后续字段排序,否则以前序为准
返回部分字段 查询前 5 条记录,并只返回 age
与 balance
字段。 1 2 3 4 5 6 7 8 9 10 11 12 GET /bank/_search { "query" : { "match_all" : {} }, "from" : 0 , "size" : 5 , "_source" : [ "age" , "balance" ] }
match 匹配查询 提示
使用 match
匹配字符串类型字段的时候,ES 会进行全文检索,并且每条记录都有相关性得分。 ES 进行全文检索时,底层使用的是倒排索引(Inverted Index)。 基本类型(非字符串),精确匹配。查询 account_number
是 20 的所有记录。 1 2 3 4 5 6 7 8 GET /bank/_search { "query" : { "match" : { "account_number" : 20 } } }
字符串类型,精确匹配(使用 keyword
关键字)。查询出 address
是 990 Mill Road
的所有记录,并给出相关性得分。 1 2 3 4 5 6 7 8 GET /bank/_search { "query" : { "match" : { "address.keyword" : "990 Mill Road" } } }
字符串类型,单个单词(全文检索)。查询出 address
字段中包含 mill
单词的所有记录,并给出相关性得分。 1 2 3 4 5 6 7 8 GET /bank/_search { "query" : { "match" : { "address" : "mill" } } }
字符串类型,多个单词(分词 + 全文检索)。查询出 address
字段中包含 mill
或者 road
或者 mill road
的所有记录,并给出相关性得分。 1 2 3 4 5 6 7 8 GET /bank/_search { "query" : { "match" : { "address" : "mill road" } } }
match_phrase 短语匹配 将需要匹配的值当成一个整体单词(不分词)进行检索
1 2 3 4 5 6 7 8 GET /bank/_search { "query" : { "match_phrase" : { "address" : "mill road" } } }
查出 address
字段中包含 mill road
的所有记录,不进行分词处理,并给出相关性得分。
multi_match 多字段匹配 1 2 3 4 5 6 7 8 9 10 11 12 GET /bank/_search { "query" : { "multi_match" : { "fields" : [ "state" , "address" ], "query" : "mill" } } }
查出 state
或者 address
字段中包含 mill
的所有记录。如果匹配的是包含多个单词的字符串(例如 mill road
),会进行分词处理,并给出相关性得分。
bool 复合查询 bool 用来做复合查询。复合语句可以合并任何其它查询语句,包括复合语句,了解这一点是很重要的。这就意味着,复合语句之间可以互相嵌套,可以表达非常复杂的逻辑。
类型 描述 must 子句 (查询) 必须出现在匹配的文档中,并将有助于得分。 filter 子句 (查询) 必须出现在匹配的文档中。然而,与 must 不同的是,查询的分数将被忽略。过滤器子句在过滤器上下文中执行,这意味着评分被忽略,子句被考虑用于缓存。 should 子句 (查询) 应该出现在匹配的文档中。在 bool 查询中不包含 must 或者 filter 子句时,一个或多个 should 子句必须有相匹配的文档。 must_not 子句 (查询) 不能出现在匹配的文档中。子句在过滤器上下文中执行,这意味着评分被忽略,子句被考虑用于缓存。因为评分会被忽略,所以会返回所有文档的评分为 0。
must 复合查询 must
复合查询,表示必须满足列举的所有条件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 GET /bank/_search { "query" : { "bool" : { "must" : [ { "match" : { "address" : "mill" } }, { "match" : { "gender" : "M" } } ] } } }
should 复合查询 should
复合查询,表示应该满足列举的条件。如果满足会增加相关性的评分,但不会改变查询的结果。如果 query
中只有 should
且只有一种匹配规则,那么 should
的条件就会被作为默认匹配条件而去改变查询结果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 GET /bank/_search { "query" : { "bool" : { "must" : [ { "match" : { "address" : "mill" } }, { "match" : { "gender" : "M" } } ], "should" : [ { "match" : { "address" : "lane" } } ] } } }
must_not 复合查询 must_not
复合查询,表示必须不满足列出的所有条件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 GET /bank/_search { "query" : { "bool" : { "must" : [ { "match" : { "address" : "mill" } }, { "match" : { "gender" : "M" } } ], "should" : [ { "match" : { "address" : "lane" } } ], "must_not" : [ { "match" : { "email" : "baluba.com" } } ] } } }
filter 复合查询 filter
复合查询,作用与 must
一样,可用于查询结果过滤。除此之外,filter
还可以用于忽略相关性得分。
filter
可以与 must
、should
、must_not
复合查询一起使用,此时的作用是查询结果过滤,会产生相关性得分。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 GET /bank/_search { "query" : { "bool" : { "must" : [ { "match" : { "address" : "mill" } } ], "filter" : { "range" : { "balance" : { "gte" : 10000 , "lte" : 20000 } } } } } }
filter
单独使用时,查询结果中不会产生相关性得分。值得一提的是,并不是所有的查询都需要产生相关性得分,特别是那些仅用于 filtering(过滤)
的文档。为了不计算得分,ES 会自动检查场景并且优化查询的执行。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 GET /bank/_search { "query" : { "bool" : { "filter" : { "range" : { "balance" : { "gte" : 10000 , "lte" : 20000 } } } } } }
term 匹配查询 term
的作用和 match
一样,用于匹配某个字段的值。全文检索字段(字符串类型)用 match
,其他非字符串类型字段的匹配用 term
。
1 2 3 4 5 6 7 8 GET /bank/_search { "query" : { "term" : { "account_number" : 20 } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 GET /bank/_search { "query" : { "bool" : { "must" : [ { "term" : { "age" : { "value" : 28 } } }, { "match" : { "address" : "990 Mill Road" } } ] } } }
aggregations 聚合查询 聚合查询提供了从数据中分组和提取数据的能力,最简单的聚合方法大致等于 SQL GROUP BY 和 SQL 聚合函数。在 ES 中,有执行搜索返回 hits
(命中结果),并且同时返回聚合结果,把一个响应中的所有 hits
(命中结果)分隔开的能力。这是非常强大且有效的,可以执行查询和多个聚合,并且在一次使用中得到各自的(任何一个的)返回结果,使用一次简洁和简化的 API 来避免网络往返。
1 2 3 4 5 6 7 "aggs": { "aggs_name": { // aggs_name - 聚合的名称,方便展示在结果集中 "agg_type": { // agg_type - 聚合的类型(avg、terms、max、min、sum 等等) } } }
使用案例一 查询 address
中包含 mill
的所有人的年龄分布以及平均年龄,但不显示这些人的详情。
提示
下述 Query DSL 中的 size:0
,表示不显示查询结果的数据,只显示聚合结果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 GET /bank/_search { "query" : { "match" : { "address" : "mill" } }, "aggs" : { "group_by_age" : { "terms" : { "field" : "age" } }, "avg_age" : { "avg" : { "field" : "age" } } }, "size" : 0 }
返回的聚合查询结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 { "took" : 1 , "timed_out" : false , "_shards" : { "total" : 1 , "successful" : 1 , "skipped" : 0 , "failed" : 0 }, "hits" : { "total" : { "value" : 4 , "relation" : "eq" }, "max_score" : null , "hits" : [] }, "aggregations" : { "avg_age" : { "value" : 34.0 }, "group_by_age" : { "doc_count_error_upper_bound" : 0 , "sum_other_doc_count" : 0 , "buckets" : [ { "key" : 38 , "doc_count" : 2 }, { "key" : 28 , "doc_count" : 1 }, { "key" : 32 , "doc_count" : 1 } ] } } }
使用案例二 查出所有年龄的分布,并且获取这些年龄段的这些人的平均薪资。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 GET /bank/_search { "query" : { "match_all" : {} }, "aggs" : { "age_avg" : { "terms" : { "field" : "age" , "size" : 1000 }, "aggs" : { "avg_banlance" : { "avg" : { "field" : "balance" } } } } }, "size" : 1000 }
使用案例三 查出所有年龄的分布,并且获取这些年龄段中男性与女性的平均薪资,以及这些年龄段的这些人的平均薪资。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 GET /bank/_search { "query" : { "match_all" : {} }, "aggs" : { "age_agg" : { "terms" : { "field" : "age" , "size" : 100 }, "aggs" : { "gender_agg" : { "terms" : { "field" : "gender.keyword" , "size" : 100 }, "aggs" : { "avg_banlance" : { "avg" : { "field" : "balance" } } } }, "avg_banlance" : { "avg" : { "field" : "balance" } } } } }, "size" : 1000 }