Natürlich gibt es keinen direkten Weg von SQL zum Elasticsearch DSL, aber es gibt einige ziemlich häufig Korrelationen.
Für den Anfang wird jede GROUP BY
/HAVING
zu einer Aggregation kommen. Die normale Abfragesemantik kann im Allgemeinen (und mehr) von der Query DSL abgedeckt werden.
Wie eine vor der Aggregation subsed von Daten vorbereiten (wie in diesem Beispiel im Bereich der neuesten Reihe für jede Frucht)
So, du bist Art für zwei verschiedene Dinge zu fragen.
Wie ein subsed von Daten vor der Aggregation
Diese prepare ist die Abfrage Phase.
(wie in diesem Beispiel wird die letzte Zeile im Bereich für jede Frucht)
Sie fragen es technisch die Antwort auf dieses Beispiel erhalten zu aggregieren: keine normale Abfrage. In deinem Beispiel machst du MAX
, um das zu erhalten, was mit einer GROUP BY funktioniert, um es zu bekommen.
So gruppieren Ergebnisse nach mehreren Feldern
Es hängt davon ab. Willst du sie abgestuft (in der Regel ja) oder willst du sie zusammen?
Wenn Sie möchten, dass sie mehrstufig sind, dann verwenden Sie nur Unteraggregationen, um zu bekommen, was Sie wollen. Wenn Sie sie kombinieren möchten, verwenden Sie im Allgemeinen nur eine filters
Aggregation für die verschiedenen Gruppierungen.
Alles wieder zusammenfassen: Sie möchten den letzten Kauf pro Obst, bei einem bestimmten gefilterten Datumsbereich. Die Datumsbereiche sind nur ganz normale Abfragen/Filter:
{
"query": {
"bool": {
"filter": [
{
"range": {
"BoughtDate": {
"gte": "2016-01-01",
"lte": "2016-01-31"
}
}
},
{
"range": {
"BestBeforeDate": {
"gte": "2016-01-01",
"lte": "2016-01-31"
}
}
}
]
}
}
}
Damit wird kein Dokument in der Anforderung enthalten sein, die nicht innerhalb dieser Datumsbereiche für beide Felder ist (effektiv ein AND
). Weil ich einen Filter verwendet habe, ist dieser nicht gescort und kann zwischengespeichert werden.
Jetzt müssen Sie beginnen zu aggregieren, um den Rest der Informationen zu erhalten. Beginnen wir mit der Annahme, dass die Dokumente mit dem obigen Filter gefiltert wurden, um das zu vereinfachen, was wir betrachten. Wir werden es am Ende kombinieren.
{
"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"
}
}
}
}
}
}
}
}
Sie wollen "size" : 0
auf der obersten Ebene, weil man eigentlich nicht über Hits kümmern. Sie möchten nur aggregierte Ergebnisse.
Ihre erste Aggregation wurde tatsächlich nach dem letzten Datum gruppiert. Ich änderte es ein wenig, um es ein wenig realistischer zu machen (jeder Tag), aber es ist effektiv das gleiche. Die Art, wie Sie MAX
verwenden, könnten wir eine terms
Aggregation mit "size": 1
verwenden, aber das ist wahrer zu, wie Sie es tun möchten, wenn ein Datum (und vermutlich Zeit!) Beteiligt ist. Ich habe auch darum gebeten, Tage in den übereinstimmenden Dokumenten zu ignorieren, die keine Daten haben (da es von Anfang bis Ende läuft, interessieren wir uns eigentlich nicht für diese Tage).
Wenn Sie wirklich nur den letzten Tag wollten, dann könnten Sie eine Pipeline-Aggregation verwenden, um alles außer dem maximalen Bucket zu löschen, aber eine realistische Verwendung dieser Art von Anforderung würde den gesamten Datumsbereich wünschen.
Also, wir fahren dann fort, indem wir nach Geschäft gruppieren, was Sie wollen. Dann unterteilen wir uns nach Personen (BiteBy
). Dies gibt Ihnen die Anzahl implizit.
alles wieder zusammen:
{
"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"
}
}
}
}
}
}
}
}
Hinweis: Hier ist, wie ich die Daten indiziert.
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"}
Es ist kritische, die Ihre String-Werte, die Sie aggregieren auf möchten (Speicher und Person) sind not_analyzed
string
s (keyword
in ES 5.0)! Ansonsten wird es Felddaten verwenden, und das ist keine gute Sache.
Die Zuordnungen wie dies in ES 1.x/2.x ES aussehen:
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"
}
}
}
}
}
All dies zusammen, und Sie erhalten die Antwort wie:
{
"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
}
]
}
}
]
}
}
]
}
}
}
Ab jetzt , meine kleine bekannte Problemumgehung mit der Bucket-Aggregation zur Begrenzung auf das maximale Datum wird nicht mit einem 'date_histogram' funktionieren. Ironischerweise würde es funktionieren, wenn ich die Werte einfach so belassen würde, wie Sie es ursprünglich gezeigt hatten. – pickypg