Агрегации с динамическими данными/вложенными_объектами

Я пытаюсь агрегировать динамически отображаемые поля в ElasticSearch.

Например:

POST test/_doc/1
{
    "settings": {
        "range": {
            "value": 200,
            "display": "200 km"
        },
        "transmitter": {
            "value": 1.2,
            "display": "1.2 Ghz"
        }
    }
}

Свойства под settings являются динамическими. По сути, мне нужен такой запрос:

{
    "size": 0,
    "query": {
        "match_all": {}
    },
    "aggs": {
        "settings": {
            "terms": {
                "field": "settings.*.display"
            }
        }
    }
}

Поскольку * здесь не работает, мне интересно, есть ли способ вернуть поля из безболезненного скрипта, а затем, возможно, использовать конвейерную агрегацию? Я не могу найти безболезненный эквивалент Object.keys(settings) в JavaScript.

Я видел подход с вложенными объектами, но я хотел бы избежать этого, так как может быть много свойств «настроек» и ограничение по умолчанию равно 50, по сравнению с вложенными_объектами с 10 000 свойств.




Ответы (1)


Безболезненным эквивалентом Object.keys() является .keySet(). Вы можете реализовать следующую итеративную логику в сценарии метрики agg:

GET test/_search
{
  "size": 0,
  "aggs": {
    "dynamic_fields_agg": {
      "scripted_metric": {
        "init_script": "state.map = [:];",
        "map_script": """
          def source = params._source['settings'];
            for (def key : source.keySet()) {
              if (source[key].containsKey("display")) {
                 if (state.map.containsKey(key)) { 
                  state.map[key].add(source[key].display);
                 } else {
                   state.map[key] = [source[key].display];
                 }
              }
            }
        """,
        "combine_script": "return state",
        "reduce_script": "return states"
      }
    }
  }
}

который даст что-то вроде

{
  "aggregations":{
    "dynamic_fields_agg":{
      "value":[
        {
          "map":{
            "range":[
              "200 km"
            ],
            "transmitter":[
              "1.2 Ghz"
            ]
          }
        }
      ]
    }
  }
}

Теперь вы можете выполнять постобработку значений в сценариях уменьшения/объединения по своему усмотрению.


Использование вложенных полей не принесет вам здесь большого преимущества — пути с подстановочными знаками также не разрешены. Я сам спрашивал об этом некоторое время назад.


ОБНОВЛЕНИЕ -- встроенная версия:

GET /test/_search
{  "size": 0,  "aggs": {    "dynamic_fields_agg": {      "scripted_metric": {        "init_script": "state.map = [:];",        "map_script": "          def source = params._source[\"settings\"];\n            for (def key : source.keySet()) {\n              if (source[key].containsKey(\"display\")) {\n                 if (state.map.containsKey(key)) { \n                  state.map[key].add(source[key].display);\n                 } else {\n                   state.map[key] = [source[key].display];\n                 }\n              }\n            }",        "combine_script": "return state",        "reduce_script": "return states"      }    }  }}
person Joe Sorocin    schedule 28.08.2020
comment
Я не могу заставить работать многострочный скрипт, но он работает встроенный, спасибо! Это довольно впечатляюще, я посмотрю на функцию сокращения, чтобы удалить дубликаты, тогда результат будет именно тем, что мне нужно. - person Patrick; 28.08.2020
comment
Прохладный! Я добавил встроенную версию в свой ответ. - person Joe Sorocin; 28.08.2020
comment
Кажется, при использовании combine_script: 'return state.map' вывод уменьшается на один уровень. Кроме того, мой подход с reduce был неправильным, вместо этого я не добавляю значение в первую очередь с другим условием if (!state.map[key].values.contains(source[key].displayValue)) {. Спасибо! - person Patrick; 28.08.2020