Многократная группировка в Elasticsearch
Мне нужно агрегировать (group-by), используя 3 поля в ES.
могу ли я сделать это в 1 запросе или мне нужно использовать фасет + итерацию для каждого столбца?
спасибо
2 ответов
вы можете сделать это 2 способами :
1) Использование нескольких полей в одном аспекте :
пример для фасета одиночных полей:
curl -X GET "http://localhost:9200/sales/order/_search?pretty=true" -d '{
"query": {
"query_string": {
"query": "shohi*",
"fields": [
"billing_name"
]
}
},
"facets": {
"facet_result": {
"terms": {
"fields": [
"status"
],
"order": "term",
"size": 15
}
}
}
}'
пример для нескольких полей в одном результате фасета:
curl -X GET "http://localhost:9200/sales/order/_search?pretty=true" -d '{
"query": {
"query_string": {
"query": "shohi*",
"fields": [
"billing_name"
]
}
},
"facets": {
"facet_result": {
"terms": {
"fields": [
"status",
"customer_gender",
"state"
],
"order": "term",
"size": 15
}
}
}
}'
2) Используйте множественный набор результатов фасета:
curl -X GET "http://localhost:9200/sales/order/_search?pretty=true" -d '{
"query": {
"query_string": {
"query": "*",
"fields": [
"increment_id"
]
}
},
"facets": {
"status_facets": {
"terms": {
"fields": [
"status"
],
"size": 50,
"order": "term"
}
},
"gender_facets": {
"terms": {
"fields": [
"customer_gender"
]
}
},
"state_facets": {
"terms": {
"fields": [
"state"
],
,
"order": "term"
}
}
}
}'
Ссылка : http://www.elasticsearch.org/guide/reference/api/search/facets/terms-facet.html
начиная с версии 1.0 ElasticSearch
новая агрегаты API позволяет группировать данные по нескольким полям, используя суб-агрегатов. Предположим, вы хотите сгруппировать по полям field1
, field2
и field3
:
{
"aggs": {
"agg1": {
"terms": {
"field": "field1"
},
"aggs": {
"agg2": {
"terms": {
"field": "field2"
},
"aggs": {
"agg3": {
"terms": {
"field": "field3"
}
}
}
}
}
}
}
}
конечно это может продолжаться столько полей, сколько вы хотите.
обновление:
Для полноты, вот как выглядит вывод вышеуказанного запроса. Также ниже приведен код python для создание запроса агрегирования и сглаживание результата в список словарей.
{
"aggregations": {
"agg1": {
"buckets": [{
"doc_count": <count>,
"key": <value of field1>,
"agg2": {
"buckets": [{
"doc_count": <count>,
"key": <value of field2>,
"agg3": {
"buckets": [{
"doc_count": <count>,
"key": <value of field3>
},
{
"doc_count": <count>,
"key": <value of field3>
}, ...
]
},
{
"doc_count": <count>,
"key": <value of field2>,
"agg3": {
"buckets": [{
"doc_count": <count>,
"key": <value of field3>
},
{
"doc_count": <count>,
"key": <value of field3>
}, ...
]
}, ...
]
},
{
"doc_count": <count>,
"key": <value of field1>,
"agg2": {
"buckets": [{
"doc_count": <count>,
"key": <value of field2>,
"agg3": {
"buckets": [{
"doc_count": <count>,
"key": <value of field3>
},
{
"doc_count": <count>,
"key": <value of field3>
}, ...
]
},
{
"doc_count": <count>,
"key": <value of field2>,
"agg3": {
"buckets": [{
"doc_count": <count>,
"key": <value of field3>
},
{
"doc_count": <count>,
"key": <value of field3>
}, ...
]
}, ...
]
}, ...
]
}
}
}
следующий код python выполняет группировку по заданному списку полей. Я вам указываю include_missing=True
, он также включает в себя комбинации значений, где некоторые из полей отсутствуют (вам это не нужно, если у вас есть версия 2.0 Elasticsearch благодаря этой)
def group_by(es, fields, include_missing):
current_level_terms = {'terms': {'field': fields[0]}}
agg_spec = {fields[0]: current_level_terms}
if include_missing:
current_level_missing = {'missing': {'field': fields[0]}}
agg_spec[fields[0] + '_missing'] = current_level_missing
for field in fields[1:]:
next_level_terms = {'terms': {'field': field}}
current_level_terms['aggs'] = {
field: next_level_terms,
}
if include_missing:
next_level_missing = {'missing': {'field': field}}
current_level_terms['aggs'][field + '_missing'] = next_level_missing
current_level_missing['aggs'] = {
field: next_level_terms,
field + '_missing': next_level_missing,
}
current_level_missing = next_level_missing
current_level_terms = next_level_terms
agg_result = es.search(body={'aggs': agg_spec})['aggregations']
return get_docs_from_agg_result(agg_result, fields, include_missing)
def get_docs_from_agg_result(agg_result, fields, include_missing):
current_field = fields[0]
buckets = agg_result[current_field]['buckets']
if include_missing:
buckets.append(agg_result[(current_field + '_missing')])
if len(fields) == 1:
return [
{
current_field: bucket.get('key'),
'doc_count': bucket['doc_count'],
}
for bucket in buckets if bucket['doc_count'] > 0
]
result = []
for bucket in buckets:
records = get_docs_from_agg_result(bucket, fields[1:], include_missing)
value = bucket.get('key')
for record in records:
record[current_field] = value
result.extend(records)
return result