多样化采样器聚合

编辑

sampler 聚合类似,这是一个过滤聚合,用于将任何子聚合的处理限制为得分最高的文档的样本。diversified_sampler 聚合增加了限制共享相同值(例如“作者”)的匹配项数量的功能。

任何优秀的市场研究人员都会告诉你,在使用数据样本时,重要的是样本能代表各种健康的观点,而不是被任何单一的声音所扭曲。聚合和采样也是如此,使用这些多样化设置可以提供一种方法来消除内容中的偏差(人口过多的地理区域、时间线上的大幅峰值或过度活跃的论坛垃圾邮件发送者)。

示例用例

  • 将分析的重点缩小到高相关性匹配项,而不是潜在的非常长的低质量匹配项尾部
  • 通过确保不同来源的内容得到公平表示来消除分析中的偏差
  • 降低仅使用样本即可产生有用结果的聚合的运行成本,例如 significant_terms

field 设置用于提供用于去重的值,而 max_docs_per_value 设置控制在任何一个分片上收集的共享公共值的文档的最大数量。max_docs_per_value 的默认设置为 1。

如果 field 为单个文档生成多个值,则聚合将抛出错误(由于效率问题,不支持使用多值字段进行去重)。

示例

我们可能想查看哪些标签与 StackOverflow 论坛帖子上的 #elasticsearch 紧密相关,但忽略一些喜欢将 #Kibana 拼写为 #Cabana 的多产用户的影响。

resp = client.search(
    index="stackoverflow",
    size="0",
    query={
        "query_string": {
            "query": "tags:elasticsearch"
        }
    },
    aggs={
        "my_unbiased_sample": {
            "diversified_sampler": {
                "shard_size": 200,
                "field": "author"
            },
            "aggs": {
                "keywords": {
                    "significant_terms": {
                        "field": "tags",
                        "exclude": [
                            "elasticsearch"
                        ]
                    }
                }
            }
        }
    },
)
print(resp)
response = client.search(
  index: 'stackoverflow',
  size: 0,
  body: {
    query: {
      query_string: {
        query: 'tags:elasticsearch'
      }
    },
    aggregations: {
      my_unbiased_sample: {
        diversified_sampler: {
          shard_size: 200,
          field: 'author'
        },
        aggregations: {
          keywords: {
            significant_terms: {
              field: 'tags',
              exclude: [
                'elasticsearch'
              ]
            }
          }
        }
      }
    }
  }
)
puts response
const response = await client.search({
  index: "stackoverflow",
  size: 0,
  query: {
    query_string: {
      query: "tags:elasticsearch",
    },
  },
  aggs: {
    my_unbiased_sample: {
      diversified_sampler: {
        shard_size: 200,
        field: "author",
      },
      aggs: {
        keywords: {
          significant_terms: {
            field: "tags",
            exclude: ["elasticsearch"],
          },
        },
      },
    },
  },
});
console.log(response);
POST /stackoverflow/_search?size=0
{
  "query": {
    "query_string": {
      "query": "tags:elasticsearch"
    }
  },
  "aggs": {
    "my_unbiased_sample": {
      "diversified_sampler": {
        "shard_size": 200,
        "field": "author"
      },
      "aggs": {
        "keywords": {
          "significant_terms": {
            "field": "tags",
            "exclude": [ "elasticsearch" ]
          }
        }
      }
    }
  }
}

响应

{
  ...
  "aggregations": {
    "my_unbiased_sample": {
      "doc_count": 151,           
      "keywords": {               
        "doc_count": 151,
        "bg_count": 650,
        "buckets": [
          {
            "key": "kibana",
            "doc_count": 150,
            "score": 2.213,
            "bg_count": 200
          }
        ]
      }
    }
  }
}

总共采样了 151 个文档。

由于我们要求在样本中最多选择一位作者的一篇文章,因此 significant_terms 聚合的结果不会被任何一位作者的怪癖所扭曲。

脚本示例

编辑

在这种情况下,我们可能希望对字段值的组合进行多样化。我们可以使用运行时字段生成 tags 字段中多个值的哈希值,以确保我们没有由相同重复的标签组合组成的样本。

resp = client.search(
    index="stackoverflow",
    size="0",
    query={
        "query_string": {
            "query": "tags:kibana"
        }
    },
    runtime_mappings={
        "tags.hash": {
            "type": "long",
            "script": "emit(doc['tags'].hashCode())"
        }
    },
    aggs={
        "my_unbiased_sample": {
            "diversified_sampler": {
                "shard_size": 200,
                "max_docs_per_value": 3,
                "field": "tags.hash"
            },
            "aggs": {
                "keywords": {
                    "significant_terms": {
                        "field": "tags",
                        "exclude": [
                            "kibana"
                        ]
                    }
                }
            }
        }
    },
)
print(resp)
response = client.search(
  index: 'stackoverflow',
  size: 0,
  body: {
    query: {
      query_string: {
        query: 'tags:kibana'
      }
    },
    runtime_mappings: {
      'tags.hash' => {
        type: 'long',
        script: "emit(doc['tags'].hashCode())"
      }
    },
    aggregations: {
      my_unbiased_sample: {
        diversified_sampler: {
          shard_size: 200,
          max_docs_per_value: 3,
          field: 'tags.hash'
        },
        aggregations: {
          keywords: {
            significant_terms: {
              field: 'tags',
              exclude: [
                'kibana'
              ]
            }
          }
        }
      }
    }
  }
)
puts response
const response = await client.search({
  index: "stackoverflow",
  size: 0,
  query: {
    query_string: {
      query: "tags:kibana",
    },
  },
  runtime_mappings: {
    "tags.hash": {
      type: "long",
      script: "emit(doc['tags'].hashCode())",
    },
  },
  aggs: {
    my_unbiased_sample: {
      diversified_sampler: {
        shard_size: 200,
        max_docs_per_value: 3,
        field: "tags.hash",
      },
      aggs: {
        keywords: {
          significant_terms: {
            field: "tags",
            exclude: ["kibana"],
          },
        },
      },
    },
  },
});
console.log(response);
POST /stackoverflow/_search?size=0
{
  "query": {
    "query_string": {
      "query": "tags:kibana"
    }
  },
  "runtime_mappings": {
    "tags.hash": {
      "type": "long",
      "script": "emit(doc['tags'].hashCode())"
    }
  },
  "aggs": {
    "my_unbiased_sample": {
      "diversified_sampler": {
        "shard_size": 200,
        "max_docs_per_value": 3,
        "field": "tags.hash"
      },
      "aggs": {
        "keywords": {
          "significant_terms": {
            "field": "tags",
            "exclude": [ "kibana" ]
          }
        }
      }
    }
  }
}

响应

{
  ...
  "aggregations": {
    "my_unbiased_sample": {
      "doc_count": 6,
      "keywords": {
        "doc_count": 6,
        "bg_count": 650,
        "buckets": [
          {
            "key": "logstash",
            "doc_count": 3,
            "score": 2.213,
            "bg_count": 50
          },
          {
            "key": "elasticsearch",
            "doc_count": 3,
            "score": 1.34,
            "bg_count": 200
          }
        ]
      }
    }
  }
}

shard_size

编辑

shard_size 参数限制在每个分片上处理的样本中收集的得分最高的文档的数量。默认值为 100。

max_docs_per_value

编辑

max_docs_per_value 是一个可选参数,用于限制每个去重值选择允许的文档数量。默认设置为“1”。

execution_hint

编辑

可选的 execution_hint 设置会影响用于去重的值的管理。每个选项在执行去重时都会在内存中保存最多 shard_size 个值,但可以按如下方式控制保存的值的类型

  • 直接保存字段值 (map)
  • 保存由 Lucene 索引确定的字段的序号 (global_ordinals)
  • 保存字段值的哈希值 - 可能存在哈希冲突 (bytes_hash)

默认设置是使用global_ordinals(如果此信息可从 Lucene 索引中获得),否则回退到 mapbytes_hash 设置在某些情况下可能更快,但由于可能存在哈希冲突,因此会在去重逻辑中引入误报的可能性。请注意,如果执行提示不适用,Elasticsearch 将忽略执行提示的选择,并且这些提示没有向后兼容性保证。

限制

编辑

不能嵌套在 breadth_first 聚合下

编辑

作为基于质量的过滤器,多样化采样器聚合需要访问为每个文档生成的相关性得分。因此,它不能嵌套在 terms 聚合下,后者的 collect_mode 从默认的 depth_first 模式切换到 breadth_first,因为这会丢弃得分。在这种情况下,将抛出一个错误。

有限的去重逻辑。

编辑

去重逻辑仅在分片级别应用,因此不会跨分片应用。

没有针对地理/日期字段的特殊语法

编辑

目前,用于定义多样化值的语法由 fieldscript 的选择来定义 - 没有用于表达地理或日期单位(例如“7d”(7 天))的附加语法糖。此支持可能会在以后的版本中添加,用户目前必须使用脚本创建这些类型的值。