Вот как мне удалось выполнить то, что мне нужно.Допустим, у нас есть индекс под названием visitor_carts
с такими документами:
{
"visitor_id" : 1,
"total_value" : 111,
"total_products" : 2
}
{
"visitor_id" : 1,
"total_value" : 199.99,
"total_products" : 1
}
{
"visitor_id" : 1,
"total_value" : 890.56,
"total_products" : 2
}
{
"visitor_id" : 2,
"total_value" : 223.56,
"total_products" : 2
}
{
"visitor_id" : 3,
"total_value" : 4.56,
"total_products" : 2
}
Существует решение - оно называется агрегированная метрическая агрегация .При этом вы можете создавать практически все, что захотите, недостатком является то, что вам нужно ознакомиться с безболезненным сценарием.Документация в этом отношении довольно сложна для понимания, к тому же кажется, что конкретные версии не очень хорошо поддерживаются, поскольку то, что находится в безболезненной документации, не работает с моей версией ElasticSearch 6.5 (хотя это должно быть в соответствии с указанной документацией).Так что предупреждающее слово - если это не сработает, продолжайте искать больше примеров.Я нашел примеры использования здесь очень полезными.В любом случае, вот рабочее решение:
POST visitor_carts/_search
{
"query" : {
"match_all" : {}
},
"aggs": {
"purchases": {
"scripted_metric": {
"init_script" : "state['visitorPurchases'] = [:]",
"map_script" : "if (state['visitorPurchases'].containsKey(doc['visitor_id'].value)) {state['visitorPurchases'][doc['visitor_id'].value]++} else {state['visitorPurchases'][doc['visitor_id'].value] = 1}",
"combine_script": "def combine = [:]; for (visitor in state['visitorPurchases'].entrySet()) {if (combine.containsKey(visitor.getValue().toString())) {combine[visitor.getValue().toString()]++} else {combine[visitor.getValue().toString()] = 1}} return combine",
"reduce_script": "def reduce = [:]; for (shard in states) { for (count in shard.entrySet()) {if (reduce.containsKey(count.getKey())) {reduce[count.getKey()] += count.getValue()} else {reduce[count.getKey()] = count.getValue()}}} return reduce"
}
}
}
}
В map_script
оно просматривает все документы, соответствующие запросу, и подсчитывает количество случаев каждого visitor_id
.Затем в combine_script
он берет то, что map_script
подготовил ранее, и группирует результат по количеству появлений.Поскольку combine_script
работает для каждого сегмента, нам нужно, чтобы reduce_script
объединял все наборы результатов из каждого сегмента и возвращал его следующим образом:
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : 5,
"max_score" : 0.0,
"hits" : [ ]
},
"aggregations" : {
"purchases" : {
"value" : {
"1" : 2,
"3" : 1
}
}
}
}
См. Документацию Сценарий метрического агрегирования Документациячтобы выяснить, что делает каждый из типов сценариев, а затем следуйте приведенным здесь примерам , чтобы составить то, что вам нужно.
Я слишком новичок в ElasticSearch, чтобы понять, насколько эффективно это решение.является.Он хорошо работает с несколькими тысячами документов, на которых я его протестировал, но я понятия не имею, как он будет вести себя с миллионами / миллиардами записей.Если кому-то захочется это проверить - будь моим гостем:)