• 重学Elasticsearch第5章 : 过滤查询、聚合查询



    Filter Query(过滤查询)

    过滤查询

    其实准确来说,ES中的查询操作分为2种: 查询(query)过滤(filter)

    • 查询即是之前提到的query查询,它(查询)默认会计算每个返回文档的得分,然后根据得分排序
    • 过滤(filter)只会筛选出符合的文档,并不计算得分,且它可以缓存文档 。所以,单从性能考虑,过滤比查询更快

    换句话说,过滤适合在大范围筛选数据,而查询则适合精确匹配数据。一般应用时, 应先使用过滤操作过滤数据, 然后使用查询匹配数据。

    在这里插入图片描述

    过滤语法

    // 从所有文档中过滤age>=10的文档
    GET /ems/_search
    {
      "query": {
        "bool": {
          "must": [
            {"match_all": {}}
          ],
          "filter": {
            "range": {
              "age": {
                "gte": 10
              }
            }
          }
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    提示: 在执行filter和query时,先执行filter在执行query

    提示: Elasticsearch会自动缓存经常使用的过滤器,以加快性能。

    常见的过滤器类型

    term 、terms Filter

    // 先过滤出content字段关键词为spring的文档,再query出name为黑的文档
    GET /ems/_search
    {
      "query": {
        "bool": {
          "must": [
            {
              "term": {
                "name": {
                  "value": "黑"
                }
              }
            }
          ],
          "filter": {
            "term": {
              "content":"spring"
            }
          }
        }
      }
    }
    
    // 找到content中过滤不包含 科技,声音的文档, 然后在进行term关键词查询name为中国的文档
    GET /dangdang/_search  #使用terms过滤
    {
      "query": {
        "bool": {
          "must": [
            {"term": {
              "name": {
                "value": "中国"
              }
            }}
          ],
          "filter": {
            "terms": {
              "content":[
                  "科技",
                  "声音"
                ]
            }
          }
        }
      }
    }
    
    • 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
    • 42
    • 43
    • 44
    • 45
    • 46

    ranage filter

    GET /ems/_search
    {
      "query": {
        "bool": {
          "must": [
            {"term": {
              "name": {
                "value": "中国"
              }
            }}
          ],
          "filter": {
            "range": {
              "age": {
                "gte": 7,
                "lte": 20
              }
            }
          }
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    exists filter

    过滤出存在指定字段的文档

    // 首先过滤出存在name字段的文档,并且进行term关键词查询,name字段中包含中国的文档
    GET /ems/_search
    {
      "query": {
        "bool": {
          "must": [
            {"term": {
              "name": {
                "value": "中国"
              }
            }}
          ],
          "filter": {
            "exists": {
              "field":"name"
            }
          }
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    ids filter

    过滤含有指定字段的索引记录

    GET /ems/_search
    {
      "query": {
        "bool": {
          "must": [
            {"term": {
              "name": {
                "value": "中国"
              }
            }}
          ],
          "filter": {
            "ids": {
              "values": ["1","2","3"]
            }
          }
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    聚合查询

    简介

    聚合:英文为Aggregation,是es除搜索功能外提供的针对es数据做统计分析的功能。聚合有助于根据搜索查询提供聚合数据。

    聚合查询是数据库中重要的功能特性,ES作为搜索引擎兼数据库,同样提供了强大的聚合分析能力。它基于查询条件来对数据进行分桶、计算的方法。有点类似于 SQL 中的 group by 再加一些函数方法的操作。

    注意事项:text类型是不支持聚合的。

    测试数据

    // 创建索引 index 和映射 mapping
    PUT /fruit
    {
      "mappings": {
        "properties": {
          "title":{
            "type": "keyword"
          },
          "price":{
            "type":"double"
          },
          "description":{
            "type": "text",
            "analyzer": "ik_max_word"
          }
        }
      }
    }
    // 放入测试数据
    PUT /fruit/_bulk
    {"index":{}}
      {"title" : "面包","price" : 19.9,"description" : "小面包非常好吃"}
    {"index":{}}
      {"title" : "旺仔牛奶","price" : 29.9,"description" : "非常好喝"}
    {"index":{}}
      {"title" : "日本豆","price" : 19.9,"description" : "日本豆非常好吃"}
    {"index":{}}
      {"title" : "小馒头","price" : 19.9,"description" : "小馒头非常好吃"}
    {"index":{}}
      {"title" : "大辣片","price" : 39.9,"description" : "大辣片非常好吃"}
    {"index":{}}
      {"title" : "透心凉","price" : 9.9,"description" : "透心凉非常好喝"}
    {"index":{}}
      {"title" : "小浣熊","price" : 19.9,"description" : "童年的味道"}
    {"index":{}}
      {"title" : "海苔","price" : 19.9,"description" : "海的味道"}
    
    • 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

    使用

    根据某个字段分组

    // 根据检索条件查询出来的文档, 进行分组;根据price进行分组,分组叫price_group
    GET /fruit/_search
    {
      "query": {
        "term": {
          "description": {
            "value": "吃"
          }
        }
      }, 
      "aggs": {
        "price_group": {
          "terms": {
            "field": "price"
          }
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    // 结果
    {
      "took" : 3,
      "timed_out" : false,
      "_shards" : {
        "total" : 1,
        "successful" : 1,
        "skipped" : 0,
        "failed" : 0
      },
      "hits" : {
        "total" : {
          "value" : 4,
          "relation" : "eq"
        },
        "max_score" : 0.6489038,
        "hits" : [
          {
            "_index" : "fruit",
            "_type" : "_doc",
            "_id" : "xbUHXYIBkJ4IdFBIeDXc",
            "_score" : 0.6489038,
            "_source" : {
              "title" : "面包",
              "price" : 19.9,
              "description" : "小面包非常好吃"
            }
          },
          {
            "_index" : "fruit",
            "_type" : "_doc",
            "_id" : "x7UHXYIBkJ4IdFBIeDXc",
            "_score" : 0.6489038,
            "_source" : {
              "title" : "日本豆",
              "price" : 19.9,
              "description" : "日本豆非常好吃"
            }
          },
          {
            "_index" : "fruit",
            "_type" : "_doc",
            "_id" : "yLUHXYIBkJ4IdFBIeDXc",
            "_score" : 0.6489038,
            "_source" : {
              "title" : "小馒头",
              "price" : 19.9,
              "description" : "小馒头非常好吃"
            }
          },
          {
            "_index" : "fruit",
            "_type" : "_doc",
            "_id" : "ybUHXYIBkJ4IdFBIeDXc",
            "_score" : 0.6489038,
            "_source" : {
              "title" : "大辣片",
              "price" : 39.9,
              "description" : "大辣片非常好吃"
            }
          }
        ]
      },
      "aggregations" : {
        "price_group" : {
          "doc_count_error_upper_bound" : 0,
          "sum_other_doc_count" : 0,
          "buckets" : [
            {
              "key" : 19.9,
              "doc_count" : 3
            },
            {
              "key" : 39.9,
              "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
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81

    求最大值

    # 求最大值 
    GET /fruit/_search
    {
      "aggs": {
        "price_max": {
          "max": {
            "field": "price"
          }
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在这里插入图片描述

    求最小值

    # 求最小值
    GET /fruit/_search
    {
      "aggs": {
        "price_min": {
          "min": {
            "field": "price"
          }
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在这里插入图片描述

    求平均值

    # 求平均值
    GET /fruit/_search
    {
      "aggs": {
        "price_agv": {
          "avg": {
            "field": "price"
          }
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在这里插入图片描述

    求和

    # 求和
    GET /fruit/_search
    {
      "aggs": {
        "price_sum": {
          "sum": {
            "field": "price"
          }
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在这里插入图片描述

    聚合查询整合Java

    // 求不同价格的数量
    @Test
    public void testAggsPrice() throws IOException {
      SearchRequest searchRequest = new SearchRequest("fruit");
      SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
      sourceBuilder.aggregation(AggregationBuilders.terms("group_price").field("price"));
      searchRequest.source(sourceBuilder);
      SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
      Aggregations aggregations = searchResponse.getAggregations();
      ParsedDoubleTerms terms = aggregations.get("group_price");
      List<? extends Terms.Bucket> buckets = terms.getBuckets();
      for (Terms.Bucket bucket : buckets) {
        System.out.println(bucket.getKey() + ", "+ bucket.getDocCount());
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    // 求不同名称的数量
    @Test
    public void testAggsTitle() throws IOException {
      SearchRequest searchRequest = new SearchRequest("fruit");
      SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
      sourceBuilder.aggregation(AggregationBuilders.terms("group_title").field("title"));
      searchRequest.source(sourceBuilder);
      SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
      Aggregations aggregations = searchResponse.getAggregations();
      ParsedStringTerms terms = aggregations.get("group_title");
      List<? extends Terms.Bucket> buckets = terms.getBuckets();
      for (Terms.Bucket bucket : buckets) {
      	System.out.println(bucket.getKey() + ", "+ bucket.getDocCount());
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    // 求和
    @Test
    public void testAggsSum() throws IOException {
      SearchRequest searchRequest = new SearchRequest("fruit");
      SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
      sourceBuilder.aggregation(AggregationBuilders.sum("sum_price").field("price"));
      searchRequest.source(sourceBuilder);
      SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
      ParsedSum parsedSum = searchResponse.getAggregations().get("sum_price");
      System.out.println(parsedSum.getValue());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
  • 相关阅读:
    HBase原理深入
    重生奇迹MU天空之城玩转攻略
    数字化转型这么火热,企业选择数字化转型都有哪些目的
    K8S:kubectl陈述式及声明式资源管理
    模拟桌面自动整理, 先顶左,再顶上。
    springboot实现全局事务管理
    docker安装ELK详细步骤(冒着被老板开的风险,生产试验,适用所有版本)
    分享从0学习arthas的17篇文章
    红队隧道应用篇之ICMP协议传输(八)
    Hutool的BeanUtil.copyProperties() 的改进详情
  • 原文地址:https://blog.csdn.net/m0_37989980/article/details/126120126