首页 > 其他分享 >elasticsearch聚合查询实践

elasticsearch聚合查询实践

时间:2023-12-05 22:11:51浏览次数:30  
标签:salary index 聚合 name gender 查询 job elasticsearch id

目录

概念

用于聚合的字段必须是 exact value,即doc_value=true。分词字段不可进行聚合,对于 text 字段如需使用聚合,需开启 fielddata,不推荐因容易造成 OOM。

聚合分类

  1. Bucket aggregations(桶聚合)
  2. Metric aggregations(指标聚合)
  3. Pipeline aggregations(管道聚合)

聚合语法

request

GET /my-index/_search
{
  "aggs": {
    "my-agg-name": {
      "terms": {
        "field": "my-field"
      }
    }
  }
}

response

{
  "took": 78,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 5,
      "relation": "eq"
    },
    "max_score": 1.0,
    "hits": [...]
  },
  "aggregations": {
    "my-agg-name": {
      "doc_count_error_upper_bound": 0,
      "sum_other_doc_count": 0,
      "buckets": []
    }
  }
}

聚合作用范围及排序

  • queryfilter,是先选定数据范围,再聚合桶;
  • post_filter 对聚合桶没影响,桶全部返回,只对查询结果进行过滤返回,功能类似 mysql 中的 having;
  • global 的作用是覆盖掉 query 的查询作用。

聚合原理及 terms 精准度

Terms Aggregation 的返回中有两个特殊的数值

  • doc_count_error_upper_bound:被遗漏的 term 分桶,包含的文档,有可能的最大值
  • sum_other_doc_count:除了返回结果 bucket 的 terms 以外,其他的 terms 的文档总数(总数-返回的总数)

聚合实验

实验数据引用自《Elasticsearch 核心技术与实战》- 阮一鸣(eBay Pronto 平台技术负责人)

创建索引

PUT /employees/
{
  "mappings" : {
      "properties" : {
        "age" : {
          "type" : "integer"
        },
        "gender" : {
          "type" : "keyword"
        },
        "job" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 50
            }
          }
        },
        "name" : {
          "type" : "keyword"
        },
        "salary" : {
          "type" : "integer"
        }
      }
    }
}

批量写入数据

PUT /employees/_bulk
{ "index" : {  "_id" : "1" } }
{ "name" : "Emma","age":32,"job":"Product Manager","gender":"female","salary":35000 }
{ "index" : {  "_id" : "2" } }
{ "name" : "Underwood","age":41,"job":"Dev Manager","gender":"male","salary": 50000}
{ "index" : {  "_id" : "3" } }
{ "name" : "Tran","age":25,"job":"Web Designer","gender":"male","salary":18000 }
{ "index" : {  "_id" : "4" } }
{ "name" : "Rivera","age":26,"job":"Web Designer","gender":"female","salary": 22000}
{ "index" : {  "_id" : "5" } }
{ "name" : "Rose","age":25,"job":"QA","gender":"female","salary":18000 }
{ "index" : {  "_id" : "6" } }
{ "name" : "Lucy","age":31,"job":"QA","gender":"female","salary": 25000}
{ "index" : {  "_id" : "7" } }
{ "name" : "Byrd","age":27,"job":"QA","gender":"male","salary":20000 }
{ "index" : {  "_id" : "8" } }
{ "name" : "Foster","age":27,"job":"Java Programmer","gender":"male","salary": 20000}
{ "index" : {  "_id" : "9" } }
{ "name" : "Gregory","age":32,"job":"Java Programmer","gender":"male","salary":22000 }
{ "index" : {  "_id" : "10" } }
{ "name" : "Bryant","age":20,"job":"Java Programmer","gender":"male","salary": 9000}
{ "index" : {  "_id" : "11" } }
{ "name" : "Jenny","age":36,"job":"Java Programmer","gender":"female","salary":38000 }
{ "index" : {  "_id" : "12" } }
{ "name" : "Mcdonald","age":31,"job":"Java Programmer","gender":"male","salary": 32000}
{ "index" : {  "_id" : "13" } }
{ "name" : "Jonthna","age":30,"job":"Java Programmer","gender":"female","salary":30000 }
{ "index" : {  "_id" : "14" } }
{ "name" : "Marshall","age":32,"job":"Javascript Programmer","gender":"male","salary": 25000}
{ "index" : {  "_id" : "15" } }
{ "name" : "King","age":33,"job":"Java Programmer","gender":"male","salary":28000 }
{ "index" : {  "_id" : "16" } }
{ "name" : "Mccarthy","age":21,"job":"Javascript Programmer","gender":"male","salary": 16000}
{ "index" : {  "_id" : "17" } }
{ "name" : "Goodwin","age":25,"job":"Javascript Programmer","gender":"male","salary": 16000}
{ "index" : {  "_id" : "18" } }
{ "name" : "Catherine","age":29,"job":"Javascript Programmer","gender":"female","salary": 20000}
{ "index" : {  "_id" : "19" } }
{ "name" : "Boone","age":30,"job":"DBA","gender":"male","salary": 30000}
{ "index" : {  "_id" : "20" } }
{ "name" : "Kathy","age":29,"job":"DBA","gender":"female","salary": 20000}

桶聚合

对 keword 进行聚合

GET employees/_search
{
  "size": 0,
  "aggs": {
    "jobs": {
      "terms": {
        "field":"job.keyword"
      }
    }
  }
}

指标聚合

多个 Metric 聚合,找到最低最高和平均 salary

GET employees/_search
{
  "size": 0,
  "aggs": {
    "max_salary": {
      "max": {
        "field": "salary"
      }
    },
    "min_salary": {
      "min": {
        "field": "salary"
      }
    },
    "avg_salary": {
      "avg": {
        "field": "salary"
      }
    }
  }
}

多次嵌套。根据工作类型分桶,然后按照性别分桶,计算 salary 的统计信息

GET employees/_search
{
  "size": 0,
  "aggs": {
    "job_gender_stats": {
      "terms": {
        "field": "job.keyword"
      },
      "aggs": {
        "gender_stats": {
          "terms": {
            "field": "gender"
          },
          "aggs": {
            "salary_stats": {
              "stats": {
                "field": "salary"
              }
            }
          }
        }
      }
    }
  }
}

response

{
  "took" : 6,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 20,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [ ]
  },
  "aggregations" : {
    "job_gender_stats" : {
      "doc_count_error_upper_bound" : 0,
      "sum_other_doc_count" : 0,
      "buckets" : [
        {
          "key" : "Java Programmer",
          "doc_count" : 7,
          "gender_stats" : {
            "doc_count_error_upper_bound" : 0,
            "sum_other_doc_count" : 0,
            "buckets" : [
              {
                "key" : "male",
                "doc_count" : 5,
                "salary_stats" : {
                  "count" : 5,
                  "min" : 9000.0,
                  "max" : 32000.0,
                  "avg" : 22200.0,
                  "sum" : 111000.0
                }
              },
              {
                "key" : "female",
                "doc_count" : 2,
                "salary_stats" : {
                  "count" : 2,
                  "min" : 30000.0,
                  "max" : 38000.0,
                  "avg" : 34000.0,
                  "sum" : 68000.0
                }
              }
            ]
          }
        },
        ......
      ]
    }
  }
}

Pipeline 聚合

平均 salary 的统计分析

GET employees/_search
{
  "size": 0,
  "aggs": {
    "jobs": {
      "terms": {
        "field": "job.keyword",
        "size": 10
      },
      "aggs": {
        "avg_salary": {
          "avg": {
            "field": "salary"
          }
        }
      }
    },
    "stats_salary_by_job":{
      "stats_bucket": {
        "buckets_path": "jobs>avg_salary"
      }
    }
  }
}

实践一:多商户数据权限聚合分页

collapse + cardinality 实现分页去重查询

GET my_order/_search
{
  "from": 0,
  "size": 6,
  "track_total_hits": true,
  "query": {
    "bool": {
      "must": [
        {
          "terms": {
            "tenant_id": [
              1,
              2,
              3,
              4
            ]
          }
        }
      ]
    }
  },
  "aggs": {
    "cidAgg": {
      "cardinality": {
        "field": "cid"
      }
    }
  },
  "collapse": {
    "field": "cid"
  }
}

注:不支持 search_after,导出推荐 scroll

实践二:多维度嵌套聚合

date_histogram 日期直方图 + terms 分桶聚合过去一周每天产生的工单量,每天各品类工单量,每天各品类排名前 N 的爆品等等。

GET my_order/_search
{
  "size": 0,
  "query": {
    "bool": {
      "filter": [
        {
          "range": {
            "created_at": {
              "gte": "2023-11-10",
              "lte": "2023-11-16"
            }
          }
        }
      ]
    }
  },
  "aggs": {
    "ranges": {
      "date_histogram": {
        "field": "created_at",
        "format": "yyyy-MM-dd",
        "calendar_interval": "day"
      },
      "aggs": {
        "order_type_agg": {
          "terms": {
            "field": "order_type"
          }
        }
      }
    }
  }
}

实践三:删除 ES 索引重复数据

核酸检测数据量大,数据存储选型如使用 elasticsearchclick house 等列数据库,数据重复是绕不开的话题,应用可通过计划任务等方式检测到重复数据并及时处理。

单字段查重

GET my_order/_search
{
  "size": 0,
  "query": {
    "term": {
      "tenant_id": 1
    }
  },
  "aggs": {
    "duplicateCount": {
      "terms": {
        "field": "cid",
        "size": 1000,
        "min_doc_count": 2
      }
    }
  }
}

多字段查重

GET my_order/_search
{
  "size": 0,
  "aggs": {
    "duplicateCount": {
      "terms": {
        "script": {
          "lang": "painless",
          "source": "doc['tenant_id'].value + doc['cid'].value"
        },
        "size": 100,
        "min_doc_count": 2
      }
    }
  }
}

数据查重并在 duplicateDocuments 数组展示细节

GET my_order/_search
{
  "size": 0,
  "aggs": {
    "duplicateCount": {
      "terms": {
        "script": {
          "lang": "painless",
          "source": "doc['tenant_id'].value + doc['cid'].value"
        },
        "size": 100,
        "min_doc_count": 2
      },
      "aggs": {
        "duplicateDocuments": {
          "top_hits": {}
        }
      }
    }
  }
}

查询到的重复数据记入日志,核实后使用_delete_by_query删除

POST my_order/_delete_by_query?conflicts=proceed&max_docs=1
{
  "query": {
    "term": {
      "cid": 2
    }
  }
}

max_docs为 response 当前 key 中bucket.doc_count的数量-1

php版本 demo 供参考

public function clearDuplicate()
{
    $index = 'my_order';
    $client = ClientBuilder::create()->build();
    $params = [
        'index' => $index,
        'size' => 0,
        'body' => [
            'query' => [
                ...
            ],
            'aggs' => [
                'duplicateCount' => [
                    'terms' => [
                        'field' => 'cid',
                        'size' => 1000,
                        'min_doc_count' => 2
                    ]
                ]
            ],
        ],
    ];
    $result = $client->search($params);
    $bucket = ArrayHelper::getValue($result, 'aggregations.duplicateCount.buckets');
    if (!is_array($bucket) || empty($bucket)) {
        return;
    }
    foreach ($bucket as $item) {
        $maxDocs = ArrayHelper::getValue($item, 'doc_count', 0) - 1;
        $key = ArrayHelper::getValue($item, 'key');
        if ($maxDocs < 1 || empty($key)) {
            continue;
        }
        $client->deleteByQuery([
            'index' => $index,
            'conflicts' => 'proceed',
            'max_docs' => $maxDocs,
            'body' => [
                'query' => [
                    'bool' => [
                        ...
                    ],
                ],
            ],
        ]);
    }
}

附:实验环境

linux 操作系统

$ uname -a
Linux LAPTOP-QK4HAU1D 5.15.90.1-microsoft-standard-WSL2 #1 SMP Fri Jan 27 02:56:13 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux

$ cat /etc/issue
Ubuntu 22.04.2 LTS \n \l

elasticsearch 版本

GET /
{
  "name" : "elasticsearch",
  "cluster_name" : "docker-cluster",
  "cluster_uuid" : "6xwN3rfbQ2KGgQdt8IUKqg",
  "version" : {
    "number" : "7.16.2",
    "build_flavor" : "default",
    "build_type" : "docker",
    "build_hash" : "2b937c44140b6559905130a8650c64dbd0879cfb",
    "build_date" : "2021-12-18T19:42:46.604893745Z",
    "build_snapshot" : false,
    "lucene_version" : "8.10.1",
    "minimum_wire_compatibility_version" : "6.8.0",
    "minimum_index_compatibility_version" : "6.0.0-beta1"
  },
  "tagline" : "You Know, for Search"
}

参考:
[1] https://gitee.com/geektime-geekbang/geektime-ELK
[2] https://www.elastic.co/guide/en/elasticsearch/reference/7.17/search-aggregations.html

标签:salary,index,聚合,name,gender,查询,job,elasticsearch,id
From: https://www.cnblogs.com/mozili/p/17878430.html

相关文章

  • 从 Elasticsearch 到 SelectDB,观测云实现日志存储与分析的 10 倍性价比提升
    导读:在云计算逐渐成熟的当下,越来越多的企业开始将业务迁移到云端,传统的监控和故障排查方法已经无法满足企业的需求。观测云可以实现对云、云原生、应用及业务的统一监测,提供整体数据的分析、洞察、可视化、自动化、监测告警、智能巡查、安全巡查等服务。本文将分享SelectDB如何助......
  • Sequelize的聚合查询查出messsage表中userId字段再user表中的用户信息
    这里记个一对多的,其他类似模型正常写,然后:入口文件main.js//...importmodelRelationfrom"./services/modelRelation";//...//同步模型和数据库modelRelation();modelRelation.js//import{Sequelize}from"sequelize";importsequelizefrom"./pool&quo......
  • 【MySQL】回收普通用户对其他库的查询权限,普通用户只能看见自己的表
    如何解决已回收权限的普通用户对其他库的查看权限?正常来说,普通用户自己创建的数据库表的权限无法被回收,如果用户曾经被授权过所有权限,取消权限后,依然可以看见其他库。其原因就是被授予了grantoption权限,需要手动取消"WITHGRANTOPTION"是MySQL中的一个选项,用于将授予权限给其......
  • 2023年最全在线聊天客服系统聚合软件推荐
    在这个全球化盛行的时代,跨境出海已经成为许多企业拓展业务的必然选择。然而,随之而来的挑战也是不可忽视的。在全球市场多种多样的社交媒体平台中,如何获取快速地集成多渠道信息成为了企业获取国际市场客户的关键。拥有一个可以聚合多渠道信息的平台,将成为企业在跨境出海中的得力助手......
  • ORM跨表查询
     ORM跨表查询、join查询、聚合查询、分组查询、FQ查询跨表查询分为两类:基于对象查询(子查询)基于双下划线查询(join查询)1、基于对象的跨表查询(sql语句:子查询)子查询:基于一个查询结果作为另一个查询的条件1.1一对多"""正向查询:多找一,按字段反向查询:一找多,按......
  • Prometheus Dashboard for elasticsearch exporter
    prometheus-community/elasticsearch_exporter:ElasticsearchstatsexporterforPrometheusReleases·prometheus-community/elasticsearch_exporterDashboards|GrafanaLabsElasticsearchExporterQuickstartandDashboard|GrafanaLabsPrometheusOSS|Elast......
  • spark-sql查询Iceberg时处理流程
    1、查询表结构showcreatetabledata_lake_ods.testCREATETABLEspark_catalog.data_lake_ods.test(`user_number`BIGINTNOTNULL,`subclazz_number`BIGINTNOTNULL,`clazz_number`BIGINT,`clazz_lesson_number`BIGINTNOTNULL,`lesson_live_property`......
  • 【elasticsearch】Elasticsearch相关文档
     【参考文档】一篇文章让你学会Elasticsearch中的查询ElasticSearch常用查询操作Elasticsearch增、删、改、查操作深入详解ElasticSearch(ES从入门到精通一篇就够了)Elasticsearch基础及入门Elasticsearch:GeoPoint和GeoShape查询解释elastisearchdocument......
  • C#中的并行处理、并行查询的方法你用对了吗?
    Parallel.ForEachParallel.ForEach 是一个用于在集合上并行执行迭代操作的强大工具。它通过有效地利用多核处理器的能力来提高性能。Parallel.ForEach 不仅能够简化并行编程,而且它在执行简单循环时可以提供比传统迭代更好的性能。下面是一个简单的示例,演示了如何使用 Paralle......
  • ES 什么是elasticsearch
    ES是elasticsearch的简称,elasticsearch是一款非常强大的开源分布式搜索引擎,可以帮助我们从海量数据中快速找到需要的内容。elasticsearch的底层实现是一个名为Lucene的技术。而Lucene中的核心技术就是倒排索引Lucene的优势:易扩展、高性能(基于倒排索引)Lucene的缺点:......