史上最细ES保姆级教学2-深入了解聚合查询以及查询权重的调整

史上最细ES保姆级教学2-深入了解聚合查询以及查询权重的调整

在之前的文章中,我们学习了 Elasticsearch 的基本全文检索和术语级检索。今天,我们将继续讲解 Elasticsearch 的相关性、如何调整多字段或多条件查询的权重,并深入探讨 Elasticsearch 的聚合查询,这在大数据分析中发挥着至关重要的作用。

Elasticsearch 中相关性的深入探讨

什么是相关性?

在搜索引擎中,相关性是指查询与结果文档之间的匹配程度。一个文档的相关性越高,意味着它与用户的查询请求越契合。相关性通常通过**评分(score)**来衡量,搜索引擎通过计算查询与文档内容的匹配度来生成分数,分数越高,文档就越有可能出现在查询结果的前面。

在 Elasticsearch 中,相关性是通过 TF-IDF(词频-逆文档频率)BM25(Okapi BM25) 等算法来计算的,结合文档中的术语频率(TF)、术语在所有文档中的出现频率(IDF)等因素来评估文档和查询的匹配度。

为什么 Elasticsearch 需要相关性?

Elasticsearch 作为一个全文搜索引擎,它的核心目标是帮助用户快速找到与查询最相关的信息。为了实现这一点,Elasticsearch 必须能够评估文档与用户查询之间的匹配度,返回最相关的文档。相关性帮助决定哪些文档应该排在搜索结果的前面,哪些则应该排在后面。

举例说明:

假设你使用 Elasticsearch 搜索一个电子商务网站,查询“Apple iPhone 13”。

  • 如果搜索结果显示一个包含关键词 AppleiPhone 13 的产品页面,且该页面描述中详细列出了有关 Apple iPhone 13 的信息,那么该页面会被认为是与查询非常相关,相关性分数较高。
  • 如果另一个页面中也包含了关键词 AppleiPhone 13,但它的描述是 关于苹果公司历史的介绍,没有提到 iPhone 13,那么该页面会被认为是与查询不太相关,相关性分数较低。

此时,Elasticsearch 会通过相关性分数来评估文档,返回最相关的文档排序在前面,以便用户更快速地找到想要的商品。

相关性计算的示例:

假设我们有两个文档,查询为 “iPhone 13”。

文档 1:

  • 标题: “Buy iPhone 13 - Apple’s Latest Model”
  • 描述: “Get the new iPhone 13, featuring powerful features, and available in multiple colors.”

文档 2:

  • 标题: “Apple Announces New Features for iPhone 13”
  • 描述: “Apple just announced the iPhone 13 with new features like improved camera quality.”

查询: “iPhone 13”

  • 文档 1 与查询非常相关,因为标题和描述中都详细提到 iPhone 13,并且可能包含了更多的关键词匹配。
  • 文档 2 也相关,但可能没有像文档 1 那样全面提到 iPhone 13。因此,尽管它包含了部分关键词,但与查询的匹配度不如文档 1。

Elasticsearch 会基于上述相关性分析,计算两个文档的相关性分数。文档 1 的相关性分数可能会高于文档 2,从而文档 1 会排在查询结果的前面。

相关性的使用:
  1. 提高用户体验: 用户希望看到最相关的结果。如果没有相关性,用户可能会看到不相关的文档,导致搜索结果无用,用户体验差。
  2. 排序优化: 相关性帮助 Elasticsearch 排序搜索结果,确保用户能够快速找到最符合查询意图的信息。
  3. 处理模糊查询: 有时用户可能无法精确描述自己的需求,相关性算法能够处理模糊匹配,提供最相关的文档。
Elasticsearch 相关性计算机制

Elasticsearch 的相关性计算主要依赖以下几个机制:

  1. TF-IDF 和 BM25:计算查询和文档的匹配度。
  2. Norm(标准化):根据文档的长度来调整相关性评分。
  3. 查询解析和字段分析:将查询和文档的文本分析为词项后进行匹配。
  4. Boolean Model:通过布尔逻辑调整相关性评分。
  5. Field Value Factors 和 Function Score:通过字段值和函数来调整相关性。

我们在ES的使用中常常通过调整boost和multi_match对字段的相关性权重进行调整

Multi-Match 与 Boosting 查询的应用

Boost和Multi_match

1. Boost(加权)
  • 相关性调节boost 是一种 自定义相关性打分(Custom Relevance Scoring) 的方式。通过为特定的查询元素(如字段、查询词、查询本身等)加权,提升其对最终结果的影响,从而调整文档的排序。也就是说,boost 是直接控制某些查询或字段在相关性计算中所占的权重。
2. Multi-Match Query(多字段查询)
  • 相关性计算的方式multi_match 查询是 查询解析和字段分析(Analyzer)自定义相关性打分(Custom Relevance Scoring) 的结合。它允许在多个字段上进行匹配,并使用不同的匹配类型(如 best_fieldsmost_fieldscross_fields 等)来计算相关性分数。同时,你可以为不同的字段设置不同的 boost 值,从而影响不同字段的匹配得分。

multi_match 会根据设置的匹配类型(best_fieldsmost_fields 等)在多个字段中查找相关性,结合每个字段的分数来计算最终的相关性得分。

Boosting 查询在 Elasticsearch 中的应用

Boosting 是搜索引擎中用于提高某些文档、字段或查询匹配项相对重要性的技术。它通常用于增强搜索结果的相关性,使某些词、字段或文档能够得到更多的关注和排名。在 Elasticsearch 中,boosting 通过对查询中的字段、查询本身或查询的某些部分应用加权来提高相关性。Boosting 使得某些内容在查询时有更高的优先级,从而在检索时产生更准确的排序。

1. Boosting

Boosting 允许你给查询中的某些字段设置 boost 参数,从而增强该字段的影响力。通过为字段加权,可以使匹配该字段的文档更具优先性。

应用场景:当你希望某些字段在查询中更为重要时,可以使用 boosting。比如,标题字段 titlecontent 字段更重要。

示例:

假设你有一个包含书籍信息的 Elasticsearch 索引,字段包括 titlecontent,你希望标题字段的匹配比内容字段更重要。

 {
  "query": {
    "bool": {
      "should": [
        {
          "match": {
            "title": {
              "query": "Elasticsearch",
              "boost": 3.0  // 给 title 字段加权
            }
          }
        },
        {
          "match": {
            "content": {
              "query": "Elasticsearch",
              "boost": 1.0  // 给 content 字段加权,默认值为1.0
            }
          }
        }
      ]
    }
  }
}

解释:

  • 在这个查询中,title 字段的 boost 为 3.0,content 字段的 boost 为 1.0。这意味着如果 title 匹配 “Elasticsearch”,它的相关性得分会比 content 匹配时高三倍。
  • 如果文档同时在 titlecontent 字段中匹配到 Elasticsearch,则标题匹配的文档得分会更高,优先返回。
    multi_match 查询专门用于在多个字段中进行匹配,并且支持对不同字段的加权设置。

示例:

使用 multi_match 查询匹配多个字段

{
  "query": {
    "multi_match": {
      "query": "Elasticsearch",
      "fields": ["title^3", "content^1"],  // 对 title 字段和 content 字段加权
      "type": "best_fields"  // 默认类型
    }
  }
}

解释:

  • query: 要查询的文本 “Elasticsearch”。
  • fields: 用于查询的字段,这里使用了 title^3content^1,表示对 title 字段加权 3.0,对 content 字段加权 1.0。
  • type: 查询的类型,best_fields 是默认值,表示选择最相关的字段。如果你希望查询多个字段的相关性合并,可以使用 most_fields 或其他类型。

field^boost和boost 字段的区别和总结

  • field^num 适用于 multi_match 查询,用于对多个字段进行加权。
  • boost 适用于 match 查询,直接对一个字段的匹配结果进行加权。
  • field^boostboost 都是boost加权的方式 不过一个是针对字段,另一个更针对于查询。
2. negative_boost 查询

应用场景:当你希望某些文档匹配特定条件时增加它们的得分,而匹配其他条件时降低它们的得分时,可以使用 negative_boost 查询。比如,提升包含“最新”内容的文档,减少包含“过时”内容的文档。

示例:

 {
  "query": {
    "boosting": {
      "positive": {
        "match": {
          "title": "Elasticsearch"
        }
      },
      "negative": {
        "match": {
          "content": "Old Search"
        }
      },
      "negative_boost": 0.3  // 降低负面查询的影响
    }
  }
}

解释:

  • positive 部分提高包含 “Elasticsearch” 的文档得分。

  • negative 部分减少匹配到"Old Search" 的文档的得分。

  • negative_boost 参数设为 0.3,表示负面查询的影响被减弱到 30%,即负面查询的影响较小,但仍然能对相关文档得分产生影响。

  • s c o r e = b a s e S c o r e × ( 1 − n e g a t i v e B o o s t ) score=baseScore×(1−negativeBoost) score=baseScore×(1negativeBoost)

举例:

原始得分:文档 A 的原始得分来自于正向查询,假设为 1.0

负向得分调整:负向查询(“Old Search”)匹配文档 A,所以需要应用 negative_boost 参数。negative_boost 的值是 0.3,所以我们用原始得分乘以 negative_boost

  • 1.0 * 0.3 = 0.3

最终得分:最终的得分就是调整后的得分,0.3

boostnegative_boost 都是 Elasticsearch 中用于调整查询结果得分的参数,但它们的作用是不同的。下面是它们的主要区别:

作用对象

  • boost:用于增加文档的相关性得分。它通过给文档的得分添加一个额外的权重来让它们在搜索结果中排名更靠前。一般来说,boost 的值大于 1 会提高匹配文档的得分。
  • negative_boost:用于减少文档的相关性得分。它通过给文档的得分施加一个负面影响,降低它们在搜索结果中的排名。negative_boost 的值在 0 到 1 之间,值越小,影响越大。
2.4 Function Score Query

Function Score Query 允许你根据文档的某些属性(例如字段值、距离、时间等)动态计算和加权文档得分。通过这种方式,你可以通过自定义评分函数来调整文档得分,通常是基于特定的业务逻辑。

应用场景:当你希望根据文档的某些属性(如流行度、发布时间等)动态计算文档得分时,可以使用 function_score 查询。比如,你希望增加热门文章的得分,或者根据文档的发布时间调整它们的排名。

score_mode、modifier 和 boost_mode

在 Elasticsearch 中,score_modemodifierboost_mode 都是与评分相关的参数,它们的作用如下:

  1. score_mode
  • 作用:控制如何将多个函数的得分合并。由于 Elasticsearch 支持在查询中使用多个得分函数,score_mode 决定了这些函数得分是如何组合的。
  • 常用值
    • multiply(默认值):将函数得分相乘。适用于函数得分之间相互影响的情况。
    • sum:将所有函数得分相加。适用于希望所有函数的影响力叠加的情况。
    • avg:对所有函数得分取平均值。适用于平衡多个函数对最终得分的贡献。
    • first:只使用第一个匹配的函数得分。如果有多个函数,可以选择哪个函数的得分对最终结果有影响。
    • max:使用最大得分。适用于只关心某个函数得分的最大值的情况。
    • min:使用最小得分。适用于只关心某个函数得分的最小值的情况。
  1. modifier
  • 作用modifier 是某些得分函数(如 field_value_factor)的一个附加选项,用来进一步调整得分计算的方式。它影响得分函数的计算逻辑,通常用于基于字段值来修改得分的情况。
  • 常用值
    • none(默认值):不做任何修改。
    • log:对字段值进行对数转换,用来减缓得分的增幅。
    • log1p:对字段值进行 log(x+1) 转换,避免字段值为 0 时出错。
    • square:将字段值平方,用来增加字段值对得分的影响。
    • sqrt:对字段值进行平方根转换,减小字段值对得分的影响。
  1. boost_mode
  • 作用boost_mode 用来控制函数得分与查询得分(基础查询得分)如何组合。它定义了查询得分和函数得分的最终得分是如何结合的。
  • 常用值
    • multiply:将查询得分与函数得分相乘。通常用于加权函数得分的情况。
    • sum:将查询得分与函数得分相加。通常用于希望查询得分与函数得分的影响叠加的情况。
    • avg:将查询得分与函数得分的平均值作为最终得分。
    • first:只使用查询得分或第一个匹配的函数得分中的一个。
    • max:选择查询得分和函数得分中的最大值。
    • min:选择查询得分和函数得分中的最小值。

示例:

{
  "query": {
    "function_score": {
      "query": {
        "match": {
          "content": "Elasticsearch"
        }
      },
      "boost": 1.0,  // 默认的加权因子
      "functions": [
        {
          "field_value_factor": {
            "field": "popularity",  // 根据 popularity 字段调整得分
            "modifier": "log1p",     //modifier 是某些得分函数(如 field_value_factor)的一个附加选项,用来进一步调整得分计算的方式。它影响得分函数的计算逻辑,通常用于基于字段值来修改得分的情况。
            "factor": 2.0           // 放大影响因子
          },
          "weight": 1.5  // 给这个函数添加权重
        },
        {
          "gauss": {
            "date": {
              "origin": "2025-01-01",
              "scale": "10d",  // 距离目标日期 10 天内的文档得分会更高
              "offset": "5d",  // 距离目标日期 5 天外的文档得分会降低
              "decay": 0.5     // 衰减因子
            }
          },
          "weight": 2.0  // 给这个函数添加权重
        }
      ],
      "score_mode": "sum",     //控制如何将多个函数的得分合并。由于 Elasticsearch 支持在查询中使用多个得分函数,score_mode 决定了这些函数得分是如何组合的。
      "boost_mode": "multiply" // boost_mode 用来控制函数得分与查询得分(基础查询得分)如何组合。它定义了查询得分和函数得分的最终得分是如何结合的。
    }
  }
}

解析:

  • match 查询:查询内容包含 “Elasticsearch” 的文档。
  • 第一个函数field_value_factor 根据 popularity 字段来调整得分,使用 log1p 修改器进行对数加法,factor 为 2.0。这个函数的得分还被设置了权重 1.5,表示该函数对最终得分的影响会乘以 1.5。
  • 第二个函数gauss 函数根据 date 字段的值与给定的目标日期进行比较,并应用衰减函数来调整得分。这个函数的得分也被设置了权重 2.0
  • score_mode:选择 sum,表示所有函数得分会相加。
  • boost_mode:选择 multiply,表示最终得分会是查询得分与函数得分的乘积。

模拟得分计算

  • 基础查询得分(match 查询)

    • 假设基础查询得分为 3.0,这是从 match 查询中得到的。
  • 第一个函数(field_value_factor

    • 假设 popularity 字段的值为 10
    • 使用 log1p 进行调整:log⁡(10+1)=log⁡(11)≈2.3979
    • 然后乘以 factor(2.0)来放大:2.3979×2.0=4.79582.3979
    • 权重 1.5 的影响:4.7958×1.5=7.19374.7958
  • 第二个函数(gauss

    • 假设 date 字段的值与目标日期(2025-01-01)相差 7 天。
    • 使用给定的参数:
      • scale10d,表示如果距离目标日期越近,得分越高。
      • offset5d,表示超过 5d 后衰减效果会逐渐减弱。
      • decay0.5,表示衰减因子的影响。
    • 计算衰减得分:假设衰减因子为 0.8(这个是通过高斯衰减计算得出的),那么得分是 0.8
    • 权重 2.0 的影响:0.8×2.0=1.6
  • score_modesum:函数得分相加。

    • 7.1937 + 1.6 = 8.7937
  • boost_modemultiply:最终得分是查询得分与函数得分的乘积。

    • 3.0(基础得分) * 8.7937(函数得分) = 26.3811
总结:
  • Field-Level Boosting:通过为字段设置 boost 参数,提升某些字段匹配的文档得分。
  • negative-Boosting 查询:通过 positivenegative 查询,以及 negative_boost 来增强和减少文档得分。
  • Function Score Query:通过自定义的函数(例如基于字段值、时间、距离等)动态调整文档得分。

每种方式都有不同的应用场景,可以根据具体需求来选择使用,帮助你更加精细地控制搜索结果的排序和相关性。

multi_match在Elasticsearch 查询中的应用

在 Elasticsearch 中,multi_match 查询有以下几种查询类型,每种类型都有其特定的应用场景和工作原理:

1. Best Fields(默认类型)

best_fieldsmulti_match 查询的默认类型,它的目标是选择最匹配的单个字段,并根据该字段的匹配程度对文档进行排序。

工作原理:
  • 在多个字段中,best_fields 会选择得分最高的字段来决定文档的最终排名。l
  • 适用于字段之间有明显优先级,并且你希望只考虑最相关的字段来排序文档的情况。
适用场景:
  • 如果你认为某些字段比其他字段更重要,并且你希望查询结果由最相关的单一字段决定排名。
示例:
{
  "query": {
    "multi_match": {
      "query": "Elasticsearch search",  // 查询的搜索字符串
      "fields": ["title", "description"],  // 查询的字段
      "type": "best_fields",  // 查询类型为最佳字段
      "tie_breaker": 0.3  // tie_breaker 设置为 0.3,表示当得分相同或接近时,合并字段得分时给予 30% 的加权
    }
  }
}

在上面的查询中,如果 title 字段的得分高于 descriptionbest_fields 将会基于 title 字段的匹配度来排序文档。

模拟计算得分:

根据 best_fields 查询类型,Elasticsearch 会选择每个文档中得分最高的字段来计算基础得分。如果两个字段的得分接近,tie_breaker 会起作用,增加合并字段得分的加权。

Document 1:

  • title 字段得分:2.5description 字段得分:1.8
  • 选择 title 字段,因为它的得分更高,最终得分为 2.5
    • 无需使用 tie_breaker,因为没有得分相同的情况。

Document 3:

  • title 字段得分:2.8description 字段得分:2.7
  • 两个字段的得分较接近,tie_breaker 参数为 0.3,所以使用 tie_breaker 来合并两个字段的得分。

final score = 2.8 + ( 2.7 × 0.3 ) = 2.8 + 0.81 = 3.61 \text{final score} = 2.8 + (2.7 \times 0.3) = 2.8 + 0.81 = 3.61 final score=2.8+(2.7×0.3)=2.8+0.81=3.61


2. Most Fields

most_fields 查询将多个字段的得分加在一起,返回得分最高的文档。它不是选择最匹配的单一字段,而是将多个字段的匹配度累加,从而影响文档的排序。

工作原理:
  • 会将多个字段的得分加总,并根据加总的得分对文档进行排序。
  • 适用于所有字段都对文档的相关性有影响的场景。
适用场景:
  • 当多个字段对查询结果都很重要,并且你希望将它们的得分合并来影响最终的排序时。
示例:
{
  "query": {
    "multi_match": {
      "query": "Elasticsearch tutorial",
      "fields": ["title", "description"],
      "type": "most_fields"
    }
  }
}

在这个例子中,titledescription 字段的得分会被加总,而不是单独评估得分最高的字段。

模拟计算得分

Document 2

  • title 字段得分:2.2(匹配 "Elasticsearch")。

  • description 字段得分:3.0(匹配 "Elasticsearch""tutorial")。

  • titledescription 字段分别匹配了查询字符串 "Elasticsearch""tutorial",但由于 most_fields 的策略,如果多个字段匹配,则它们的得分会叠加。

  • 最终得分计算
    f i n a l s c o r e = t i t l e s c o r e + d e s c r i p t i o n s c o r e = 2.2 + 3.0 = 5.2 final score=title score+description score=2.2+3.0=5.2 finalscore=titlescore+descriptionscore=2.2+3.0=5.2


3. Cross Fields

cross_fields 类型将多个字段视为一个联合字段进行查询,它不会考虑字段顺序,而是将多个字段的内容合并在一起进行匹配。此类型非常适用于当多个字段的内容相互配合时。

工作原理:
  • cross_fields 会将多个字段的内容拼接在一起,忽略字段的顺序,并将它们当作一个单一字段进行匹配。
  • 适用于字段之间没有顺序要求,并且它们的组合内容对查询的相关性更重要的场景。
适用场景:
  • 当多个字段的内容配合起来更有意义,而不仅仅是字段中的单个部分时,cross_fields 会通过联合这些字段的内容来进行查询。
示例:
{
  "query": {
    "multi_match": {
      "query": "Elasticsearch tutorial",
      "fields": ["title", "description"],
      "type": "cross_fields"
    }
  }
}

在这个例子中,cross_fields 会将 titledescription 字段视为一个整体,进行联合分析。

模拟计算得分:

cross_fields 查询类型中,所有字段的内容会视为一个整体,而不是单独计算每个字段的得分。因此,得分的计算方式更加综合。

  • title 字段得分: Elasticsearch tutorial 匹配 title 字段,得分为 2.8
  • description 字段得分: Elasticsearch tutorial 也匹配 description 字段,得分为 2.0

关键区别:cross_fields 查询类型下,得分计算不仅仅是加法,还会综合考虑多个字段匹配的情况。假设最终得分基于所有字段的交集与整体匹配,得分可能会综合平衡两个字段的贡献。

例如,假设最终得分是:

Final score = (title得分 + description得分) / 2
            = (2.8 + 2.0) / 2
            = 2.4

最终得分:

  • cross_fields 查询类型的得分可能会相对低一些,因为它综合了两个字段的得分,而不是简单地加总。

总结
查询类型描述适用场景
Best Fields选择最匹配的单个字段,按该字段的得分排序。当你希望查询结果由最相关的单一字段决定排序时。
Most Fields将多个字段的得分加总,影响最终的排序。当多个字段对文档相关性都有影响时,适合将它们的得分合并。
Cross Fields将多个字段的内容视为一个整体进行匹配,忽略字段顺序。当多个字段内容配合起来更有意义,而不仅仅是单一字段时。

根据你的具体需求,可以选择合适的 multi_match 查询类型,以优化查询结果的相关性和排序。如果你有更多问题,随时欢迎继续提问!

聚合操作概述

Elasticsearch 中,聚合操作(Aggregation)是一个强大的特性,用于从数据中提取统计信息,生成报告或进行数据分析。通过聚合操作,您可以对数据进行分组、计数、平均、最大值、最小值等操作,从而对大量数据进行有效的分析和探索。

聚合操作是 Elasticsearch 查询的一部分,主要包括以下类型:

  1. 桶聚合(Bucket Aggregations):用于将文档分组(桶),每个桶包含满足某些条件的文档。
  2. 度量聚合(Metric Aggregations):用于对文档的某个字段进行数值计算,像是求平均值、总和、最小值、最大值等。
  3. 管道聚合(Pipeline Aggregations):对其他聚合结果进行进一步计算或处理,通常是基于已经计算出来的度量值。
  4. 复合聚合(Composite Aggregations):允许进行大规模的分组,能够处理超过聚合结果限制的情况。

1. 桶聚合(Bucket Aggregations)

桶聚合是将文档分为若干组,每个桶包含符合条件的文档。例如,可以根据某个字段的值将文档分组。

常见的桶聚合有:

  • terms 聚合:将文档按某个字段的值进行分组。
  • range 聚合:将文档按字段值范围进行分组。
  • date_histogram 聚合:根据日期范围(例如每天、每月)进行分组。
  • histogram 聚合:基于数值字段的固定间隔将文档分组。
示例:terms 聚合
{
  "query": {
    "match_all": {}  // 查询所有文档
  },
  "aggs": {
    "price_terms": {
      "terms": {
        "field": "price",  // 基于 price 字段进行聚合
        "size": 10  // 返回前 10 个出现频率最高的 price 值
      }
    }
  }
}

这个例子中,文档将按照 price 字段的值进行分组,返回 price 字段的前 10 个最常见的值。

返回数据:
{
  "took": 45,  // 查询执行的时间(毫秒)
  "timed_out": false,  // 查询是否超时
  "_shards": {
    "total": 5,  // 总的分片数
    "successful": 5,  // 成功的分片数
    "skipped": 0,  // 被跳过的分片数
    "failed": 0  // 失败的分片数
  },
  "hits": {
    "total": {
      "value": 500,  // 总文档数
      "relation": "eq"
    },
    "max_score": 1.0,  // 查询文档的最大分数(通常用于排序)
    "hits": [
      {
        "_index": "products",  // 文档所在的索引
        "_id": "1",  // 文档的ID
        "_score": 1.0,  // 文档的相关性得分
        "_source": {
          "price": 19.99,
          "name": "Product 1",
          "category": "Electronics"
        }
      },
      {
        "_index": "products",
        "_id": "2",
        "_score": 1.0,
        "_source": {
          "price": 29.99,
          "name": "Product 2",
          "category": "Clothing"
        }
      },
      // 其他文档...
    ]
  },
  "aggregations": {
    "price_terms": {
      "buckets": [
        {
          "key": 19.99,  // 聚合到的 price 值
          "doc_count": 120  // 该价格对应的文档数量
        },
        {
          "key": 29.99,
          "doc_count": 95
        },
        {
          "key": 49.99,
          "doc_count": 85
        },
        {
          "key": 9.99,
          "doc_count": 80
        },
        {
          "key": 39.99,
          "doc_count": 75
        },
        {
          "key": 59.99,
          "doc_count": 70
        },
        {
          "key": 99.99,
          "doc_count": 60
        },
        {
          "key": 79.99,
          "doc_count": 50
        },
        {
          "key": 149.99,
          "doc_count": 45
        },
        {
          "key": 199.99,
          "doc_count": 40
        }
      ]
    }
  }
}
2. range 聚合

range 聚合用于对某个字段的值进行范围分组统计。

查询:

假设我们有一个 price 字段,想要对价格进行分段统计。

{
  "size": 0,  //"size": 0 的意思是 不返回任何查询结果,只返回聚合结果
  "aggs": {
    "price_range": {
      "range": {
        "field": "price",
        "ranges": [
          { "to": 50 },
          { "from": 50, "to": 100 },
          { "from": 100 }
        ]
      }
    }
  }
}
返回数据:
{
  "aggregations": {
    "price_range": {
      "buckets": [
        {
          "key": "*-50.0",
          "to": 50,
          "doc_count": 200
        },
        {
          "key": "50.0-100.0",
          "from": 50,
          "to": 100,
          "doc_count": 150
        },
        {
          "key": "100.0-*",
          "from": 100,
          "doc_count": 120
        }
      ]
    }
  }
}
3. date_histogram 聚合

date_histogram 聚合用于根据日期字段进行时间间隔统计,常用于按天、月、年等时间单位进行分组。

查询:

假设我们有一个 timestamp 字段,想要按天统计销售数据。

{
  "size": 0,
  "aggs": {
    "sales_over_time": {
      "date_histogram": {
        "field": "timestamp",
        "calendar_interval": "day"  // 按天进行分组
      }
    }
  }
}
返回数据:
{
  "aggregations": {
    "sales_over_time": {
      "buckets": [
        {
          "key_as_string": "2025-01-01T00:00:00.000Z",
          "key": 1640995200000,
          "doc_count": 50
        },
        {
          "key_as_string": "2025-01-02T00:00:00.000Z",
          "key": 1641081600000,
          "doc_count": 70
        },
        {
          "key_as_string": "2025-01-03T00:00:00.000Z",
          "key": 1641168000000,
          "doc_count": 90
        }
      ]
    }
  }
}
4. histogram 聚合

histogram 聚合类似于 date_histogram,但它基于数字字段进行分组。你可以设置间隔值(如每 10 个单位)。

查询:

假设我们有一个 price 字段,想要按价格区间进行统计。

{
  "size": 0,
  "aggs": {
    "price_histogram": {
      "histogram": {
        "field": "price",
        "interval": 10  // 每 10 单位为一个区间
      }
    }
  }
}
返回数据:
{
  "aggregations": {
    "price_histogram": {
      "buckets": [
        {
          "key": 0,
          "doc_count": 50
        },
        {
          "key": 10,
          "doc_count": 30
        },
        {
          "key": 20,
          "doc_count": 45
        },
        {
          "key": 30,
          "doc_count": 60
        }
      ]
    }
  }
}
总结:
  • terms 聚合 用于对字段的不同值进行分组,统计每个值出现的次数。
  • range 聚合 用于对字段值进行范围分组统计,常用于数值或日期字段。
  • date_histogram 聚合 用于按时间间隔(如天、月等)进行分组,适用于日期字段。
  • histogram 聚合 用于按数值区间进行分组,适用于数值字段。

每种聚合的返回数据格式通常都会包含一个 buckets 数组,每个 bucket 对应一个分组,其中包含该组的统计数据。

2. 度量聚合(Metric Aggregations)

度量聚合用于计算与文档的数值相关的统计数据。常见的度量聚合包括:

  • avg 聚合:计算字段的平均值。
  • sum 聚合:计算字段的总和。
  • min 聚合:计算字段的最小值。
  • max 聚合:计算字段的最大值。
  • stats 聚合:计算字段的最小值、最大值、平均值、总和等统计信息。
1. avg 聚合 (计算字段的平均值)
查询:

假设我们有一个 price 字段,想要计算价格字段的平均值。

{
  "size": 0,
  "aggs": {
    "avg_price": {
      "avg": {
        "field": "price"
      }
    }
  }
}
返回数据:
{
  "aggregations": {
    "avg_price": {
      "value": 100.5
    }
  }
}

说明: avg 聚合返回的结果包含字段的平均值,这里表示价格的平均值为 100.5


2. sum 聚合 (计算字段的总和)
查询:

假设我们有一个 price 字段,想要计算价格字段的总和。

{
  "size": 0,
  "aggs": {
    "sum_price": {
      "sum": {
        "field": "price"
      }
    }
  }
}
返回数据:
{
  "aggregations": {
    "sum_price": {
      "value": 5000.0
    }
  }
}

说明: sum 聚合返回的结果是字段的总和,这里表示所有价格的总和为 5000.0


3. min 聚合 (计算字段的最小值)
查询:

假设我们有一个 price 字段,想要计算价格字段的最小值。

{
  "size": 0,
  "aggs": {
    "min_price": {
      "min": {
        "field": "price"
      }
    }
  }
}
返回数据:
{
  "aggregations": {
    "min_price": {
      "value": 50.0
    }
  }
}

说明: min 聚合返回字段的最小值,这里表示价格的最小值为 50.0


4. max 聚合 (计算字段的最大值)
查询:

假设我们有一个 price 字段,想要计算价格字段的最大值。

{
  "size": 0,
  "aggs": {
    "max_price": {
      "max": {
        "field": "price"
      }
    }
  }
}
返回数据:
{
  "aggregations": {
    "max_price": {
      "value": 300.0
    }
  }
}

说明: max 聚合返回字段的最大值,这里表示价格的最大值为 300.0


5. stats 聚合 (计算字段的最小值、最大值、平均值、总和等统计信息)
查询:

假设我们有一个 price 字段,想要计算该字段的统计信息(最小值、最大值、平均值和总和)。

{
  "size": 0,
  "aggs": {
    "price_stats": {
      "stats": {
        "field": "price"
      }
    }
  }
}
返回数据:
{
  "aggregations": {
    "price_stats": {
      "count": 100,
      "min": 50.0,
      "max": 300.0,
      "avg": 150.0,
      "sum": 15000.0
    }
  }
}

说明: stats 聚合返回字段的完整统计信息,包括:

  • count: 文档数量,表示该字段有多少个值。
  • min: 最小值。
  • max: 最大值。
  • avg: 平均值。
  • sum: 总和。

这里表示价格字段有 100 个值,最小值为 50.0,最大值为 300.0,平均值为 150.0,总和为 15000.0


总结:
聚合类型查询示例
avg计算平均值
sum计算总和
min计算最小值
max计算最大值
stats计算最小值、最大值、平均值、总和

这些聚合方式在 Elasticsearch 中可以帮助你对数值类型的字段进行各种统计,适用于各种场景,比如计算平均价格、总销量、最小和最大价格等。

3. 管道聚合(Pipeline Aggregations)

管道聚合用于在已聚合结果上执行进一步计算。常见的管道聚合包括:

  • avg_bucket:计算某个桶聚合结果的平均值。
  • sum_bucket:计算某个桶聚合结果的总和。
  • bucket_selector:根据某些条件筛选桶。
1. avg_bucket 聚合 (计算某个桶聚合结果的平均值)
查询:

假设我们有一个按 category 字段进行桶聚合的情况,并希望计算每个桶中 price 字段的平均值。

{
  "size": 0,
  "aggs": {
    "by_category": {
      "terms": {
        "field": "category"
      },
      "aggs": {
        "avg_price": {
          "avg": {
            "field": "price"
          }
        }
      }
    },
    "avg_bucket": {
      "avg_bucket": {
        "buckets_path": "by_category>avg_price"
      }
    }
  }
}
返回数据:
{
  "aggregations": {
    "by_category": {
      "buckets": [
        {
          "key": "electronics",
          "doc_count": 10,
          "avg_price": {
            "value": 100.0
          }
        },
        {
          "key": "clothing",
          "doc_count": 15,
          "avg_price": {
            "value": 50.0
          }
        }
      ]
    },
    "avg_bucket": {
      "value": 75.0
    }
  }
}

说明:

  • avg_bucket 聚合计算了 by_category 桶聚合的平均值。electronicsclothing 两个桶中 price 的平均值分别为 100.050.0,最终结果为这两个平均值的平均值,即 75.0

2. sum_bucket 聚合 (计算某个桶聚合结果的总和)
查询:

假设我们有一个按 category 字段进行桶聚合的情况,并希望计算每个桶中 price 字段的总和。

{
  "size": 0,
  "aggs": {
    "by_category": {
      "terms": {
        "field": "category"
      },
      "aggs": {
        "sum_price": {
          "sum": {
            "field": "price"
          }
        }
      }
    },
    "sum_bucket": {
      "sum_bucket": {
        "buckets_path": "by_category>sum_price"
      }
    }
  }
}
返回数据:
{
  "aggregations": {
    "by_category": {
      "buckets": [
        {
          "key": "electronics",
          "doc_count": 10,
          "sum_price": {
            "value": 1000.0
          }
        },
        {
          "key": "clothing",
          "doc_count": 15,
          "sum_price": {
            "value": 750.0
          }
        }
      ]
    },
    "sum_bucket": {
      "value": 1750.0
    }
  }
}

说明:

  • sum_bucket 聚合计算了 by_category 桶聚合的总和。electronicsclothing 两个桶中 price 的总和分别为 1000.0750.0,最终结果为这两个总和的总和,即 1750.0

3. bucket_selector 聚合 (根据某些条件筛选桶)
查询:

假设我们有一个按 category 字段进行桶聚合的情况,并希望筛选出总和大于 1000 的桶。

{
  "size": 0,
  "aggs": {
    "by_category": {
      "terms": {
        "field": "category"
      },
      "aggs": {
        "sum_price": {
          "sum": {
            "field": "price"
          }
        },
        "bucket_filter": {
          "bucket_selector": {
            "buckets_path": {
              "total_price": "sum_price"
            },
            "script": "params.total_price > 1000"
          }
        }
      }
    }
  }
}
返回数据:
{
  "aggregations": {
    "by_category": {
      "buckets": [
        {
          "key": "electronics",
          "doc_count": 10,
          "sum_price": {
            "value": 1000.0
          }
        },
        {
          "key": "clothing",
          "doc_count": 15,
          "sum_price": {
            "value": 1500.0
          },
          "bucket_filter": {}
        }
      ]
    }
  }
}

说明:

  • bucket_selector 聚合根据条件筛选桶,这里筛选了总和大于 1000 的桶。只有 clothing 桶的总和大于 1000,所以它被保留下来,而 electronics 桶被过滤掉。

总结:
聚合类型查询示例
avg_bucket计算某个桶的平均值
sum_bucket计算某个桶的总和
bucket_selector根据条件筛选桶

这三种聚合方式常常用于基于现有桶聚合结果的进一步统计和筛选,avg_bucketsum_bucket 聚合分别对桶中的结果进行平均或求和,而 bucket_selector 则用于根据特定条件对桶进行筛选。

4. 复合聚合(Composite Aggregations)

复合聚合(composite aggregation)是一种特殊类型的聚合,用于在 Elasticsearch 中进行分组和分页。与传统的聚合方式不同,复合聚合允许你对多个字段同时进行聚合,并能实现高效的分页,尤其适用于需要大量结果集的场景。

复合聚合非常适合用于大规模数据的场景,尤其是在需要对多个字段进行分组时。例如,如果你需要基于产品的类别和品牌进行分组并分页返回结果,复合聚合可以帮助你有效地实现这一点。

主要特点:
  • 多字段聚合:允许对多个字段进行组合聚合。
  • 高效分页:可以对大数据集进行分页,返回有限的桶(bucket)。
  • 不需要全量排序:因为复合聚合使用了游标机制,可以分页获取结果集。
复合聚合的基本结构
{
  "size": 0,
  "aggs": {
    "composite_agg_name": {
      "composite": {
        "sources": [
          {
            "field1": {
              "terms": {
                "field": "field1"
              }
            }
          },
          {
            "field2": {
              "terms": {
                "field": "field2"
              }
            }
          }
        ],
        "after": { "field1": "value1", "field2": "value2" }
      }
    }
  }
}
  • sources:指定一个或多个字段来进行分组。每个字段可以使用 terms 聚合来定义。
  • after:如果使用分页,这个字段会指定上次聚合的最后一个值,从而实现分页。
请求示例(复合聚合)

假设我们有一个名为 products 的索引,其中包含 categorybrand 字段,我们希望根据 categorybrand 对产品进行分组,并进行分页。

{
  "size": 0,
  "aggs": {
    "category_brand": {
      "composite": {
        "sources": [
          {
            "category": {
              "terms": {
                "field": "category"
              }
            }
          },
          {
            "brand": {
              "terms": {
                "field": "brand"
              }
            }
          }
        ]
      }
    }
  }
}
返回结果示例(复合聚合)
{
  "aggregations": {
    "category_brand": {
      "buckets": [
        {
          "key": {
            "category": "electronics",
            "brand": "Apple"
          },
          "doc_count": 5
        },
        {
          "key": {
            "category": "electronics",
            "brand": "Samsung"
          },
          "doc_count": 3
        },
        {
          "key": {
            "category": "clothing",
            "brand": "Nike"
          },
          "doc_count": 8
        },
        {
          "key": {
            "category": "clothing",
            "brand": "Adidas"
          },
          "doc_count": 6
        }
      ]
    }
  }
}

解释:

  • category_brand 聚合根据 categorybrand 字段进行组合分组。

  • 每个桶(bucket)代表一种

    category
    

    brand
    

    的组合,并返回每个组合下的文档数量(

    doc_count
    

    )。

    • category: electronics, brand: Apple 组合的文档数量是 5。
    • category: clothing, brand: Nike 组合的文档数量是 8。
带分页的复合聚合请求示例

为了实现分页,我们需要使用 after 参数。假设我们已经从上面的请求中获得了第一页的数据,并希望获取第二页的数据。

{
  "size": 0,
  "aggs": {
    "category_brand": {
      "composite": {
        "sources": [
          {
            "category": {
              "terms": {
                "field": "category"
              }
            }
          },
          {
            "brand": {
              "terms": {
                "field": "brand"
              }
            }
          }
        ],
        "after": {
          "category": "clothing",
          "brand": "Adidas"
        }
      }
    }
  }
}
返回结果示例(带分页的复合聚合)
{
  "aggregations": {
    "category_brand": {
      "buckets": [
        {
          "key": {
            "category": "electronics",
            "brand": "Sony"
          },
          "doc_count": 2
        },
        {
          "key": {
            "category": "clothing",
            "brand": "Puma"
          },
          "doc_count": 4
        }
      ]
    }
  }
}

解释:

  • 我们通过 after 参数获取了 category: clothingbrand: Adidas 的组合之后的数据。这些数据是分页的“下一页”结果。
复合聚合的优势:
  1. 高效分页:通过使用游标机制,复合聚合允许对大数据集进行分页,而不会一次性加载所有数据。
  2. 灵活性:支持多个字段组合聚合,能够根据不同的需求进行灵活配置。
  3. 适用于大数据集:对于需要处理大量桶的聚合场景,复合聚合非常有效。
总结:

复合聚合是一种高级聚合方式,允许用户基于多个字段进行分组,并支持高效的分页操作。它常用于处理大规模数据时,特别是需要对多个维度进行分组时(如产品类别、品牌等)。通过复合聚合,Elasticsearch 可以为你提供灵活的结果分页功能,避免了传统聚合方式中一次性加载所有数据的性能问题。

Elasticsearch 的聚合操作非常强大,可以帮助你从数据中提取各种统计信息,包括分组、计数、求平均、求最大最小值等。常见的聚合类型包括:

  • 桶聚合(Bucket Aggregations):按字段值范围、日期等分组。
  • 度量聚合(Metric Aggregations):对字段进行统计,如求平均、总和等。
  • 管道聚合(Pipeline Aggregations):对其他聚合结果进行计算。
  • 复合聚合(Composite Aggregations):用于处理大规模的分组,支持分页。

通过合理使用聚合,你可以轻松地对 Elasticsearch 中存储的大量数据进行分析和总结。

Elasticsearch 聚合作用范围及控制

Elasticsearch 中,聚合分析的默认作用范围是查询的查询结果集,所有聚合操作都会基于查询的结果集进行计算。你可以通过以下方式改变聚合的作用范围:

  1. Filter Aggregation:限制聚合只对符合某个条件的文档进行计算。
  2. Post Filter Aggregation:应用聚合后过滤文档,适用于你想要过滤聚合结果而不影响聚合本身。
  3. Global Aggregation:允许聚合作用于整个数据集,而不受查询的限制。

以下是这三种方式的请求和返回结果示例:

1. Filter Aggregation

Filter 聚合允许你在聚合之前通过条件筛选文档,从而改变聚合的作用范围。

请求示例
{
  "query": {
    "match_all": {}  // 查询所有文档
  },
  "aggs": {
    "filtered_avg_price": {
      "filter": {
        "range": {
          "price": {
            "gte": 100  // 只对价格大于等于 100 的文档进行聚合
          }
        }
      },
      "aggs": {
        "avg_price": {
          "avg": {
            "field": "price"  // 计算筛选后文档的平均价格
          }
        }
      }
    }
  }
}
返回结果
{
  "aggregations": {
    "filtered_avg_price": {
      "doc_count": 50,
      "avg_price": {
        "value": 150.75
      }
    }
  }
}

在这个示例中,filter 聚合只对价格大于等于 100 的文档进行计算,结果显示了筛选文档后的平均价格。


2. Post Filter Aggregation

Post Filter 聚合应用于聚合结果后,适用于你想过滤聚合结果而不影响聚合计算本身。

请求示例
{
  "query": {
    "match_all": {}  // 查询所有文档
  },
  "aggs": {
    "total_sales": {
      "terms": {
        "field": "product"  // 对产品字段进行聚合
      }
    }
  },
  "post_filter": {
    "range": {
      "price": {
        "gte": 50  // 只显示价格大于等于 50 的结果
      }
    }
  }
}
返回结果
{
  "aggregations": {
    "total_sales": {
      "buckets": [
        {
          "key": "Product A",
          "doc_count": 100
        },
        {
          "key": "Product B",
          "doc_count": 50
        }
      ]
    }
  },
  "hits": {
    "total": {
      "value": 150,
      "relation": "eq"
    },
    "hits": [
      {
        "_source": {
          "product": "Product A",
          "price": 60
        }
      },
      {
        "_source": {
          "product": "Product B",
          "price": 70
        }
      }
    ]
  }
}

在这个示例中,聚合结果显示了所有产品的销售数量,post_filter 只对返回结果中的文档进行筛选,展示了价格大于等于 50 的产品。


3. Global Aggregation

Global 聚合作用于整个数据集,而不受查询结果的限制,适用于你希望对整个数据集进行聚合计算的场景。

请求示例
{
  "query": {
    "match_all": {}  // 查询所有文档
  },
  "aggs": {
    "global_avg_price": {
      "global": {},  // 作用于整个数据集
      "aggs": {
        "avg_price": {
          "avg": {
            "field": "price"  // 计算整个数据集的平均价格
          }
        }
      }
    }
  }
}
返回结果
{
  "aggregations": {
    "global_avg_price": {
      "avg_price": {
        "value": 120.5
      }
    }
  }
}

在这个示例中,global 聚合作用于整个数据集,而不受查询的过滤条件影响,计算整个数据集的平均价格。


总结
  1. Filter Aggregation:用于在聚合计算之前根据条件过滤文档。
  2. Post Filter Aggregation:用于聚合计算后对结果进行过滤。
  3. Global Aggregation:用于对整个数据集进行聚合计算,忽略查询的限制。

这三种聚合方式都能改变聚合的作用范围,但它们的使用场景不同。

ES三种不同聚合方式与mysql比对

它们的行为与 MySQL 中的 WHEREHAVING 子句有一些相似性,下面是具体的对比解释:

Filter 聚合 ≈ WHERE

在 MySQL 中,WHERE 子句用于过滤查询中的数据行。类似地,Elasticsearch 中的 Filter 聚合 在聚合计算之前通过特定条件筛选文档,类似于在数据库查询中对数据的预处理。它会先过滤符合条件的文档,再进行聚合操作。

MySQL 示例:
SELECT product, COUNT(*) FROM sales WHERE price >= 100 GROUP BY product;
Elasticsearch 示例:
{
  "query": {
    "match_all": {}  // 查询所有文档
  },
  "aggs": {
    "filtered_avg_price": {
      "filter": {
        "range": {
          "price": {
            "gte": 100  // 只对价格大于等于 100 的文档进行聚合
          }
        }
      },
      "aggs": {
        "avg_price": {
          "avg": {
            "field": "price"  // 计算筛选后文档的平均价格
          }
        }
      }
    }
  }
}
Post Filter 聚合 ≈ HAVING

在 MySQL 中,HAVING 子句用于过滤 聚合后的结果,即对 GROUP BY 操作后的结果进行过滤。类似地,Elasticsearch 中的 Post Filter 聚合 用于聚合计算后过滤结果集,它不会影响聚合的计算过程,只会在返回结果时进行后处理。

MySQL 示例:
SELECT product, AVG(price) FROM sales GROUP BY product HAVING AVG(price) >= 100;
Elasticsearch 示例:
{
  "query": {
    "match_all": {}  // 查询所有文档
  },
  "aggs": {
    "total_sales": {
      "terms": {
        "field": "product"  // 对产品字段进行聚合
      }
    }
  },
  "post_filter": {
    "range": {
      "price": {
        "gte": 50  // 只显示价格大于等于 50 的聚合结果
      }
    }
  }
}
Global 聚合 ≈ 全表聚合

在 MySQL 中,没有 WHERE 或 HAVING 子句的聚合会作用于整个数据集,类似于 Elasticsearch 中的 Global 聚合Global 聚合不受查询的限制,它对整个数据集进行聚合,而不是仅对查询的结果进行聚合。

MySQL 示例:
SELECT AVG(price) FROM sales;
Elasticsearch 示例:
{
  "query": {
    "match_all": {}  // 查询所有文档
  },
  "aggs": {
    "global_avg_price": {
      "global": {},  // 作用于整个数据集
      "aggs": {
        "avg_price": {
          "avg": {
            "field": "price"  // 计算整个数据集的平均价格
          }
        }
      }
    }
  }
}
总结对比
  • Filter WHERE:在聚合前过滤文档,决定哪些文档参与聚合。
  • Post Filter HAVING:聚合完成后,对聚合结果进行过滤。
  • Global 全表聚合:忽略查询限制,作用于整个数据集,计算全局聚合结果。

在 Elasticsearch 中,排序操作允许你根据文档中的字段或聚合结果对查询结果进行排序。Elasticsearch 提供了多种排序方式,如按字段值排序、按评分排序、按多种字段组合排序等。

Elasticsearch 聚合数据不精准的场景以及解决方案

Elasticsearch 聚合数据不精准的原因可能有多种,下面列出了一些常见的原因及相应的处理方式:

ES聚合不精确可能的原因
1. 文档数据的不完整或不准确
  • 问题:聚合结果可能会受到文档中数据本身质量的影响。例如,某些字段缺失、格式不一致,或者数据没有正确索引。
  • 解决方案
    • 确保数据在索引时完整,字段格式一致,并且数据没有丢失。
    • 使用 index 操作时,可以对数据进行预处理,确保字段的标准化。
    • 对于数值字段,确保没有意外的 null 或空值。
2. 聚合数据精度问题
  • 问题:聚合结果可能不精确,尤其是在处理大量数据时。这是因为 Elasticsearch 聚合使用了 桶聚合(bucket aggregation),在某些情况下,数据会被分桶或采样,导致聚合结果不准确。
  • 解决方案
    • 使用 precision_thresholdshard_size 参数提高聚合的精度。
    • 增加聚合的 sizeshard_size 值,以便更多的文档参与聚合。
3. 分片问题
  • 问题:Elasticsearch 默认会将数据分片存储在多个节点上,聚合的结果可能会因为数据分布不均、分片偏差等问题而不精确。分片过多或过少都会影响聚合的效果。
  • 解决方案
    • 调整分片数量,通常建议使用 适当的分片数,避免分片过多。
    • 对于某些聚合,可以使用 shard_size 参数来指定在分片中参与聚合的文档数量,以便从更多的分片中获取结果。
4. 聚合精度和内存限制
  • 问题:在聚合大量数据时,Elasticsearch 可能会因为内存限制而丢弃某些数据,导致聚合结果不精准。默认情况下,Elasticsearch 会在内存中存储聚合结果,如果数据量过大,可能会导致精度丧失。
  • 解决方案
    • 增加内存限制,通过调整 JVM 堆内存配置来提高聚合精度。
    • 使用 size 参数来限制聚合结果返回的条目数量,减少聚合过程中的内存消耗。
    • 使用 doc_values 来加速数值字段的聚合操作。
5. 精度控制和近似值
  • 问题:某些聚合类型,如 terms 聚合,在处理大量不同的值时,会进行近似计算,从而可能导致数据的误差或偏差。
  • 解决方案
    • 对于 terms 聚合,使用 execution_hint 参数调整聚合的执行方式(如使用 mapglobal_ordinals),以提高精度。
    • 如果需要更加精确的聚合,尽量避免在字段上进行高基数的 terms 聚合。
    • 对于数值类型的聚合,可以使用 precision_thresholdextended_bounds 等配置来控制聚合精度。
6. 数据类型和映射问题
  • 问题:数据类型不匹配或映射错误可能导致聚合结果不准确。例如,数值字段被错误地映射为字符串,或在文档中有错误的类型。
  • 解决方案
    • 确保数据字段的映射是正确的,并且所有文档中的相同字段类型一致。
    • 对于数值聚合,确保该字段已经被映射为正确的数值类型(如 integerfloatdouble 等)。
7. 聚合排序和限制
  • 问题:聚合排序可能导致某些聚合结果被遗漏或不准确,尤其是在有 terms 聚合时,如果聚合值的数量超过了限制,会导致丢失一些值。
  • 解决方案
    • 使用 sizeshard_size 参数,确保聚合查询返回足够多的结果。
    • 对于高基数的字段,考虑使用 composite 聚合来分段处理结果,以避免遗漏部分数据。
8. 分布式聚合不一致
  • 问题:因为 Elasticsearch 是一个分布式系统,聚合结果有时会受到数据分布和分片策略的影响,导致聚合结果出现不一致性。
  • 解决方案
    • 使用 global 聚合 结合 filter 聚合,可以确保跨所有分片的聚合一致性。
    • 调整 min_doc_countsize 以保证充分的聚合结果。
9. 并发聚合导致的延迟和不一致
  • 问题:当有多个聚合操作同时进行时,可能会导致计算延迟或聚合结果的不一致。
  • 解决方案
    • 可以通过 reduce 参数控制聚合的最终结果,确保所有聚合操作能够正确整合。
    • 适当使用 parallelism 参数来控制聚合的并发度,以确保高效计算。
ES聚合查询的相关优化方案
1. 启用 Eager Global Ordinals 提升高基数聚合性能
  • 性能:高
  • 常用度:高
    启用 eager global ordinals 可以提高高基数聚合(如 terms 聚合)的性能。当聚合涉及大量不同的值时,默认情况下 Elasticsearch 会延迟加载全局 ordinals,直到需要时才会加载。然而,启用 eager global ordinals 会提前加载所有的 ordinals,从而避免聚合过程中频繁的访问 IO 或内存,从而提升查询的性能。

启用方法: 在创建索引时设置字段映射(mapping)中的 index_optionsfreqs(或使用 eager_global_ordinals)。

{
  "mappings": {
    "properties": {
      "field_name": {
        "type": "keyword",
        "index_options": "freqs"
      }
    }
  }
}
2. 使用分片请求缓存(Shard Request Cache)
  • 性能:高
  • 常用度:高
    分片请求缓存可以显著提高重复查询的性能,尤其是在大规模数据集上。当你多次执行相同的查询时,启用分片请求缓存会使 Elasticsearch 将查询结果缓存,以避免重复计算。

启用方法: 在查询时,使用 request_cache 参数启用缓存。

{
  "query": {
    "match": {
      "title": "Elasticsearch"
    }
  },
  "request_cache": true
}

注意:分片请求缓存适合重复查询的场景,针对动态更新的场景不适用。

3. 使用节点查询缓存(Node Query Cache)
  • 性能:高
  • 常用度:中
    节点查询缓存是基于整个节点级别的查询缓存,尤其适合相同查询的重复执行。与分片请求缓存不同,节点查询缓存处理的是多个分片的数据合并时产生的查询结果。

启用方法: 你可以通过集群设置来启用或调整节点查询缓存的大小。

PUT /_cluster/settings
{
  "persistent": {
    "indices.queries.cache.size": "20%"
  }
}
4. 预排序数据(Insert Data in Pre-Sorted Order)
  • 性能:中
  • 常用度:中
    插入数据时按照某些字段(如时间戳、排序键等)预排序数据可以帮助减少 Elasticsearch 在执行查询时的排序成本。特别是对于有排序需求的查询,预排序数据能显著提高性能。

这通常需要在数据插入时进行排序,以便尽量减少查询时对排序的依赖。对于大数据量的排序,预排序可以避免查询时的排序过程,从而提高响应速度。

注意:这一策略要求数据导入过程的预排序,因此在大规模数据导入时,可能会对数据加载的速度产生影响。

5. 合理使用聚合的 sizeshard_size 参数
  • 性能:中
  • 常用度:高
    控制聚合的结果集大小,特别是在执行高基数聚合时,设置合理的 sizeshard_size 可以避免计算不必要的结果并提高性能。
{
  "aggs": {
    "top_tags": {
      "terms": {
        "field": "tag",
        "size": 5,
        "shard_size": 10
      }
    }
  }
}
6. 合理的硬件资源配置
  • 性能:中
  • 常用度:高
    增加硬件资源(如内存、CPU 和存储)可以帮助提升聚合查询性能,尤其是在处理大规模数据集时。在物理资源允许的情况下,增加内存和 CPU 可以显著提高 Elasticsearch 查询和聚合操作的性能。
7. 优化字段映射(Mapping)和数据类型
  • 性能:中
  • 常用度:高
    确保字段映射合理,使用合适的字段类型(如 keyword 替代 text 类型字段)对于提高聚合性能至关重要。text 类型字段进行聚合时,Elasticsearch 需要进行分词和分析,导致内存和 CPU 消耗增大。

建议:对于需要精确聚合的字段,使用 keyword 类型。

"properties": {
  "field_name": {
    "type": "keyword"
  }
}
8. 避免过度使用复杂的嵌套聚合
  • 性能:低
  • 常用度:中
    多层嵌套聚合会大幅增加查询的计算成本,尤其在大规模数据集上。尽量避免复杂的嵌套聚合,或者在必要时使用 composite 聚合来逐步获取结果。
{
  "aggs": {
    "level1": {
      "terms": { "field": "field1" },
      "aggs": {
        "level2": {
          "terms": { "field": "field2" }
        }
      }
    }
  }
}
9. 限制聚合返回结果的数量
  • 性能:中
  • 常用度:中
    对于聚合查询,尤其是高基数聚合,限制返回的桶数(size)可以减少计算的开销,并且提高查询性能。默认情况下,terms 聚合返回 10 个桶,通过合理设置 size 可以优化性能。
{
  "aggs": {
    "top_tags": {
      "terms": {
        "field": "tag",
        "size": 5
      }
    }
  }
}
总结:按性能和常用度的优先级
  1. 启用 Eager Global Ordinals
  2. 使用分片请求缓存(Shard Request Cache)
  3. 使用节点查询缓存(Node Query Cache)
  4. 合理使用聚合的 sizeshard_size 参数
  5. 优化字段映射(Mapping)和数据类型
  6. 预排序数据(Insert Data in Pre-Sorted Order)
  7. 合理的硬件资源配置
  8. 避免过度使用复杂的嵌套聚合
  9. 限制聚合返回结果的数量

Elasticsearch聚合操作的经典使用场景

Elasticsearch 的聚合操作非常强大,能够支持多种不同的使用场景,尤其在数据分析、业务监控、日志分析、推荐系统、数据可视化等领域中都有广泛应用。以下是一些常见的 聚合使用场景,它们展示了如何利用 Elasticsearch 聚合来从海量数据中提取有用信息。

1. 数据分析与报告生成

场景:

在一个电商平台,分析每个品类的销售情况,并生成销售报告,例如每个品类的总销售额、销量、平均价格等。

示例:
  • 聚合类型:terms 聚合 + sum 聚合 + avg 聚合
{
  "aggs": {
    "categories": {
      "terms": {
        "field": "category.keyword"  // 按 `category` 字段进行分组
      },
      "aggs": {
        "total_sales": {
          "sum": {
            "field": "sales"  // 计算每个品类的总销售额
          }
        },
        "average_price": {
          "avg": {
            "field": "price"  // 计算每个品类的平均价格
          }
        }
      }
    }
  }
}
说明:
  • 使用 terms 聚合按 category 字段对品类进行分组。
  • 对每个品类,使用 sum 聚合计算总销售额,使用 avg 聚合计算平均价格。
  • 返回每个品类的销售情况。
业务价值:

通过这种聚合查询,电商平台可以轻松得到各品类的总销售额、销量和平均价格等信息,从而帮助业务决策和优化。


2. 用户行为分析
场景:

在社交媒体平台,分析用户每天的活跃情况(如发布文章或点赞数),并通过日期进行时间趋势分析。

示例:
  • 聚合类型:date_histogram 聚合 + sum 聚合
{
  "aggs": {
    "daily_activity": {
      "date_histogram": {
        "field": "timestamp",    // 按 `timestamp` 字段分组
        "calendar_interval": "day",   // 按天分组
        "format": "yyyy-MM-dd"   // 日期格式化
      },
      "aggs": {
        "total_likes": {
          "sum": {
            "field": "likes"  // 计算每天的点赞数
          }
        }
      }
    }
  }
}
说明:
  • 使用 date_histogram 聚合按日期将文档分组。
  • 对每一天,使用 sum 聚合计算该天的点赞数。
  • 结果中包含每天的点赞总数,用于分析用户活跃度。
业务价值:

这种聚合查询可以帮助社交平台了解用户在不同时间段的活跃情况,并为产品改进、促销活动等提供数据支持。


3. 实时监控与告警
场景:

在一个电子商务平台上,实时监控每个品类的销售数据。如果某个品类的销售额低于预设阈值,触发告警。

示例:
  • 聚合类型:terms 聚合 + sum 聚合 + bucket_selector 聚合
{
  "aggs": {
    "categories": {
      "terms": {
        "field": "category.keyword"  // 按 `category` 字段分组
      },
      "aggs": {
        "total_sales": {
          "sum": {
            "field": "sales"  // 计算每个品类的总销售额
          }
        },
        "low_sales_alert": {
          "bucket_selector": {
            "buckets_path": {
              "total_sales": "total_sales"  // 使用上面计算的销售总额
            },
            "script": "params.total_sales < 1000"  // 销售额低于 1000 的品类触发告警
          }
        }
      }
    }
  }
}
说明:
  • 使用 terms 聚合按品类分组。
  • 对每个品类,使用 sum 聚合计算总销售额。
  • 使用 bucket_selector 聚合设置告警阈值,筛选销售额低于 1000 的品类,进行告警处理。
业务价值:

这种聚合查询可以实时监控品类的销售情况,一旦某个品类的销售额低于预设的阈值,系统将立即触发告警,帮助运营团队及时采取措施。


4. 推荐系统与个性化推荐
场景:

在视频平台,根据用户观看历史分析用户的偏好,并根据这些信息为用户推荐新的视频内容。例如,按照观看频次聚合视频分类,并推荐相似类别的视频。

示例:
  • 聚合类型:terms 聚合 + avg 聚合
{
  "aggs": {
    "favorite_genres": {
      "terms": {
        "field": "genre.keyword",  // 根据 `genre` 字段对用户观看的内容进行分组
        "size": 5  // 返回最常观看的前 5 个类别
      },
      "aggs": {
        "average_rating": {
          "avg": {
            "field": "rating"  // 计算每个视频类别的平均评分
          }
        }
      }
    }
  }
}
说明:
  • 使用 terms 聚合按 genre 字段分组,找出用户最常观看的视频类别。
  • 对每个类别,使用 avg 聚合计算该类别的平均评分。
  • 通过这种方式,可以推荐用户最喜欢的类别或相似评分的视频。
业务价值:

视频平台可以根据用户观看历史和评分情况进行个性化推荐,提高用户体验和平台黏性。


5. 日志分析与故障排查
场景:

在服务器日志中分析错误类型的分布情况,找出最常见的错误类别,以帮助开发人员进行故障排查。

示例:
  • 聚合类型:terms 聚合 + filter 聚合
{
  "aggs": {
    "error_types": {
      "terms": {
        "field": "error_type.keyword"  // 按错误类型分组
      },
      "aggs": {
        "error_count": {
          "value_count": {
            "field": "error_message"  // 统计每种错误类型的出现次数
          }
        }
      }
    }
  }
}
说明:
  • 使用 terms 聚合按错误类型分组。
  • 使用 value_count 聚合计算每种错误类型出现的次数。
  • 通过这种聚合,可以识别最常见的错误类型,帮助快速定位故障。
业务价值:

通过日志聚合,开发团队可以快速了解系统的错误类型和发生频率,进行故障排查,提升系统的稳定性。


6. 时间序列数据分析
场景:

在监控系统中,收集并分析时间序列数据(如 CPU 使用率、内存占用等),并按时间(如小时、天)进行聚合,以便于进行趋势分析和预测。

示例:
  • 聚合类型:date_histogram 聚合 + avg 聚合
{
  "aggs": {
    "hourly_cpu_usage": {
      "date_histogram": {
        "field": "timestamp",  // 按 `timestamp` 字段分组
        "calendar_interval": "hour",  // 按小时分组
        "format": "yyyy-MM-dd HH:00"
      },
      "aggs": {
        "avg_cpu_usage": {
          "avg": {
            "field": "cpu_usage"  // 计算每小时的平均 CPU 使用率
          }
        }
      }
    }
  }
}
说明:
  • 使用 date_histogram 聚合按小时对时间戳进行分组。
  • 对每个小时,使用 avg 聚合计算该小时内的 CPU 使用率。
  • 这种聚合可以帮助管理员实时了解系统负载,提前预警系统过载问题。
业务价值:

该聚合查询可以帮助监控人员及时了解系统负载,进行系统优化或预警,避免系统崩溃。


总结

Elasticsearch 聚合的应用场景非常广泛,几乎适用于所有需要从海量数据中提取统计信息的领域。以下是几个常见的应用场景:

  • 数据分析与报告生成:如电商平台的销售分析和报告生成。
  • 用户行为分析:如社交平台的活跃度分析。
  • 实时监控与告警:如电商平台的品类销售监控。
  • 推荐系统与个性化推荐:如视频平台的个性化推荐。
  • 日志分析与故障排查:如系统错误日志的分析。
  • 时间序列数据分析

Elasticsearch排序

基本排序

最常见的排序方式是按文档中的字段值排序。你可以通过查询中的 sort 参数来指定排序字段。

1. 按字段值排序(升序/降序)

可以对字段进行升序(asc)或降序(desc)排序。

请求示例
{
  "query": {
    "match_all": {}  // 查询所有文档
  },
  "sort": [
    {
      "price": {
        "order": "asc"  // 按价格字段升序排序
      }
    }
  ]
}
返回结果
{
  "hits": {
    "total": {
      "value": 100,
      "relation": "eq"
    },
    "hits": [
      {
        "_source": {
          "product": "Product A",
          "price": 20
        }
      },
      {
        "_source": {
          "product": "Product B",
          "price": 50
        }
      },
      {
        "_source": {
          "product": "Product C",
          "price": 100
        }
      }
    ]
  }
}

在这个例子中,文档按 price 字段的升序排列。


2. 按多个字段排序

你也可以按多个字段排序,Elasticsearch 会按顺序逐一比较字段,直到确定文档的排序顺序。

请求示例
{
  "query": {
    "match_all": {}  // 查询所有文档
  },
  "sort": [
    {
      "price": {
        "order": "asc"  // 首先按价格升序排序
      }
    },
    {
      "product": {
        "order": "desc"  // 如果价格相同,则按产品名称降序排序
      }
    }
  ]
}
返回结果
{
  "hits": {
    "total": {
      "value": 100,
      "relation": "eq"
    },
    "hits": [
      {
        "_source": {
          "product": "Product A",
          "price": 20
        }
      },
      {
        "_source": {
          "product": "Product B",
          "price": 50
        }
      },
      {
        "_source": {
          "product": "Product C",
          "price": 100
        }
      }
    ]
  }
}

在此例中,首先根据 price 字段升序排序,如果 price 相同,则使用 product 字段按降序排序。


评分排序

默认情况下,Elasticsearch 根据查询的相关性评分(_score)对文档进行排序。你可以显式地指定按 _score 排序,也可以指定其他字段。

请求示例
{
  "query": {
    "match": {
      "product": "Elasticsearch"  // 搜索产品名包含 "Elasticsearch"
    }
  },
  "sort": [
    {
      "_score": {
        "order": "desc"  // 按相关性评分降序排序
      }
    }
  ]
}
返回结果
{
  "hits": {
    "total": {
      "value": 10,
      "relation": "eq"
    },
    "hits": [
      {
        "_score": 1.2,
        "_source": {
          "product": "Elasticsearch Book",
          "price": 45
        }
      },
      {
        "_score": 0.9,
        "_source": {
          "product": "Elasticsearch Tutorial",
          "price": 35
        }
      }
    ]
  }
}

在这个例子中,文档按相关性评分(_score)降序排序。


排序与聚合

你也可以在聚合查询中使用排序,特别是当你对聚合结果进行排序时。

请求示例

{
  "query": {
    "match_all": {}  // 查询所有文档
  },
  "aggs": {
    "by_price": {
      "terms": {
        "field": "price",
        "order": {
          "_key": "desc"  // 按价格降序排序
        }
      }
    }
  }
}

返回结果

{
  "aggregations": {
    "by_price": {
      "buckets": [
        {
          "key": 100,
          "doc_count": 20
        },
        {
          "key": 80,
          "doc_count": 15
        },
        {
          "key": 50,
          "doc_count": 30
        }
      ]
    }
  }
}

在这个例子中,聚合结果按价格字段的降序排序。


排序优化:

  1. Doc Values:对于排序字段,确保字段启用了 doc_values,以获得更快的排序性能。
  2. Nested Sorting:对于嵌套对象,可以使用 nested 聚合进行排序。
  3. Field Data:对于未启用 doc_values 的字段,可以使用 fielddata 进行排序,但这可能会影响性能。

总结

Elasticsearch 中进行排序,基本操作是通过 sort 参数来指定排序字段,可以按单个字段或多个字段进行排序,也可以按相关性评分进行排序。排序可以用于普通查询,也可以与聚合结合使用,以优化查询结果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值