ElasticSearch - 从索引获取所有可用的过滤器(聚合)

Tom*_*ski 5 elasticsearch

比方说我有:

"hits": [
      {
        "_index": "products",
        "_type": "product",
        "_id": "599c2b3fc991ee0a597034fa",
        "_score": 1,
        "_source": {,
          "attributes": {
            "1": [
              "a"
            ],
            "2": [
              "b",
              "c"
            ],
            "3": [
              "d",
              "e"
            ],
            "4": [
              "f",
              "g"
            ],
            "5": [
              "h",
              "i"
            ]
          }
        }
      },
      {
        "_index": "products",
        "_type": "product",
        "_id": "599c4bb4b970c25976ced8bd",
        "_score": 1,
        "_source": {
          "attributes": {
            "1": [
              "z"
            ],
            "2": [
              "y"
            ]
          }
        }
Run Code Online (Sandbox Code Playgroud)

每个产品都有属性.每个属性都有ID和值.我可以按属性过滤产品,但是现在我正在从MongoDB创建"可能的属性"列表.我想找到一种方法从ElasticSearch单独生成这样的列表(也许只是查询MongoDB以获取更多数据).

我需要的是:

{
  1: [a, z],
  2: [b, c, y],
  etc.
}
Run Code Online (Sandbox Code Playgroud)

这样的聚合怎么样?获取所有可用的属性(按分组attribute.id)及其所有可能的值(在所有产品中)?

Pan*_*wan 6

您不能在一个查询中完成,但在两个查询中相当容易:

检索属性列表

您可以使用映射来获取文档中的所有字段:

curl -XGET "http://localhost:9200/your_index/your_type/_mapping"
Run Code Online (Sandbox Code Playgroud)

检索它们的值

然后,您可以使用多个术语聚合来获取字段的所有值:

curl -XGET "http://localhost:9200/your_index/your_type/_search" -H 'Content-Type: application/json' -d'
{
  "size": 0,
  "aggs": {
    "field1Values": {
      "terms": {
        "field": "field1",
        "size": 20
      }
    },
    "field2Values": {
      "terms": {
        "field": "field2",
        "size": 20
      }
    },
    "field3Values": {
      "terms": {
        "field": "field3",
        "size": 20
      }
    },
    ...
  }
}'
Run Code Online (Sandbox Code Playgroud)

这将检索每个字段的前 20 个最常出现的值。

这个 20 个值的限制是为了防止巨大的响应(例如,如果您有几十亿个具有唯一字段的文档)。您可以修改术语聚合的“大小”参数以增加它。根据您的要求,我想选择比每个字段采用的不同值的粗略估计数大 10 倍的东西应该可以解决问题。

如何处理值的巨大基数

您还可以使用基数聚合进行中间查询以获取此实际值,然后将其用作术语聚合的大小。请注意,基数是大数时的估计值,因此您可能需要使用cardinality * 2.

curl -XGET "http://localhost:9200/your_index/your_type/_search" -H 'Content-Type: application/json' -d'
{
  "size": 0,
  "aggs": {
    "field1Cardinality": {
      "cardinality": {
        "field": "field1"
      }
    },
    "field2Cardinality": {
      "cardinality": {
        "field": "field2"
      }
    },
    "field3Cardinality": {
      "cardinality": {
        "field": "field3"
      }
    },
    ...
  }
}'
Run Code Online (Sandbox Code Playgroud)

如何处理值的巨大基数

如果没有这么多不同的属性,以前的工作。如果有,您应该更改文档的存储方式以防止Mapping 爆炸

像这样存储它们:

{
    "attributes":[
        {
            "name":"1",
            "value":[
                "a"
            ]
        },
        {
            "name":"2",
            "value":[
                "b",
                "c"
            ]
        },
        {
            "name":"3",
            "value":[
                "d",
                "e"
            ]
        },
        {
            "name":"4",
            "value":[
                "f",
                "g"
            ]
        },
        {
            "name":"5",
            "value":[
                "h",
                "i"
            ]
        }
    ]
}
Run Code Online (Sandbox Code Playgroud)

将解决这个问题,您将能够在“名称”上使用术语聚合,然后在“值”上使用子术语聚合来获得您想要的:

curl -XGET "http://localhost:9200/your_index/your_type/_search" -H 'Content-Type: application/json' -d'
{
  "size": 0,
  "aggs": {
    "attributes": {
      "terms": {
        "field": "attributes.name",
        "size": 1000
      },
      "aggs": {
        "values": {
          "terms": {
            "field": "attributes.value",
            "size": 100
          }
        }
      }
    }
  }
}'
Run Code Online (Sandbox Code Playgroud)

它需要对属性使用嵌套映射