在Elasticsearch中获得缺失的价值

问题描述

我正在寻找一个查询,该查询将返回给定值列表中文档中缺少的值。 例如,文档中有一个国家/地区字段,其值是USA,dubai,Singapore,Japan。现在,我想告诉弹性搜索,我正在向您提供国家/地区列表(美国,迪拜,俄罗斯),您将给出的输出告诉我俄罗斯不属于任何文档。 这可能吗?

解决方法

您需要执行类似下面的查询,该查询将仅选择具有美国,迪拜和俄罗斯的文档,然后汇总country值。

{
  "size": 0,"query": {
    "terms": {
      "country": [
        "USA","Dubai","Russia"
      ]
    }
  },"aggs": {
    "countries": {
      "terms": {
        "field": "country"
      }
    }
  }
}

在结果中,您将获得针对所有存在的国家(例如美国和迪拜)的存储桶,而没有针对俄罗斯的存储桶。

然后,您可以通过从聚合结果中减去输入数组减去输入数组来进行简单的集合运算,您将找到所需的内容,即:

[USA,Dubai,Russia] - [USA,Dubai] = [Russia]

更新:如果您想在单个国家/地区中进行上述所有操作,则可以利用scripted_metric aggregation

map_script将在分片上为每个文档运行,并将所有当前国家/地区存储在临时变量state.countries中。

reduce_script将在协调节点上运行,并接收所有分片的结果。该脚本只是在比较params.countries数组中存在的国家,并且仅输出不存在的国家。

POST country/_search
{
  "size": 0,"aggs": {
    "missing_countries": {
      "scripted_metric": {
        "init_script": "state.countries = [:]","map_script": """
          def country = doc['country.keyword'].value;
          if (!state.countries.containsKey(country)) {
            state.countries[country] = 0;
          }
          state.countries[country]++;
        ""","combine_script": """
          return state.countries;
        ""","reduce_script": """
          // gather all present countries
          def countries = new HashSet(); 
          for (state in states) {
            countries.addAll(state.keySet());
          }
          // figure out which country in params is not present in countries
          def missing = [];
          for (country in params.countries) {
            if (!countries.contains(country)) {
              missing.add(country);
            }
          }
          return missing;
        ""","params": {
          "countries": ["USA","Russia"]
        }
      }
    }
  }
}

在这种情况下,输出将是

  "aggregations" : {
    "missing_countries" : {
      "value" : [
        "Russia"
      ]
    }
  }