ES入门:查询和聚合

安装完ElasticSearch 和 Kibana后我们开始学习

为了方便测试,使用kibana的dev tool来进行学习测试:

测试工具

从索引文档开始

插入

向 Elasticsearch 索引 customer 的 _doc 类型的文档 id 为 1 的文档发送 PUT 请求的例子。

请求体为 JSON 格式,包含一个字段 name 和其值 DLBoy。

Elasticsearch 支持多种请求方法来对索引进行操作,其中包括 GET、POST、PUT、DELETE 等等。

在这个例子中,我们使用的 PUT 方法将更新或创建一个新的文档:

javascript 复制代码
PUT /customer/_doc/1
{
  "name": "DLBoy"
}

/customer/_doc/1**,其中****customer是索引的名称, \_doc通常是文档类型(在Elasticsearch 7.x及更高版本中,文档类型通常被忽略),而 1**是文档的唯一标识ID。

使用 PUT 方法提交文档时,如果指定的 id 已经存在,则该文档将被更新;如果不存在则该文档将被创建。在 POST 方法中,不需要提供 id 参数, Elasticsearch 会生成一个唯一的 id 。

查询

批量索引文档

下载测试数据

数据的格式如下:

批量插入数据

  • 将accounts.json拷贝至指定目录,我这里放在/opt/下面

执行

javascript 复制代码
curl -H "Content-Type: application/json" -XPOST "localhost:9200/bank/_bulk?pretty&refresh" --data-binary "@/opt/accounts.json"

解释

  • curl: 这是一个命令行工具,用于发起HTTP请求。
  • -H "Content-Type: application/json": 这是HTTP请求头,指定请求的内容类型为JSON。
  • -XPOST: 这指示curl执行HTTP POST请求。
  • "localhost:9200/bank/_bulk?pretty&refresh": 这是目标Elasticsearch服务器的URL,它指定了索引名称"bank",并在URL中使用"_bulk"来指示批量导入操作。pretty参数是可选的,用于格式化响应以使其更易阅读,refresh参数用于在导入完成后刷新索引,以使新数据立即可用。
  • --data-binary "@/opt/accounts.json": 这是HTTP请求的数据部分,它指定了要导入的数据文件的路径,这里是"/opt/accounts.json"。@符号表示要上传文件的路径。这个文件包含了要批量导入的JSON数据。

查看状态

javascript 复制代码
curl "localhost:9200/_cat/indices?v=true" | grep bank

查询数据

查询所有

match_all表示查询所有的数据,sort即按照什么字段排序

javascript 复制代码
GET /bank/_search
{
  "query": { "match_all": {} },
  "sort": [
    { "account_number": "asc" }
  ]
}

解释

这是一个Elasticsearch批量导入数据的响应示例。响应包含了以下信息:

  • "took" : 1: 表示处理请求所花费的时间,以毫秒为单位。
  • "timed_out" : false: 表示请求没有超时。
  • "_shards": 提供了与索引分片相关的信息,包括总分片数、成功的分片数、跳过的分片数和失败的分片数。
  • "total": 表示索引总共包含了1个分片。在分布式环境下,索引通常被分成多个分片以提高性能和可伸缩性。这里的值为1,表示索引可能是单一分片的。
  • "successful": 表示成功完成的分片数。在这里,所有的分片操作都成功,所以值为1。
  • "skipped": 表示跳过的分片数。在这个响应中,没有分片被跳过,所以值为0。
  • "failed": 表示失败的分片数。在这个响应中,没有分片失败,所以值为0。
  • "hits": 这是一个包含有关查询匹配文档的信息的部分。
  • "total": 提供了匹配查询条件的总文档数,这里是1000个文档。这是符合查询条件的文档总数。
  • "relation": 表示与总文档数的关系,这里是"eq",表示匹配文档的数量等于总文档数。其他可能的关系包括"gte"(大于或等于)、"lte"(小于或等于)等,根据查询条件的具体情况而定。
  • "hits": 这是一个文档数组,包含了查询匹配的文档。每个文档都包括了以下信息:
  • "_index": 文档所属的索引名称,这里是"bank"。
  • "_type": 文档的类型,通常在Elasticsearch 7.x及更高版本中使用"_doc"。
  • "_id": 文档的唯一标识ID。
  • "_score" - 文档的相关性得分(使用match_all时不适用)
  • "_source": 包含文档的实际数据。在这个示例中,包含了账户信息,如账号号码、余额、姓名、年龄、性别、地址等。
  • "sort" - 文档的排序位置(不按相关性得分排序时)

这个响应示例表明批量导入操作成功,共导入了1000个文档,并提供了匹配的文档详细信息。这些信息可用于后续的搜索和分析操作。


分页查询

from和size两个字段

javascript 复制代码
GET /bank/_search
{
  "query": { "match_all": {} },
  "sort": [
    { "account_number": "asc" }
  ],
  "from": 10,
  "size": 10
}

指定字段查询

如果要在字段中搜索特定字词,可以使用match

查询address 字段中包含 mill 或者 lane的数据;

javascript 复制代码
GET /bank/_search
{
  "query": {
    "match": {
      "address": "Holmes Lane"
    }
  }
}
  • "query": 查询请求的主体,指示Elasticsearch执行查询操作。
  • "match": 查询类型,表示执行一个文本匹配查询。
  • "address": 要匹配的字段名称,这里是"address"字段。
  • "Holmes Lane": 要匹配的文本内容,这里是"Holmes Lane"。查询将在"address"字段中查找包含"Holmes Lane"的文本。

由于ES底层是按照分词索引的,所以上述查询结果是address 字段中包含 Holmes 或者 Lane 的数据

查询段落匹配

查询的条件是 address字段中包含 "Holmes Lane",则可以使用match_phrase

javascript 复制代码
GET /bank/_search
{
  "query": {
    "match_phrase": {
      "address": "Holmes Lane"
    }
  }
}
  • "query": 查询请求的主体,指示Elasticsearch执行查询操作。
  • "match_phrase": 查询类型,表示执行一个短语匹配查询。
  • "address": 要匹配的字段名称,这里是"address"字段。
  • "Holmes Lane": 要匹配的短语,这里是"Holmes Lane"。查询将在"address"字段中查找包含完整短语"Holmes Lane"的文本。

多条件查询:

如果要构造更复杂的查询,可以使用bool查询来组合多个查询条件。

javascript 复制代码
GET /bank/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "age": "40" } }
      ],
      "must_not": [
        { "match": { "state": "ID" } }
      ]
    }
  }
}
  • "query": 查询请求的主体,指示Elasticsearch执行查询操作。
  • "bool": 查询类型,表示执行一个布尔查询,它可以包含多个条件。
  • "must": 这是一个数组,包含了必须匹配的条件。在这里,我们要求文档的"age"字段必须匹配值"40"。
  • "must_not": 这也是一个数组,包含了不能匹配的条件。在这里,我们要求文档的"state"字段不能匹配值"ID"。

Query or Filter

must,should,must_notfilter 都是bool查询的子句。

那么filterquery子句有啥区别呢?

查询条件

bool查询的子句中同时具备query,must 和 filter

javascript 复制代码
GET /bank/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "state": "ND"
          }
        }
      ],
      "filter": [
        {
          "term": {
            "age": "40"
          }
        },
        {
          "range": {
            "balance": {
              "gte": 20000,
              "lte": 30000
            }
          }
        }
      ]
    }
  }
}

Elasticsearch查询的示例,使用HTTP GET请求来搜索名为"bank"的索引中的文档。这个查询是一个复杂的布尔查询,包含了多个子查询条件,同时指定了必须匹配的条件和过滤条件。以下是这个查询的各个部分的解释:

  • HTTP方法:GET,表示发起一个查询请求。
  • 路径:/bank/_search,这是指定要搜索的索引名称为"bank",并且执行搜索操作。
  • 请求正文:这是一个包含查询条件的JSON请求体,用于定义查询的细节。在这个示例中,查询条件如下:
  • "query": 查询请求的主体,指示Elasticsearch执行查询操作。
  • "bool": 查询类型,表示执行一个布尔查询,它可以包含多个条件。
  • "must": 这是一个数组,包含了必须匹配的条件。在这里,我们要求文档的"state"字段必须匹配值"ND",即北达科他州。
  • "filter": 这是一个数组,包含了过滤条件,这些条件用于排除文档。在这里,有两个过滤条件:
  • "term": 这是一个精确匹配查询条件,要求文档的"age"字段必须精确匹配值"40"。
  • "range": 这是一个范围查询条件,要求文档的"balance"字段的值必须在20000到30000之间(包括20000和30000)。

所以,这个查询的目的是从"bank"索引中查找文档,这些文档同时满足以下条件:位于北达科他州("state"字段匹配"ND"),年龄为40,账户余额在20000到30000之间。

只包含filter的查询:

javascript 复制代码
GET /bank/_search
{
  "query": {
    "bool": {
      "filter": [
        {
          "term": {
            "age": "40"
          }
        },
        {
          "range": {
            "balance": {
              "gte": 20000,
              "lte": 30000
            }
          }
        }
      ]
    }
  }
}

在Elasticsearch中,queryfilter都用于定义搜索条件,但它们之间有重要的区别,主要涉及到搜索的目的和结果处理方式。以下是它们的主要区别:

  1. 目的
  2. Query(查询):主要用于筛选和排序文档以匹配搜索查询,以便找到相关性最高的文档。查询条件会计算文档的相关性得分,然后对文档进行排序,以使最相关的文档排在前面。
  3. Filter(过滤):主要用于筛选文档,不涉及相关性得分或排序。过滤条件用于精确匹配文档,通常用于排除不符合条件的文档。
  4. 计算开销
  5. Query(查询):查询条件可能需要较大的计算开销,因为它们计算文档的相关性得分并进行排序。这在某些情况下可能会导致查询变得较慢。
  6. Filter(过滤):过滤条件通常具有较小的计算开销,因为它们不涉及相关性得分或排序。这使得过滤条件在性能上更高效。
  7. 结果处理方式
  8. Query(查询):查询结果会包括文档的相关性得分,文档按照相关性排序。查询条件用于找到最相关的文档,通常用于全文搜索等情况。
  9. Filter(过滤):过滤条件返回文档的精确匹配结果,结果不包括相关性得分。过滤条件用于精确筛选文档,通常用于精确匹配、范围查询、布尔条件等情况。

总之,query主要用于搜索和排序文档,通常在需要考虑相关性的情况下使用,如全文搜索。而filter主要用于筛选文档,通常在需要精确匹配和排除的情况下使用,如范围查询、精确匹配、布尔条件等。根据搜索需求,可以选择使用queryfilter或它们的组合,以达到所需的搜索目标。

聚合查询

我们知道SQL中有group by,在ES中它叫Aggregation,即聚合运算。

简单聚合

比如我们希望计算出account.json的数据中每个州的统计数量, 使用aggs关键字对state字段聚合,被聚合的字段无需对分词统计,所以使用state.keyword对整个字段统计

javascript 复制代码
GET /bank/_search
{
  "size": 0,
  "aggs": {
    "group_by_state": {
      "terms": {
        "field": "state.keyword"
      }
    }
  }
}
  • "size": 这指定了搜索结果的大小,这里是0,表示不返回实际文档结果,只返回聚合结果。
  • "aggs": 这是用于定义聚合的部分。
  • "group_by_state": 这是聚合的名称,可以自定义。
  • "terms": 这是一种聚合类型,表示按照指定字段的值进行分组。在这里,我们希望按照"state.keyword"字段的值进行分组。
  • "field": 这是要用于分组的字段,这里是"state.keyword",表示按照州的关键字值进行分组。

所以,这个查询的目的是执行一个名为"group_by_state"的聚合,根据文档中的"state.keyword"字段的值进行分组。它将生成一个分组列表,其中包含每个不同州的值,并统计每个州的文档数量。由于"size"设置为0,不会返回实际文档结果,只返回聚合结果,以供进一步分析或显示聚合数据。这种聚合操作对于分析文档集的统计信息非常有用。

Elasticsearch聚合操作的响应结果,具体包括了"aggregations"部分解释:

  • "aggregations": 这是包含聚合结果的部分。
  • "group_by_state": 这是聚合的名称,与查询中定义的聚合名称一致。
  • "doc_count_error_upper_bound": 这是文档计数错误的上限,通常为0。它表示文档计数的错误限制,如果值大于0,表示可能存在计数错误。
  • "sum_other_doc_count": 这是其他文档计数的总和,743表示总共有743个文档分配到了除分桶之外的"其他"类别中。
  • "buckets": 这是分桶(buckets)的数组,包含了每个分组的信息。
  • 每个"bucket"包括以下信息:
  • "key": 分组的键,即"state.keyword"字段的值。
  • "doc_count": 分组中的文档计数,表示每个州拥有的文档数量。

在这个示例中,"group_by_state"聚合对"state.keyword"字段进行了分组,并列出了每个州的文档数量。例如,"TX"(得克萨斯州)有30个文档,"MD"(马里兰州)有28个文档,以此类推。这种聚合操作有助于了解文档集中各个分组的统计信息,通常用于数据分析和可视化。

嵌套聚合

ES处理聚合条件的嵌套。

计算每个州的平均结余。涉及到的就是在对state分组的基础上,嵌套计算avg(balance):

javascript 复制代码
GET /bank/_search
{
  "size": 0,
  "aggs": {
    "group_by_state": {
      "terms": {
        "field": "state.keyword"
      },
      "aggs": {
        "average_balance": {
          "avg": {
            "field": "balance"
          }
        }
      }
    }
  }
}
  • "size": 这指定了搜索结果的大小,这里是0,表示不返回实际文档结果,只返回聚合结果。
  • "aggs": 这是用于定义聚合的部分。
  • "group_by_state": 这是聚合的名称,用于按州进行分组。
  • "terms": 这是一种聚合类型,表示按照指定字段的值进行分组,这里是"state.keyword"字段的值。
  • "aggs": 这是在每个州分组内执行的嵌套聚合。
  • "average_balance": 这是嵌套聚合的名称,用于计算每个州的平均账户余额。
  • "avg": 这是嵌套聚合的类型,表示计算平均值。
  • "field": 这是用于计算平均值的字段,这里是"balance"字段,表示计算每个州的账户余额的平均值。

所以,这个查询的目的是执行一个名为"group_by_state"的聚合,根据文档中的"state.keyword"字段的值进行分组。在每个分组内,还执行了一个名为"average_balance"的嵌套聚合,计算每个州的平均账户余额。由于"size"设置为0,不会返回实际文档结果,只返回聚合结果,以供进一步分析或显示聚合数据。这种聚合操作对于分析文档集的统计信息非常有用,包括平均值、总和、最小值、最大值等。

这是执行Elasticsearch聚合操作后的响应结果,具体包括了"aggregations"部分的解释:

  • "aggregations": 这是包含聚合结果的部分。
  • "group_by_state": 这是聚合的名称,与查询中定义的聚合名称一致。
  • "doc_count_error_upper_bound": 这是文档计数错误的上限,通常为0。它表示文档计数的错误限制,如果值大于0,表示可能存在计数错误。
  • "sum_other_doc_count": 这是其他文档计数的总和,743表示总共有743个文档分配到了除分桶之外的"其他"类别中。
  • "buckets": 这是分桶(buckets)的数组,包含了每个分组的信息。
  • 每个"bucket"包括以下信息:
  • "key": 分组的键,即"state.keyword"字段的值,表示各个州的名称。
  • "doc_count": 分组中的文档计数,表示每个州的文档数量。
  • "average_balance": 这是嵌套聚合计算的平均账户余额的结果。每个分组都包括一个"average_balance"字段,其中包含了平均值。

在这个示例中,"group_by_state"聚合对"state.keyword"字段进行了分组,列出了每个州的文档数量,并计算了每个州的平均账户余额。例如,"TX"(得克萨斯州)有30个文档,平均账户余额为26073.3,"MD"(马里兰州)有28个文档,平均账户余额为26161.535714285714,以此类推。这种聚合操作非常有助于对文档集进行统计和分析,以获得有关每个分组的信息。

聚合结果排序

通过在aggs中对嵌套聚合的结果进行排序

对嵌套计算出的avg(balance),这里是average_balance,进行排序

javascript 复制代码
GET /bank/_search
{
  "size": 0,
  "aggs": {
    "group_by_state": {
      "terms": {
        "field": "state.keyword",
        "order": {
          "average_balance": "desc"
        }
      },
      "aggs": {
        "average_balance": {
          "avg": {
            "field": "balance"
          }
        }
      }
    }
  }
}
  • "size": 这指定了搜索结果的大小,这里是0,表示不返回实际文档结果,只返回聚合结果。
  • "aggs": 这是用于定义聚合的部分。
  • "group_by_state": 这是聚合的名称,用于按州进行分组。
  • "terms": 这是一种聚合类型,表示按照指定字段的值进行分组,这里是"state.keyword"字段的值。
  • "order": 这是一个选项,用于指定排序方式,这里按照嵌套聚合"average_balance"的降序排列。
  • "aggs": 这是在每个州分组内执行的嵌套聚合。
  • "average_balance": 这是嵌套聚合的名称,用于计算每个州的平均账户余额。
  • "avg": 这是嵌套聚合的类型,表示计算平均值。
  • "field": 这是用于计算平均值的字段,这里是"balance"字段,表示计算每个州的账户余额的平均值。

这个查询的目的是执行一个名为"group_by_state"的聚合,根据文档中的"state.keyword"字段的值进行分组,同时计算每个州的平均账户余额,并按照平均余额的降序排列结果。由于"size"设置为0,不会返回实际文档结果,只返回聚合结果,以供进一步分析或显示聚合数据。这种聚合操作有助于找到平均账户余额最高的州。

相关推荐
玄微云36 分钟前
2026年通用软件难适配,垂直店务系统反而更省心
大数据·云计算·软件需求
Elastic 中国社区官方博客1 小时前
Elastic 为什么捐赠其 OpenTelemetry PHP 发行版
大数据·开发语言·elasticsearch·搜索引擎·信息可视化·全文检索·php
方向研究2 小时前
ABS生产
大数据
TDengine (老段)2 小时前
TDengine 视图功能使用
大数据·数据库·servlet·时序数据库·tdengine·涛思数据
TDengine (老段)2 小时前
TDengine IDMP 运维指南 —— 部署架构
大数据·运维·数据库·架构·时序数据库·tdengine·涛思数据
utmhikari3 小时前
【测试人生】变更规则校验Agent研发的一些思路
大数据·人工智能·llm·agent·变更风险·openclaw
AC赳赳老秦3 小时前
DeepSeek优化多智能体指令:避免协同冲突,提升自动化流程稳定性
android·大数据·运维·人工智能·自然语言处理·自动化·deepseek
成长之路5143 小时前
【数据集】A股上市公司数字投资数据集-含代码(2000-2024年)
大数据
jkyy20144 小时前
破局家电同质化:智能冰箱+主动健康,解锁家庭健康新赛道
大数据·人工智能·健康医疗
weiyvyy4 小时前
信息化系统建设规划篇——蓝图设计与路径规划
大数据·信息可视化·信息化系统·企业信息化核心业务模块·信息化建设