如何删除搜索发现中的重复项

how to delete duplicates in search discover

我在 kibana 的某些文档字段的发现搜索栏中搜索了一些重复的结果,我想从每次重复中获取一个唯一的文档。因为我开始使用它,所以我不知道如何使用查询 dls 来执行此操作。但我需要这样的东西:

任意搜索:

  Doc 1 {log: '0701143900', name: '5018', date: '2019/07/01 14:37:41:796'}
  Doc 2 {log: '0701143900', name: '5018', date: '2019/07/01 14:37:41:796'}
  Doc 3 {log: '0701143900', name: '5018', date: '2019/07/01 14:37:41:796'}
  Doc 4 {log: '0701125212', name: '5018', date: '2019/07/01 12:44:58:595'}
  Doc 5 {log: '0701125212', name: '5018', date: '2019/07/01 12:44:58:595'}
  Doc 6 {log: '0701125212', name: '5018', date: '2019/07/01 12:44:58:595'}

在 dsl 查询之后我可以看到这个结果(按日期字段的重复数据删除结果)

Doc 3 {log: '0701143900', name: '5018', date: '2019/07/01 14:37:41:796'}
Doc 4 {log: '0701125212', name: '5018', date: '2019/07/01 12:44:58:595'}

(它可以是任何文档编号,但不能是骗子)

在 Elasticsearch 中,不完全是您要查找的 SELECT ... DISTINCT 操作类型,但我们可以接近(有一些注意事项)。

注意:以下所有内容均在 ES 6.8.1 上进行了测试,但在 ES 2.x 和 7.x 之前也应该可以正常工作。

实际上,您可以将两件事结合起来:

  1. A terms aggregation across whatever your uniqueness criteria is (if the log field is enough to assert uniqueness, this will be easy. If it's some combination of fields, this will get nominally trickier with scripting 并且性能显着降低)
  2. top hits aggregation 到 return 项聚合的每个桶中的一个匹配项

在 Elasticsearch 中...

假设:

  • 您的日志在某个索引中(例如,名为 logs - 我在本地将您的记录索引到名为 logs-2019.07.01 的索引中,并将其别名为 logs
  • log 字段被 keyword 分析(允许对其进行聚合)
POST /logs/_search
{
  "size": 0,
  "aggs": {
    "unique_logs": {
      "terms": {
        "field": "log",
        "size": 10
      },
      "aggs": {
        "docs": {
          "top_hits": {
            "size": 1
          }
        }
      }
    }
  }
}

这将 return 最多 10 个唯一记录

{
  "took" : 2,
  ...
  "aggregations" : {
    "unique_logs" : {
      ...
      "buckets" : [
        {
          "key" : "0701125212",
          "doc_count" : 3,
          "docs" : {
            "hits" : {
              "total" : 3,
              "max_score" : 1.0,
              "hits" : [
                {
                  "_index" : "logs-2019.07.01",
                  "_type" : "_doc",
                  "_id" : "x-FB2GsBn6OwEwpDhYjX",
                  "_score" : 1.0,
                  "_source" : {
                    "log" : "0701125212",
                    "name" : "5018",
                    "date" : "2019/07/01 12:44:58:595"
                  }
                }
              ]
            }
          }
        },
        {
          "key" : "0701143900",
          "doc_count" : 3,
          "docs" : {
            ...
          }
        }
  ...
}

在 Kibana 中...

与上述 index/data 相同的假设:

  1. 使用 left-nav 栏转到 Visualize 生成器
  2. 创建一个新数据 Table 可视化,然后 select 包含您的日志的索引模式
  3. Select Top Hits 作为您的指标(显示和排序您想要的任何字段),以及 Terms 聚合,用于按 doc 字段对行进行分桶
  4. 运行 可视化应该创建一个 2 列的 table,其中一列是唯一标准,另一列是 selected Top Hits Field

就是这样!根据您定义的任何唯一性标准,您现在应该有一个 table,每个 "unique" 记录一行。

选项

Multi-field唯一性

如果您想在唯一性标准中使用多个字段,而不更改 mapping/indexing,您唯一的选择是在条款聚合中使用 script 而不是 "field": "doc"。在 Elasticsearch 查询中,这很简单:

POST /logs/_search
{
  "size": 0,
  "aggs": {
    "unique_logs": {
      "terms": {
        // Remove the "field" from the agg...
        // "field": "log",
        // ...and add a "script" instead.
        "script": {
          "source": "String.format('%s.%s', new def[]{doc['log'].value, doc['name'].value})",
          "lang": "painless"
        },
        "size": 10
      },
      ...
    }
  }
}

在 Kibana 中,您可以做同样的事情,只需点击几下即可:

  1. 添加一个新的 scripted field 以连接您想要的 "uniqueness" 字段(使用上面 ES 示例中的脚本作为模板)。
  2. 在可视化的 Buckets 配置中,select 来自 Field 下拉列表的新脚本字段。

在 Kibana 中每行显示多个字段

虽然热门点击聚合支持 return 整个点击,但不幸的是,Kibana 数据 Table 可视化只支持每行显示一个字段。如果您想在每行中显示更多数据,您必须创建一个包含您要显示的数据的 scripted field

类似于 multi-field 唯一键示例,您可以编写一个脚本来格式化一些字符串和您的字段的一些组合:

String.format('[%s] %s - %s', new def[]{doc['log'].value, doc['date'].value, doc['name'].value})

注意事项

  1. 您的解决方案越依赖脚本,就越有可能成为 non-performant。尽可能限制脚本(即 - 如果可以,只使用一个字段来确保唯一性)
  2. 对于可视化,您可能想要创建保存的搜索而不是直接在索引模式上创建它,以限制您正在搜索和可视化的文档数量,因为聚合可能会变得昂贵,因为它是 运行 脚本。