Несколько aggs с ElasticSearch

#elasticsearch

#elasticsearch

Вопрос:

Я новичок в ES, и я не знаю, как разрешить этот простой сценарий:

Значение типа данных

 1 A
 1 A
 1 B
 2 B
 3 A
 3 A
 4 A
 4 B

Мне нужно знать, сколько типов данных имеют только значение ‘A’, сколько имеют только значение ‘B’ и сколько имеют оба. Ожидаемый результат для этого примера будет:

 Only A = 1 (dataType 3)
Only B = 1 (dataType 2)
Both = 2 (dataTypes 1 and 4)
  

Не могли бы вы мне помочь? Спасибо.

ChintanShah25 Привет, большое спасибо за ваш быстрый ответ. Я попробовал код, который вы разработали, но, похоже, не работает должным образом:

 "Aggregations": {
           "Datatypes": {
                "Value": {
                "Both": 0,
                "Onlya": 1,
                "OnlyB": 1
            }
        }
    }
  

Если я удалю "reduce_script", я смогу отлаживать, получая:

 "Агрегации": {
 "Типы данных": {
 "Значение": [
 [
 [
 "1"
 ],
 []
 ],
 [
 []
 []
 ],
 [
 [
 "1"
 ],
 [
 "1",
 "2"
 ]
 ],
 [
 [
 "4"
 ],
 []
 ],
 [
 [
 "3"
 ],
 [
 "4"
 ]
 ]
 ]
 }
 }

Типы данных сгруппированы правильно, но кажется, что они разделены на разные сегменты или сегменты, и последний шаг "reduce_script" завершается неудачей.

Документация "Scripted Metric" оставляет желать лучшего, и, хотя я тестирую, желаемых результатов не получается.

Приветствую.

Ответ №1:

Я думаю, это сложно, это может быть сделано с помощью скриптовой агрегации метрик. Я создал тестовый индекс и вставил предоставленные вами образцы данных. Следующий запрос дает желаемые результаты

 {
  "query": {
    "match_all": {}
  },
  "aggs": {
    "Datatypes": {
      "scripted_metric": {
        "init_script": "_agg['onlya'] = [];_agg['onlyb'] = [];",
        "map_script": "if (doc['value'].value == "A")
                         { _agg.onlya.add(doc['datatype'].value) };
                       if (doc['value'].value == "B")
                         { _agg.onlyb.add(doc['datatype'].value) };",
        "combine_script": "onlya = _agg['onlya'].unique();
                           onlyb = _agg['onlyb'].unique();
                           return[onlya, onlyb]",
        "reduce_script": "both_bucket=[];a_bucket=[];b_bucket=[];
                          for(a in _aggs)
                            {both_bucket=a[0].intersect(a[1]); 
                             a_bucket=a[0]-a[1];
                             b_bucket=a[1]-a[0]};
                             return ['Both' : both_bucket.size(),
                                     'OnlyA' : a_bucket.size(),
                                     'OnlyB' : b_bucket.size()];"
      }
    }
  },
  "size": 0
}
  

Это результат, который я получаю

 "aggregations": {
      "Datatypes": {
         "value": {
            "Both": 2,
            "OnlyA": 1,
            "OnlyB": 1
         }
      }
   }
  

Для этого вам нужно будет включить динамические сценарии или поместить эти сценарии в папку scripts.

Во время init_script я объявляю две переменные, которые будут содержать значение типа данных.

map_script просматривает каждый документ и добавляет тип данных в onlya, если значение равно "A" или onlyb. Вы можете заменить второе if на else, если вы уверены, что у вас будет либо A, либо B

combine_script преобразует список в уникальные значения, так [1,1,3,3,4] что становится [1,3,4]

reduce_script получает результаты из всех сегментов. Пересечение даст вам корзину со значением типа данных, а вычитание даст вам только часть. size() дает вам длину списка. Удалите метод size, чтобы узнать, какие значения типа данных совпадают.

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

Ответ №2:

В конце концов я заставил его работать так, как я хочу. Большое вам спасибо за вашу помощь.

   "aggs": {
    "Datatypes": {
      "scripted_metric": {
        "init_script": "_agg['onlya'] = [];_agg['onlyb'] = [];",
        "map_script": "valueAdd=doc['datatype'].value; if (doc['value'].value == "a") { _agg['onlya'].add(valueAdd) }; if (doc['value'].value == "b") { _agg['onlyb'].add(valueAdd) };",
        "combine_script": "onlya = _agg['onlya'].unique();                           onlyb = _agg['onlyb'].unique();                           return[onlya, onlyb]",
        "reduce_script": "a_bucket=[];b_bucket=[];for(a in _aggs){ a_bucket =a[0]; b_bucket =a[1];}; return ['Both' : a_bucket.intersect(b_bucket).size(), 'OnlyA' : (a_bucket-b_bucket).size(), 'OnlyB' : (b_bucket-a_bucket).size()];"

      }
    }
  }