Естественно нет прямого пути от SQL к Elasticsearch DSL, но есть некоторые довольно общие корреляции.
Для начала любая GROUP BY
/HAVING
собирается свести к агрегации. Обычная семантика запроса обычно может быть покрыта (и тем более) DSL запроса.
Как подготовить subsed данных до агрегирования (как в этом примере последней строки в диапазоне на каждом плоде)
Итак, вы вроде просят две разные вещи.
Как подготовить subsed данных до агрегирования
Это фаза запроса.
(как в этом примере последней строки в диапазоне на каждый плод)
Вы технически просим его агрегировать, чтобы получить ответ на этот пример: не обычный запрос. В вашем примере вы делаете MAX
, чтобы получить это, действующее с помощью GROUP BY, чтобы получить его.
Как сгруппируйте результаты по нескольким полям
Это зависит от многого. Вы хотите, чтобы они были многоуровневыми (как правило, да), или вы хотите, чтобы они были вместе.
Если вы хотите, чтобы они были многоуровневыми, то вы просто используете субгруппы, чтобы получить то, что хотите. Если вы хотите, чтобы они были объединены, тогда вы обычно используете агрегацию filters
для разных группировок.
Взяв все это вместе: вам нужна самая последняя покупка за каждый фрукт, учитывая определенный диапазон фильтров. Диапазоны дат просто нормальные запросы/фильтры:
{
"query": {
"bool": {
"filter": [
{
"range": {
"BoughtDate": {
"gte": "2016-01-01",
"lte": "2016-01-31"
}
}
},
{
"range": {
"BestBeforeDate": {
"gte": "2016-01-01",
"lte": "2016-01-31"
}
}
}
]
}
}
}
При том, что ни один документ не будет включен в запрос, который не находится в пределах этих диапазонов для обоих полей (фактически является AND
). Поскольку я использовал фильтр, он незащищен и кэшируется.
Теперь вам нужно начать агрегирование, чтобы получить остальную информацию. Начнем с того, что документы были отфильтрованы с использованием вышеуказанного фильтра, чтобы упростить то, на что мы смотрим. Мы объединим его в конце.
{
"size": 0,
"aggs": {
"group_by_date": {
"date_histogram": {
"field": "BoughtDate",
"interval": "day",
"min_doc_count": 1
},
"aggs": {
"group_by_store": {
"terms": {
"field": "BoughtInStore"
},
"aggs": {
"group_by_person": {
"terms": {
"field": "BiteBy"
}
}
}
}
}
}
}
}
Вы хотите "size" : 0
на самом высоком уровне, потому что вы на самом деле не волнует хитов. Вам нужны только агрегированные результаты.
Ваша первая агрегация фактически группировалась по самой последней дате. Я немного изменил его, чтобы сделать его немного более реалистичным (каждый день), но это фактически то же самое. То, как вы используете MAX
, мы могли бы использовать агрегацию terms
с "size": 1
, но это truer, как вы хотели бы сделать это, когда речь идет о дате (и предположительно времени!). Я также попросил его игнорировать дни в соответствующих документах, у которых нет данных (так как это происходит от начала до конца, на самом деле мы не заботимся о тех днях).
Если у вас действительно нужен только последний день, то вы можете использовать агрегацию конвейера, чтобы удалить все, кроме макс. Ковша, но для реалистичного использования этого типа запроса потребуется полный диапазон дат.
Итак, мы продолжим сгруппировать по магазинам, что вам нужно. Затем мы подгруппируем человека (BiteBy
). Это даст вам счет неявно.
Собираем все вместе:
{
"size": 0,
"query": {
"bool": {
"filter": [
{
"range": {
"BoughtDate": {
"gte": "2016-01-01",
"lte": "2016-01-31"
}
}
},
{
"range": {
"BestBeforeDate": {
"gte": "2016-01-01",
"lte": "2016-01-31"
}
}
}
]
}
},
"aggs": {
"group_by_date": {
"date_histogram": {
"field": "BoughtDate",
"interval": "day",
"min_doc_count": 1
},
"aggs": {
"group_by_store": {
"terms": {
"field": "BoughtInStore"
},
"aggs": {
"group_by_person": {
"terms": {
"field": "BiteBy"
}
}
}
}
}
}
}
}
Примечание: Вот как я индексироваться данные.
PUT /grocery/store/_bulk
{"index":{"_id":"1"}}
{"Fruit":"Banana","BoughtInStore":"Jungle","BoughtDate":"2016-01-01","BestBeforeDate":"2016-01-02","BiteBy":"John"}
{"index":{"_id":"2"}}
{"Fruit":"Banana","BoughtInStore":"Jungle","BoughtDate":"2016-01-02","BestBeforeDate":"2016-01-04","BiteBy":"Mat"}
{"index":{"_id":"3"}}
{"Fruit":"Banana","BoughtInStore":"Jungle","BoughtDate":"2016-01-03","BestBeforeDate":"2016-01-05","BiteBy":"Mark"}
{"index":{"_id":"4"}}
{"Fruit":"Banana","BoughtInStore":"Jungle","BoughtDate":"2016-01-04","BestBeforeDate":"2016-02-01","BiteBy":"Simon"}
{"index":{"_id":"5"}}
{"Fruit":"Orange","BoughtInStore":"Jungle","BoughtDate":"2016-01-12","BestBeforeDate":"2016-01-12","BiteBy":"John"}
{"index":{"_id":"6"}}
{"Fruit":"Orange","BoughtInStore":"Jungle","BoughtDate":"2016-01-14","BestBeforeDate":"2016-01-16","BiteBy":"Mark"}
{"index":{"_id":"7"}}
{"Fruit":"Orange","BoughtInStore":"Jungle","BoughtDate":"2016-01-20","BestBeforeDate":"2016-01-21","BiteBy":"Simon"}
{"index":{"_id":"8"}}
{"Fruit":"Kiwi","BoughtInStore":"Shop","BoughtDate":"2016-01-21","BestBeforeDate":"2016-01-21","BiteBy":"Mark"}
{"index":{"_id":"9"}}
{"Fruit":"Kiwi","BoughtInStore":"Jungle","BoughtDate":"2016-01-21","BestBeforeDate":"2016-01-21","BiteBy":"Simon"}
Это критическое что ваши строковые значения, которые вы хотите агрегировать на (магазин и человек) являются not_analyzed
string
s (keyword
в ES 5.0)! В противном случае он будет использовать то, что называется fielddata, и это не очень хорошо.
Отображения будет выглядеть следующим образом в ES 1.x/ES 2.x:
PUT /grocery
{
"settings": {
"number_of_shards": 1
},
"mappings": {
"store": {
"properties": {
"Fruit": {
"type": "string",
"index": "not_analyzed"
},
"BoughtInStore": {
"type": "string",
"index": "not_analyzed"
},
"BiteBy": {
"type": "string",
"index": "not_analyzed"
},
"BestBeforeDate": {
"type": "date"
},
"BoughtDate": {
"type": "date"
}
}
}
}
}
Все это вместе, и вы получите ответ, как:
{
"took": 8,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"failed": 0
},
"hits": {
"total": 8,
"max_score": 0,
"hits": []
},
"aggregations": {
"group_by_date": {
"buckets": [
{
"key_as_string": "2016-01-01T00:00:00.000Z",
"key": 1451606400000,
"doc_count": 1,
"group_by_store": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "Jungle",
"doc_count": 1,
"group_by_person": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "John",
"doc_count": 1
}
]
}
}
]
}
},
{
"key_as_string": "2016-01-02T00:00:00.000Z",
"key": 1451692800000,
"doc_count": 1,
"group_by_store": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "Jungle",
"doc_count": 1,
"group_by_person": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "Mat",
"doc_count": 1
}
]
}
}
]
}
},
{
"key_as_string": "2016-01-03T00:00:00.000Z",
"key": 1451779200000,
"doc_count": 1,
"group_by_store": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "Jungle",
"doc_count": 1,
"group_by_person": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "Mark",
"doc_count": 1
}
]
}
}
]
}
},
{
"key_as_string": "2016-01-12T00:00:00.000Z",
"key": 1452556800000,
"doc_count": 1,
"group_by_store": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "Jungle",
"doc_count": 1,
"group_by_person": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "John",
"doc_count": 1
}
]
}
}
]
}
},
{
"key_as_string": "2016-01-14T00:00:00.000Z",
"key": 1452729600000,
"doc_count": 1,
"group_by_store": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "Jungle",
"doc_count": 1,
"group_by_person": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "Mark",
"doc_count": 1
}
]
}
}
]
}
},
{
"key_as_string": "2016-01-20T00:00:00.000Z",
"key": 1453248000000,
"doc_count": 1,
"group_by_store": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "Jungle",
"doc_count": 1,
"group_by_person": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "Simon",
"doc_count": 1
}
]
}
}
]
}
},
{
"key_as_string": "2016-01-21T00:00:00.000Z",
"key": 1453334400000,
"doc_count": 2,
"group_by_store": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "Jungle",
"doc_count": 1,
"group_by_person": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "Simon",
"doc_count": 1
}
]
}
},
{
"key": "Shop",
"doc_count": 1,
"group_by_person": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "Mark",
"doc_count": 1
}
]
}
}
]
}
}
]
}
}
}
Как прямо сейчас , мое небольшое замеченное обходное решение с агрегацией ковша для ограничения максимальной даты не будет работать с 'date_histogram'. По иронии судьбы, это сработало бы, если бы я просто оставил значения как цифры, как вы изначально показывали. – pickypg