当前位置:   article > 正文

【大数据】es ElasticSearch常用操作:查询与聚合篇_es查询 text聚合成数组

es查询 text聚合成数组

0 说明

基于es 5.4和es 5.6,列举的是个人工作中经常用到的查询(只是工作中使用的是Java API),如果需要看完整的,可以参考官方相关文档
https://www.elastic.co/guide/en/elasticsearch/reference/5.4/search.html

1 查询

先使用一个快速入门来引入,然后后面列出的各种查询都是用得比较多的(在我的工作环境是这样),其它没怎么用的这里就不列出了。

1.1 快速入门

1.1.1 查询全部

  1. GET index/type/_search
  2. {
  3. "query":{
  4. "match_all":{}
  5. }
  6. }

GET index/type/_search

1.1.2 分页(以term为例)

  1. GET index/type/_search
  2. {
  3. "from":0,
  4. "size":100,
  5. "query":{
  6. "term":{
  7. "area":"GuangZhou"
  8. }
  9. }
  10. }

1.1.3 包含指定字段(以term为例)

  1. GET index/type/_search
  2. {
  3. "_source":["hobby", "name"],
  4. "query":{
  5. "term":{
  6. "area":"GuangZhou"
  7. }
  8. }
  9. }

1.1.4 排序(以term为例)

单个字段排序:

  1. GET index/type/_search
  2. {
  3. "query":{
  4. "term":{
  5. "area":"GuangZhou"
  6. }
  7. },
  8. "sort":[
  9. {"user_id":{"order":"asc"}},
  10. {"salary":{"order":"desc"}}
  11. ]
  12. }

1.2 全文查询

查询字段会被索引和分析,在执行之前将每个字段的分词器(或搜索分词器)应用于查询字符串。

1.2.1 match query

  1. {
  2. "query": {
  3. "match": {
  4. "content": {
  5. "query": "里皮恒大",
  6. "operator": "and"
  7. }
  8. }
  9. }
  10. }

operator默认是or,也就是说,“里皮恒大”被分词为“里皮”和“恒大”,只要content中出现两个之一,都会搜索到;设置为and之后,只有同时出现都会被搜索到。

1.2.2 match_phrase query

文档同时满足下面两个条件才会被搜索到:

  • (1)分词后所有词项都要出现在该字段中
  • (2)字段中的词项顺序要一致
  1. {
  2. "query": {
  3. "match_phrase": {
  4. "content": "里皮恒大"
  5. }
  6. }
  7. }

1.3 词项查询

词项搜索时对倒排索引中存储的词项进行精确匹配,词项级别的查询通过用于结构化数据,如数字、日期和枚举类型。

1.3.1 term query

  1. {
  2. "query": {
  3. "term": {
  4. "postdate": "2015-12-10 00:41:00"
  5. }
  6. }
  7. }

1.3.2 terms query

term的升级版,如上面查询的postdate字段,可以设置多个。

  1. {
  2. "query": {
  3. "terms": {
  4. "postdate": [
  5. "2015-12-10 00:41:00",
  6. "2016-02-01 01:39:00"
  7. ]
  8. }
  9. }
  10. }

因为term是精确匹配,所以不要问,[]中的关系怎么设置and?这怎么可能,既然是精确匹配,一个字段也不可能有两个不同的值。

1.3.3 range query

匹配某一范围内的数据型、日期类型或者字符串型字段的文档,注意只能查询一个字段,不能作用在多个字段上。

数值:

  1. {
  2. "query": {
  3. "range": {
  4. "reply": {
  5. "gte": 245,
  6. "lte": 250
  7. }
  8. }
  9. }
  10. }

支持的操作符如下:

gt:大于,gte:大于等于,lt:小于,lte:小于等于

日期:

  1. {
  2. "query": {
  3. "range": {
  4. "postdate": {
  5. "gte": "2016-09-01 00:00:00",
  6. "lte": "2016-09-30 23:59:59",
  7. "format": "yyyy-MM-dd HH:mm:ss"
  8. }
  9. }
  10. }
  11. }

format不加也行,如果写的时间格式正确。

1.3.4 exists query

返回对应字段中至少有一个非空值的文档,也就是说,该字段有值(待会会说明这个概念)。

  1. {
  2. "query": {
  3. "exists": {
  4. "field": "user"
  5. }
  6. }
  7. }

参考《从Lucene到Elasticsearch:全文检索实战》中的说明。

以下文档会匹配上面的查询:

文档说明
{"user":"jane"}有user字段,且不为空
{"user":""}有user字段,值为空字符串
{"user":"-"}有user字段,值不为空
{"user":["jane"]}有user字段,值不为空
{"user":["jane",null]}有user字段,至少一个值不为空即可

下面的文档不会被匹配:

文档说明
{"user":null}虽然有user字段,但是值为空
{"user":[]}虽然有user字段,但是值为空
{"user":[null]}虽然有user字段,但是值为空
{"foo":"bar"}没有user字段

1.3.5 ids query

查询具有指定id的文档。

  1. {
  2. "query": {
  3. "ids": {
  4. "type": "news",
  5. "values": "2101"
  6. }
  7. }
  8. }

类型是可选的,也可以以数据的方式指定多个id。

  1. {
  2. "query": {
  3. "ids": {
  4. "values": [
  5. "2101",
  6. "2301"
  7. ]
  8. }
  9. }
  10. }

1.4 复合查询

1.4.1 bool query

因为工作中接触到关于es是做聚合、统计、分类的项目,经常要做各种复杂的多条件查询,所以实际上,bool query用得非常多,因为查询条件个数不定,所以处理的逻辑思路时,外层用一个大的bool query来进行承载。(当然,项目中是使用其Java API)

bool query可以组合任意多个简单查询,各个简单查询之间的逻辑表示如下:

属性说明
must文档必须匹配must选项下的查询条件,相当于逻辑运算的AND
should文档可以匹配should选项下的查询条件,也可以不匹配,相当于逻辑运算的OR
must_not与must相反,匹配该选项下的查询条件的文档不会被返回
filter和must一样,匹配filter选项下的查询条件的文档才会被返回,但是filter不评分,只起到过滤功能

一个例子如下:

  1. {
  2. "query": {
  3. "bool": {
  4. "must": {
  5. "match": {
  6. "content": "里皮"
  7. }
  8. },
  9. "must_not": {
  10. "match": {
  11. "content": "中超"
  12. }
  13. }
  14. }
  15. }
  16. }

需要注意的是,同一个bool下,只能有一个must、must_not、should和filter。

如果希望有多个must时,比如希望同时匹配"里皮"和"中超",但是又故意分开这两个关键词(因为事实上,一个must,然后使用match,并且operator为and就可以达到目的),怎么操作?注意must下使用数组,然后里面多个match对象就可以了:

  1. {
  2. "size": 1,
  3. "query": {
  4. "bool": {
  5. "must": [
  6. {
  7. "match": {
  8. "content": "里皮"
  9. }
  10. },
  11. {
  12. "match": {
  13. "content": "恒大"
  14. }
  15. }
  16. ]
  17. }
  18. },
  19. "sort": [
  20. {
  21. "id": {
  22. "order": "desc"
  23. }
  24. }
  25. ]
  26. }

当然must下的数组也可以是多个bool查询条件,以进行更加复杂的查询。

上面的查询等价于:

  1. {
  2. "query": {
  3. "bool": {
  4. "must": {
  5. "match": {
  6. "content": {
  7. "query": "里皮恒大",
  8. "operator": "and"
  9. }
  10. }
  11. }
  12. }
  13. },
  14. "sort": [
  15. {
  16. "id": {
  17. "order": "desc"
  18. }
  19. }
  20. ]
  21. }

1.5 嵌套查询

先添加下面一个索引:

  1. PUT /my_index
  2. {
  3. "mappings": {
  4. "my_type": {
  5. "properties": {
  6. "user":{
  7. "type": "nested",
  8. "properties": {
  9. "first":{"type":"keyword"},
  10. "last":{"type":"keyword"}
  11. }
  12. },
  13. "group":{
  14. "type": "keyword"
  15. }
  16. }
  17. }
  18. }
  19. }

添加数据:

  1. PUT my_index/my_type/1
  2. {
  3. "group":"GuangZhou",
  4. "user":[
  5. {
  6. "first":"John",
  7. "last":"Smith"
  8. },
  9. {
  10. "first":"Alice",
  11. "last":"White"
  12. }
  13. ]
  14. }
  15. PUT my_index/my_type/2
  16. {
  17. "group":"QingYuan",
  18. "user":[
  19. {
  20. "first":"Li",
  21. "last":"Wang"
  22. },
  23. {
  24. "first":"Yonghao",
  25. "last":"Ye"
  26. }
  27. ]
  28. }

查询:

较简单的查询:

  1. {
  2. "query": {
  3. "nested": {
  4. "path": "user",
  5. "query": {
  6. "term": {
  7. "user.first": "John"
  8. }
  9. }
  10. }
  11. }
  12. }

较复杂的查询:

  1. {
  2. "query": {
  3. "bool": {
  4. "must": [
  5. {"nested": {
  6. "path": "user",
  7. "query": {
  8. "term": {
  9. "user.first": {
  10. "value": "Li"
  11. }
  12. }
  13. }
  14. }},
  15. {
  16. "nested": {
  17. "path": "user",
  18. "query": {
  19. "term": {
  20. "user.last": {
  21. "value": "Wang"
  22. }
  23. }
  24. }
  25. }
  26. }
  27. ]
  28. }
  29. }
  30. }

1.6 补充:数组查询与测试

添加一个索引:

  1. PUT my_index2
  2. {
  3. "mappings": {
  4. "my_type2":{
  5. "properties": {
  6. "message":{
  7. "type": "text"
  8. },
  9. "keywords":{
  10. "type": "keyword"
  11. }
  12. }
  13. }
  14. }
  15. }

添加数据:

  1. PUT /my_index2/my_type/1
  2. {
  3. "message":"keywords test1",
  4. "keywords":["美女","动漫","电影"]
  5. }
  6. PUT /my_index2/my_type/2
  7. {
  8. "message":"keywords test2",
  9. "keywords":["电影","美妆","广告"]
  10. }

搜索:

  1. {
  2. "query": {
  3. "term": {
  4. "keywords": "广告"
  5. }
  6. }
  7. }

Note1:注意设置字段类型时,keywords设置为keyword,所以使用term查询可以精确匹配,但设置为text,则不一定——如果有添加中文分词器,则可以搜索到;如果没有,而是使用默认的分词器,只是将其分为一个一个的字,就不会被搜索到。这点尤其需要注意到。

Note2:对于数组字段,也是可以做桶聚合的,做桶聚合的时候,其每一个值都会作为一个值去进行分组,而不是整个数组进行分组,可以使用上面的进行测试,不过需要注意的是,其字段类型不能为text,否则聚合会失败。

Note3:所以根据上面的提示,一般纯数组比较适合存放标签类的数据,就像上面的案例一样,同时字段类型设置为keyword,而不是text,搜索时进行精确匹配就好了。

1.7 滚动查询scroll

如果一次性要查出来比如10万条数据,那么性能会很差,此时一般会采取用scoll滚动查询,一批一批的查,直到所有数据都查询完处理完(es返回的scrollId,可以理解为是es进行此次查询的操作句柄标识,每发送一次该scrollId,es都会操作一次,或者说循环一次,直到时间窗口到期)。

使用scoll滚动搜索,可以先搜索一批数据,然后下次再搜索一批数据,以此类推,直到搜索出全部的数据来,scoll搜索会在第一次搜索的时候,保存一个当时的视图快照,之后只会基于该旧的视图快照提供数据搜索,如果这个期间数据变更,是不会让用户看到的,每次发送scroll请求,我们还需要指定一个scoll参数,指定一个时间窗口,每次搜索请求只要在这个时间窗口内能完成就可以了(也就是说,该scrollId只在这个时间窗口内有效,视图快照也是)。

  1. GET spnews/news/_search?scroll=1m
  2. {
  3. "query": {
  4. "match_all": {}
  5. },
  6. "size": 10,
  7. "_source": ["id"]
  8. }
  9. GET _search/scroll
  10. {
  11. "scroll":"1m",
  12. "scroll_id":"DnF1ZXJ5VGhlbkZldGNoAwAAAAAAADShFmpBMjJJY2F2U242RFU5UlAzUzA4MWcAAAAAAAA0oBZqQTIySWNhdlNuNkRVOVJQM1MwODFnAAAAAAAANJ8WakEyMkljYXZTbjZEVTlSUDNTMDgxZw=="
  13. }

2 聚合

2.1 指标聚合

相当于MySQL的聚合函数。

max

  1. {
  2. "size": 0,
  3. "aggs": { # aggregation的缩写,聚合的意思
  4. "max_id": {
  5. "max": {
  6. "field": "id"
  7. }
  8. }
  9. }
  10. }

size不设置为0,除了返回聚合结果外,还会返回其它所有的数据。

min

  1. {
  2. "size": 0,
  3. "aggs": {
  4. "min_id": {
  5. "min": {
  6. "field": "id"
  7. }
  8. }
  9. }
  10. }

avg

  1. {
  2. "size": 0,
  3. "aggs": {
  4. "avg_id": {
  5. "avg": {
  6. "field": "id"
  7. }
  8. }
  9. }
  10. }

sum

  1. {
  2. "size": 0,
  3. "aggs": {
  4. "sum_id": {
  5. "sum": {
  6. "field": "id"
  7. }
  8. }
  9. }
  10. }

stats

  1. {
  2. "size": 0,
  3. "aggs": {
  4. "stats_id": {
  5. "stats": {
  6. "field": "id"
  7. }
  8. }
  9. }
  10. }

2.2 桶聚合

相当于MySQL的group by操作,所以不要尝试对es中text的字段进行桶聚合,否则会失败。

Terms

相当于分组查询,根据字段做聚合。

  1. {
  2. "size": 0,
  3. "aggs": {
  4. "per_count": {
  5. "terms": {
  6. "size":100,
  7. "field": "vtype",
  8. "min_doc_count":1
  9. }
  10. }
  11. }
  12. }

在桶聚合的过程中还可以进行指标聚合,相当于mysql做group by之后,再做各种max、min、avg、sum、stats之类的:

  1. {
  2. "size": 0,
  3. "aggs": {
  4. "per_count": {
  5. "terms": {
  6. "field": "vtype"
  7. },
  8. "aggs": {
  9. "stats_follower": {
  10. "stats": {
  11. "field": "realFollowerCount"
  12. }
  13. }
  14. }
  15. }
  16. }
  17. }

Filter

相当于是MySQL根据where条件过滤出结果,然后再做各种max、min、avg、sum、stats操作。

  1. {
  2. "size": 0,
  3. "aggs": {
  4. "gender_1_follower": {
  5. "filter": {
  6. "term": {
  7. "gender": 1
  8. }
  9. },
  10. "aggs": {
  11. "stats_follower": {
  12. "stats": {
  13. "field": "realFollowerCount"
  14. }
  15. }
  16. }
  17. }
  18. }
  19. }

上面的聚合操作相当于是:查询gender为1的各个指标。

Filters

在Filter的基础上,可以查询多个字段各自独立的各个指标,即对每个查询结果分别做指标聚合。

  1. {
  2. "size": 0,
  3. "aggs": {
  4. "gender_1_2_follower": {
  5. "filters": {
  6. "filters": [
  7. {
  8. "term": {
  9. "gender": 1
  10. }
  11. },
  12. {
  13. "term": {
  14. "gender": 2
  15. }
  16. }
  17. ]
  18. },
  19. "aggs": {
  20. "stats_follower": {
  21. "stats": {
  22. "field": "realFollowerCount"
  23. }
  24. }
  25. }
  26. }
  27. }
  28. }

Range

  1. {
  2. "size": 0,
  3. "aggs": {
  4. "follower_ranges": {
  5. "range": {
  6. "field": "realFollowerCount",
  7. "ranges": [
  8. {
  9. "to": 500
  10. },
  11. {
  12. "from": 500,
  13. "to": 1000
  14. },
  15. {
  16. "from": 1000,
  17. "to": 1500
  18. },
  19. {
  20. "from": "1500",
  21. "to": 2000
  22. },
  23. {
  24. "from": 2000
  25. }
  26. ]
  27. }
  28. }
  29. }
  30. }

to:小于,from:大于等于

Date Range

跟上面一个类似的,其实只是字段为日期类型的,然后范围值也是日期。

Date Histogram Aggregation

这个功能十分有用,可以根据年月日来对数据进行分类。
索引下面的文档:

  1. DELETE my_blog
  2. PUT my_blog
  3. {
  4. "mappings": {
  5. "article":{
  6. "properties": {
  7. "title":{"type": "text"},
  8. "postdate":{
  9. "type": "date"
  10. , "format": "yyyy-MM-dd HH:mm:ss"
  11. }
  12. }
  13. }
  14. }
  15. }
  16. PUT my_blog/article/1
  17. {
  18. "title":"Elasticsearch in Action",
  19. "postdate":"2014-09-23 23:34:12"
  20. }
  21. PUT my_blog/article/2
  22. {
  23. "title":"Spark in Action",
  24. "postdate":"2015-09-13 14:12:22"
  25. }
  26. PUT my_blog/article/3
  27. {
  28. "title":"Hadoop in Action",
  29. "postdate":"2016-08-23 23:12:22"
  30. }

按年对数据进行聚合:

  1. GET my_blog/article/_search
  2. {
  3. "size": 0,
  4. "aggs": {
  5. "agg_year": {
  6. "date_histogram": {
  7. "field": "postdate",
  8. "interval": "year",
  9. "order": {
  10. "_key": "asc"
  11. }
  12. }
  13. }
  14. }
  15. }
  16. {
  17. "took": 18,
  18. "timed_out": false,
  19. "_shards": {
  20. "total": 5,
  21. "successful": 5,
  22. "failed": 0
  23. },
  24. "hits": {
  25. "total": 3,
  26. "max_score": 0,
  27. "hits": []
  28. },
  29. "aggregations": {
  30. "agg_year": {
  31. "buckets": [
  32. {
  33. "key_as_string": "2014-01-01 00:00:00",
  34. "key": 1388534400000,
  35. "doc_count": 1
  36. },
  37. {
  38. "key_as_string": "2015-01-01 00:00:00",
  39. "key": 1420070400000,
  40. "doc_count": 1
  41. },
  42. {
  43. "key_as_string": "2016-01-01 00:00:00",
  44. "key": 1451606400000,
  45. "doc_count": 1
  46. }
  47. ]
  48. }
  49. }
  50. }

按月对数据进行聚合:

  1. GET my_blog/article/_search
  2. {
  3. "size": 0,
  4. "aggs": {
  5. "agg_year": {
  6. "date_histogram": {
  7. "field": "postdate",
  8. "interval": "month",
  9. "order": {
  10. "_key": "asc"
  11. }
  12. }
  13. }
  14. }
  15. }

这样聚合的话,包含的年份的每一个月的数据都会被分类,不管其是否包含文档。

按日对数据进行聚合:

  1. GET my_blog/article/_search
  2. {
  3. "size": 0,
  4. "aggs": {
  5. "agg_year": {
  6. "date_histogram": {
  7. "field": "postdate",
  8. "interval": "day",
  9. "order": {
  10. "_key": "asc"
  11. }
  12. }
  13. }
  14. }
  15. }

这样聚合的话,包含的年份的每一个月的每一天的数据都会被分类,不管其是否包含文档。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/409898
推荐阅读
相关标签
  

闽ICP备14008679号