Как агрегировать по динамическим полям в elasticsearch?

Я пытаюсь агрегировать по динамическим полям (разные для разных документов) через elasticsearch. Документы выглядят следующим образом:

[{
   "name": "galaxy note",
   "price": 123,
   "attributes": {
      "type": "phone",
      "weight": "140gm"
   }
},{
   "name": "shirt",
   "price": 123,
   "attributes": {
      "type": "clothing",
      "size": "m"
   }
}]

Как вы можете видеть, изменения атрибутов в документах. То, что я пытаюсь достичь, - это заполнить поля этих атрибутов следующим образом:

{
     aggregations: {
         types: {
             buckets: [{key: 'phone', count: 123}, {key: 'clothing', count: 12}]
         }
     }
}

Я пытаюсь использовать aggregation функцию elasticsearch для достижения этого, но не в состоянии найти правильный путь. Можно ли добиться посредством агрегации? Или я должен начать искать facets, подумал, что это кажется лишенным.

Ответ 1

Вы должны определить атрибуты как вложенные в ваше сопоставление и изменить макет значений отдельных атрибутов на фиксированный макет { key: DynamicKey, value: DynamicValue }

PUT /catalog
{
  "settings" : {
    "number_of_shards" : 1
  },
  "mappings" : {
    "article": {
      "properties": {
        "name": { 
          "type" : "string", 
          "index" : "not_analyzed" 
        },
        "price": { 
          "type" : "integer" 
        },
        "attributes": {
          "type": "nested",
          "properties": {
            "key": {
              "type": "string"
            },
            "value": {
              "type": "string"
            }
          }
        }
      }  
    }
  }
}

Вы можете индексировать свои статьи следующим образом

POST /catalog/article
{
  "name": "shirt",
  "price": 123,
  "attributes": [
    { "key": "type", "value": "clothing"},
    { "key": "size", "value": "m"}
  ]
}

POST /catalog/article
{
  "name": "galaxy note",
  "price": 123,
  "attributes": [
    { "key": "type", "value": "phone"},
    { "key": "weight", "value": "140gm"}
  ]
}

В конце концов вы сможете агрегировать по вложенным атрибутам

GET /catalog/_search
{
  "query":{
    "match_all":{}
  },     
  "aggs": {
    "attributes": {
      "nested": {
        "path": "attributes"
      },
      "aggs": {
        "key": {
          "terms": {
            "field": "attributes.key"
          },
          "aggs": {
            "value": {
              "terms": {
                "field": "attributes.value"
              }
            }
          }
        }
      }
    }
  }
}

Которая затем дает вам запрошенную вами информацию в немного другой форме

[...]
"buckets": [
  {
    "key": "type",
    "doc_count": 2,
    "value": {
      "doc_count_error_upper_bound": 0,
      "sum_other_doc_count": 0,
      "buckets": [
      {
        "key": "clothing",
        "doc_count": 1
      }, {
        "key": "phone",
        "doc_count": 1
      }
      ]
    }
  },
[...]

Ответ 2

Не уверен, что это то, что вы имеете в виду, но это довольно просто с базовыми функциями агрегации. Остерегайтесь, я не включил отображение, поэтому с типом нескольких слов вы получаете двойные результаты.

POST /product/atype
{
   "name": "galaxy note",
   "price": 123,
   "attributes": {
      "type": "phone",
      "weight": "140gm"
   }
}

POST /product/atype
{
   "name": "shirt",
   "price": 123,
   "attributes": {
      "type": "clothing",
      "size": "m"
   }
}

GET /product/_search?search_type=count
{
  "aggs": {
    "byType": {
      "terms": {
        "field": "attributes.type",
        "size": 10
      }
    }
  }
}

Ответ 3

Я использую решение Georg Engel, и оно прекрасно работает. Проблема, которую я должен решить сейчас, состоит в том, что мне нужно показать имя для каждого ключа и значения. Я попытался использовать метатег, но он работает только с зашифрованными именами. У меня есть более 100.000 различных полей, поэтому я не могу их жестко задавать, мне нужно использовать другое поле данных в качестве имени, например:

{
  "query": {
    "match_all": {}
  },
  "aggs": {
    "attributes": {
      "nested": {
        "path": "attributes"
      },
      "aggs": {
        "atributos": {
          "terms": {
            "field": "attributes.key",
            "meta": "attribute.key_name",
          },
          "aggs": {
            "value": {
              "terms": {
                "field": "attributes.value",
                "meta": "attribute.value_name",
              }
            }
          }
        }
      }
    }
  }
}

Как я могу решить это? Заранее спасибо!