Geodaten-Suche mit Elasticsearch und ES|QL

Geodaten-Suche in der Elasticsearch Query Language (ES|QL). Elasticsearch verfügt über leistungsstarke Geodaten-Suchfunktionen, die nun in ES|QL integriert werden, um die Benutzerfreundlichkeit und OGC-Vertrautheit deutlich zu verbessern.

Testen Sie Elasticsearch: Sehen Sie sich unsere Beispiel-Notebooks an, starten Sie eine kostenlose Cloud-Testversion oder testen Sie Elastic jetzt auf Ihrem lokalem Gerät.

Elasticsearch verfügt seit vielen Jahren über leistungsstarke Funktionen für die Geodaten-Suche und -Analyse , aber die API unterschied sich deutlich von dem, was typische GIS-Anwender gewohnt waren. Im vergangenen Jahr haben wir die Abfragesprache ES|QL hinzugefügt, eine Pipe-Abfragesprache, die genauso einfach oder sogar noch einfacher als SQL ist. Es eignet sich besonders für die Anwendungsfälle Suche, Sicherheit und Beobachtbarkeit, in denen Elastic hervorragende Leistungen erbringt. Wir fügen außerdem Unterstützung für die Geodaten-Suche und -Analyse innerhalb von ES|QL hinzu, was die Nutzung deutlich vereinfacht, insbesondere für Anwender aus SQL- oder GIS- Bereichen.

Elasticsearch 8.12 und 8.13 führten die grundlegende Unterstützung für Geodaten-Typen in ES|QL ein. Dies wurde durch die Hinzufügung von Geodaten-Suchfunktionen in Version 8.14 deutlich verbessert. Wichtiger noch: Diese Unterstützung wurde so konzipiert, dass sie sich eng an den Simple Feature Access- Standard des Open Geospatial Consortium (OGC) anlehnt, der auch von anderen räumlichen Datenbanken wie PostGIS verwendet wird. Dadurch wird die Nutzung für GIS-Experten, die mit diesen Standards vertraut sind, deutlich vereinfacht.

In diesem Blog zeigen wir Ihnen, wie Sie mit ES|QL Geodatenabfragen durchführen und wie sich dies im Vergleich zu den entsprechenden SQL- und Query-DSL-Funktionen verhält. Wir zeigen Ihnen außerdem, wie Sie mit ES|QL räumliche Verknüpfungen durchführen und wie Sie die Ergebnisse in Kibana Maps visualisieren. Bitte beachten Sie, dass sich alle hier beschriebenen Funktionen in der „technischen Vorschau“ befinden. Wir freuen uns über Ihr Feedback, wie wir diese verbessern können.

Suche nach Geodaten

Beginnen wir mit einer Beispielabfrage:

Hierbei wird nach Stadtgrenzenpolygonen gesucht, die sich mit einem rechteckigen Suchpolygon um den internationalen Flughafen Sanya Phoenix (SYX) schneiden.

In einem Beispieldatensatz mit Flughäfen, Städten und Stadtgrenzen findet diese Suche das sich überschneidende Polygon und gibt die gewünschten Felder aus dem übereinstimmenden Dokument zurück:

AbkürzungFlughafenRegionStadtStadtstandort
SYXSanya Phoenix Int'l天涯区SanyaPunkt(109.5036 18.2533)

Das war einfach! Vergleichen Sie dies nun mit der klassischen Elasticsearch Query DSL für dieselbe Abfrage:

Beide Abfragen sind in ihrer Absicht einigermaßen klar, aber die ES|QL-Abfrage ähnelt stark SQL. Die gleiche Abfrage in PostGIS sieht folgendermaßen aus:

Schauen Sie sich das ES|QL-Beispiel noch einmal an. So ähnlich, nicht wahr?

Wir haben festgestellt, dass bestehende Benutzer der Elasticsearch API ES|QL als wesentlich einfacher zu bedienen empfinden. Wir gehen davon aus, dass bestehende SQL-Anwender, insbesondere Spatial SQL-Anwender, feststellen werden, dass sich ES|QL sehr vertraut anfühlt und ihnen sehr ähnlich ist.

Warum nicht SQL?

Und wie sieht es mit Elasticsearch SQL aus? Es existiert schon eine Weile und verfügt über einige Geodatenfunktionen. Elasticsearch SQL wurde jedoch als Wrapper für die ursprüngliche Query-API geschrieben, was bedeutete, dass nur Abfragen unterstützt wurden, die in die ursprüngliche API transpiliert werden konnten. ES|QL hat diese Einschränkung nicht. Da es sich um einen völlig neuen Stack handelt, sind viele Optimierungen möglich, die in SQL nicht möglich waren. Unsere Benchmarks zeigen, dass ES|QL sehr oft schneller ist als die Query API, insbesondere bei Aggregationen!

Unterschiede zu SQL

Wie das vorherige Beispiel zeigt, ist ES|QL SQL zwar in gewisser Weise ähnlich, es gibt aber auch einige wichtige Unterschiede. ES|QL ist beispielsweise eine Pipe-Abfragesprache, die mit einem Quellbefehl wie FROM beginnt und dann alle nachfolgenden Befehle mit dem Pipe-Zeichen | miteinander verkettet. Dadurch wird es sehr einfach verständlich, wie jeder Befehl eine Datentabelle empfängt und eine bestimmte Aktion an dieser Tabelle durchführt, z. B. Filtern mit WHERE, Hinzufügen von Spalten mit EVAL oder Durchführen von Aggregationen mit STATS. Anstatt mit SELECT zu beginnen, um die endgültigen Ausgabespalten zu definieren, können ein oder mehrere KEEP -Befehle verwendet werden, wobei der letzte Befehl die endgültigen Ausgaberesultate angibt. Diese Struktur vereinfacht die Argumentation bezüglich der Anfrage.

Wenn wir uns den Befehl WHERE im obigen Beispiel genauer ansehen, erkennen wir, dass er dem PostGIS-Beispiel sehr ähnlich sieht:

ES|QL

PostGIS

Abgesehen von den Unterschieden bei den Anführungszeichen für Zeichenketten besteht der größte Unterschied darin, wie wir die Zeichenkette in einen räumlichen Datentyp umwandeln. In PostGIS verwenden wir das Suffix ::geometry , während wir in ES|QL das Suffix ::geo_shape verwenden. Dies liegt daran, dass ES|QL innerhalb von Elasticsearch ausgeführt wird und der Typumwandlungsoperator :: verwendet werden kann, um eine Zeichenkette in einen der unterstützten ES|QL-Typen umzuwandeln, in diesem Fall in einen geo_shape. Darüber hinaus implizieren die Typen geo_shape und geo_point in Elasticsearch das räumliche Koordinatensystem WGS84, das häufiger unter der SRID-Nummer 4326 bezeichnet wird. In PostGIS muss dies explizit angegeben werden, daher die Verwendung des Präfixes SRID=4326; für die WKT-Zeichenkette. Wird dieses Präfix entfernt, wird die SRID auf 0 gesetzt, was eher den Elasticsearch-Typen cartesian_point und cartesian_shape entspricht, die nicht an ein bestimmtes Koordinatensystem gebunden sind.

Sowohl ES|QL als auch PostGIS bieten eine Syntax für Typkonvertierungsfunktionen:

ES|QL

PostGIS

OGC-Funktionen

Elasticsearch 8.14 führt die folgenden vier OGC-Funktionen für die räumliche Suche ein:

ES|QLPostGISBeschreibung
ST_INTERSECTSST_SchnittpunkteGibt true zurück, wenn sich zwei Geometrien schneiden, und andernfalls false.
ST_DISJOINTST_DisjointGibt true zurück, wenn sich zwei Geometrien nicht schneiden, und andernfalls false. Die Umkehrfunktion von ST_INTERSECTS.
ST_CONTAINSST_ContainsGibt true zurück, wenn eine Geometrie eine andere enthält, andernfalls false.
ST_WITHINST_WithinGibt true zurück, wenn sich eine Geometrie innerhalb einer anderen befindet, und andernfalls false. Das Inverse von ST_CONTAINS.

Diese Funktionen verhalten sich ähnlich wie ihre PostGIS-Pendants und werden auf die gleiche Weise verwendet. Beispielsweise gibt ST_INTERSECTS true zurück, wenn sich zwei Geometrien schneiden, und andernfalls false. Wenn Sie den Dokumentationslinks in der obigen Tabelle folgen, werden Sie feststellen, dass sich alle ES|QL-Beispiele innerhalb einer WHERE -Klausel nach einer FROM -Klausel befinden, während alle PostGIS-Beispiele literale Geometrien verwenden. Tatsächlich unterstützen beide Plattformen die Verwendung der Funktionen in jedem Teil der Abfrage, in dem sie sinnvoll sind.

Das erste Beispiel in der PostGIS-Dokumentation für ST_INTERSECTS lautet:

Das ES|QL-Äquivalent dazu wäre:

Beachten Sie, dass wir im PostGIS-Beispiel die SRID nicht angegeben haben. Dies liegt daran, dass in PostGIS bei Verwendung des Typs geometry alle Berechnungen auf einem planaren Koordinatensystem durchgeführt werden. Wenn also beide Geometrien die gleiche SRID haben, spielt es keine Rolle, welche SRID es ist. In Elasticsearch gilt dies auch für die meisten Funktionen. Es gibt jedoch Ausnahmen, bei denen geo_shape und geo_point sphärische Berechnungen verwenden, wie wir im nächsten Blogbeitrag über die Suche nach räumlichen Distanzen sehen werden.

ES|QL Vielseitigkeit

Wir haben oben also Beispiele für die Verwendung von räumlichen Funktionen in WHERE -Klauseln und in ROW -Befehlen gesehen. Wo sonst würden sie Sinn machen? Eine sehr nützliche Stelle dafür ist der Befehl EVAL . Mit diesem Befehl können Sie einen Ausdruck auswerten und das Ergebnis zurückgeben. Nehmen wir beispielsweise an, wir wollen feststellen, ob die Schwerpunkte aller Flughäfen, gruppiert nach ihren Ländernamen, innerhalb einer Grenze liegen, die das jeweilige Land umschließt:

Die Ergebnisse sind erwartungsgemäß: Die Schwerpunkte der britischen Flughäfen liegen innerhalb der Grenzen Großbritanniens und nicht innerhalb der Grenzen Islands, und umgekehrt.

SchwerpunktAnzahlin_ukin Islandinnerhalb Großbritanniensinnerhalb Islands
PUNKT (-21,946634463965893 64.13187285885215)1FALSCHwahrFALSCHwahr
PUNKT (-2,597342072712148 54,33551226578214)17wahrFALSCHwahrFALSCH
PUNKT (0,04453958108176276 23,74658354606057)873FALSCHFALSCHFALSCHFALSCH

Tatsächlich können diese Funktionen in jedem Teil der Abfrage verwendet werden, in dem ihre Signatur sinnvoll ist. Sie alle nehmen zwei Argumente entgegen, die entweder ein Literal eines räumlichen Objekts oder ein Feld eines räumlichen Typs sind, und sie alle geben einen booleschen Wert zurück. Eine wichtige Überlegung ist, dass das Koordinatenreferenzsystem (CRS) der Geometrien übereinstimmen muss, andernfalls wird ein Fehler zurückgegeben. Das bedeutet, dass Sie die Typen geo_shape und cartesian_shape nicht im selben Funktionsaufruf mischen können. Allerdings können Sie die Typen geo_point und geo_shape mischen, da der Typ geo_point ein Sonderfall des Typs geo_shape ist und beide das gleiche Koordinatenreferenzsystem verwenden. Die Dokumentation zu jeder der oben definierten Funktionen listet die unterstützten Typkombinationen auf.

Darüber hinaus kann jedes Argument ein räumliches Literal oder ein Feld sein, in beliebiger Reihenfolge. Sie können sogar zwei Felder, zwei Literale, ein Feld und ein Literal oder ein Literal und ein Feld angeben. Die einzige Voraussetzung ist, dass die Datentypen kompatibel sind. Diese Abfrage vergleicht beispielsweise zwei Felder im selben Index:

Die Anfrage zielt im Grunde darauf ab, ob sich der Ort innerhalb der Stadtgrenzen befindet, was im Allgemeinen zutreffen sollte, aber es gibt immer Ausnahmen:

KardinalitätAnzahlin_city
wenige29FALSCH
viele740wahr

Eine weitaus interessantere Frage wäre, ob sich der Flughafenstandort innerhalb der Grenzen der Stadt befindet, die der Flughafen bedient. Der Standort des Flughafens befindet sich jedoch in einem anderen Index als derjenige, der die Stadtgrenzen enthält. Dies erfordert eine Methode, um Daten aus diesen beiden separaten Indizes effektiv abzufragen und zu korrelieren.

Räumliche Verknüpfungen

ES|QL unterstützt keine JOIN -Befehle, aber Sie können einen Sonderfall eines Joins mit dem ENRICH -Befehl erreichen, der sich ähnlich wie ein 'left join' in SQL verhält. Dieser Befehl funktioniert ähnlich wie ein „Left Join“ in SQL und ermöglicht es Ihnen, Ergebnisse aus einem Index mit Daten aus einem anderen Index anzureichern, basierend auf einer räumlichen Beziehung zwischen den beiden Datensätzen.

Nehmen wir beispielsweise an, wir reichern die Ergebnisse einer Tabelle mit Flughäfen um zusätzliche Informationen über die jeweilige Stadt an, indem wir die Stadtgrenze ermitteln, die den Flughafenstandort enthält, und führen dann einige statistische Auswertungen der Ergebnisse durch:

Dies liefert die Top 5 Regionen mit den meisten Flughäfen, zusammen mit dem Schwerpunkt aller Flughäfen, die übereinstimmenden Regionen zugeordnet sind, und der Längenspanne der WKT-Darstellung der Stadtgrenzen innerhalb dieser Regionen:

SchwerpunktAnzahlmin_wktmax_wktRegion
PUNKT (-32.56093470960719 32.598117914802714)90207207null
PUNKT (-73.94515332765877 40.70366442203522)9438438Stadt New York
PUNKT (-83.10398317873478 42.300230911932886)9473473Detroit
PUNKT (-156.3020245861262 20.176383580081165)5307803Hawaii
PUNKT (-73.88902732171118 45.57078813901171)4837837Montréal

Was ist hier also wirklich geschehen? Wo trat das vermeintliche JOIN auf? Der Kern der Anfrage liegt im Befehl ENRICH :

Dieser Befehl weist Elasticsearch an, die aus dem Index airports abgerufenen Ergebnisse anzureichern und einen Join intersects zwischen dem Feld city_location des ursprünglichen Index und dem Feld city_boundary des Index airport_city_boundaries durchzuführen, den wir bereits in einigen Beispielen verwendet haben. Einige dieser Informationen sind in dieser Abfrage jedoch nicht klar ersichtlich. Was wir sehen, ist der Name einer Anreicherungsrichtlinie city_boundaries, und die fehlenden Informationen sind in dieser Richtliniendefinition enthalten.

Hier sehen wir, dass eine geo_match -Abfrage durchgeführt wird (intersects ist der Standardwert), das Feld, mit dem abgeglichen werden soll, ist city_boundary, und die enrich_fields sind die Felder, die wir dem Originaldokument hinzufügen möchten. Eines dieser Felder, nämlich region wurde tatsächlich als Gruppierungsschlüssel für den Befehl STATS verwendet, was ohne diese 'left join'-Funktion nicht möglich gewesen wäre. Weitere Informationen zu Anreicherungsrichtlinien finden Sie in der Anreicherungsdokumentation. Beim Lesen dieser Dokumente werden Sie feststellen, dass darin die Verwendung von Anreicherungsindizes zur Anreicherung von Daten während der Indexierung durch die Konfiguration von Aufnahmepipelines beschrieben wird. Dies ist für ES|QL nicht erforderlich, da der Befehl ENRICH zur Abfragezeit funktioniert. Es genügt, den Anreicherungsindex mit den erforderlichen Daten und der Anreicherungsrichtlinie vorzubereiten und dann den Befehl ENRICH in Ihren ES|QL-Abfragen zu verwenden.

Möglicherweise stellen Sie auch fest, dass die am häufigsten vorkommende Region null war. Was könnte das bedeuten? Zur Erinnerung: Ich habe diesen Befehl mit einem 'Left Join' in SQL verglichen. Das bedeutet, dass, wenn keine übereinstimmende Stadtgrenze für einen Flughafen gefunden wird, der Flughafen trotzdem zurückgegeben wird, jedoch mit null Werten für die Felder ab dem airport_city_boundaries Index. Es stellte sich heraus, dass es 89 Flughäfen gab, bei denen kein passender Eintrag city_boundary gefunden wurde, und einen Flughafen mit einer Übereinstimmung, bei dem das Feld region den null hatte. Dies führte zu einer Zählung von 90 Flughäfen, bei denen keine region in den Ergebnissen vorkam. Ein weiteres interessantes Detail ist die Notwendigkeit des Befehls MV_EXPAND . Dies ist notwendig, da der Befehl ENRICH für jede Eingabezeile mehrere Ergebnisse zurückgeben kann, und MV_EXPAND hilft dabei, diese Ergebnisse in mehrere Zeilen aufzuteilen, eine für jedes Ergebnis. Dies erklärt auch, warum für "Hawaii" unterschiedliche min_wkt und max_wkt Ergebnisse angezeigt werden: Es gab mehrere Regionen mit dem gleichen Namen, aber unterschiedlichen Grenzen.

Kibana-Karten

Kibana hat die Unterstützung für Spatial ES|QL in der Kartenanwendung hinzugefügt. Das bedeutet, dass Sie nun ES|QL verwenden können, um in Elasticsearch nach Geodaten zu suchen und die Ergebnisse auf einer Karte zu visualisieren.

Im Menü „Ebenen hinzufügen“ gibt es eine neue Ebenenoption mit der Bezeichnung „ES|QL“. Wie alle bisher beschriebenen Geodatenfunktionen befindet sich auch diese in der „technischen Vorschauphase“. Durch Auswahl dieser Option können Sie der Karte eine Ebene hinzufügen, die auf den Ergebnissen einer ES|QL-Abfrage basiert. Man könnte beispielsweise eine Ebene zur Karte hinzufügen, die alle Flughäfen der Welt anzeigt.

Oder Sie könnten eine Ebene hinzufügen, die die Polygone ab dem Index airport_city_boundaries anzeigt, oder noch besser, wie wäre es mit der komplexen ENRICH -Abfrage oben, die Statistiken darüber generiert, wie viele Flughäfen sich in jeder Region befinden?

Was kommt als Nächstes?

Vielleicht ist Ihnen aufgefallen, dass wir in zwei der obigen Beispiele noch eine weitere räumliche Funktion ST_CENTROID_AGG eingefügt haben. Dies ist eine Aggregationsfunktion, die im Befehl STATS verwendet wird, und die erste von vielen räumlichen Analysefunktionen, die wir ES|QL hinzufügen wollen. Wir werden darüber bloggen, sobald wir mehr zu zeigen haben!

Zuvor möchten wir Ihnen jedoch ein besonders spannendes Feature vorstellen, an dem wir gearbeitet haben: die Möglichkeit, räumliche Distanzsuchen durchzuführen – eine der am häufigsten genutzten räumlichen Suchfunktionen von Elasticsearch. Können Sie sich vorstellen, wie die Syntax für Distanzsuchen aussehen könnte? Vielleicht ähnlich einer OGC-Funktion? Bleiben Sie dran für den nächsten Blogbeitrag dieser Reihe, um es herauszufinden!

Spoiler-Alarm: Elasticsearch 8.15 wurde soeben veröffentlicht und beinhaltet die räumliche Distanzsuche mit ES|QL!

Zugehörige Inhalte

Sind Sie bereit, hochmoderne Sucherlebnisse zu schaffen?

Eine ausreichend fortgeschrittene Suche kann nicht durch die Bemühungen einer einzelnen Person erreicht werden. Elasticsearch wird von Datenwissenschaftlern, ML-Ops-Experten, Ingenieuren und vielen anderen unterstützt, die genauso leidenschaftlich an der Suche interessiert sind wie Sie. Lasst uns in Kontakt treten und zusammenarbeiten, um das magische Sucherlebnis zu schaffen, das Ihnen die gewünschten Ergebnisse liefert.

Probieren Sie es selbst aus