Geodatenunterstützung in ElasticSearch

Geodatenunterstützung in ElasticSearch

1. Einführung

Elasticsearch ist am besten für seine Volltextsuchfunktionen bekannt, bietet jedoch auch vollständige Unterstützung für Geodaten.

Inprevious article finden Sie weitere Informationen zum Einrichten von Elasticsearch und zu den ersten Schritten.

Lassen Sie uns einen Blick darauf werfen, wie wir Geodaten in Elasticsearch speichern und wie wir diese Daten mithilfe von Geoabfragen durchsuchen können.

2. Geodatentyp

Um Geo-Abfragen zu aktivieren, müssen wir die Zuordnung des Index manuell erstellen und die Feldzuordnung explizit festlegen.

Die dynamische Zuordnung funktioniert beim Festlegen der Zuordnung für Geotypen nicht.

Elasticsearch bietet zwei Möglichkeiten zur Darstellung von Geodaten:

  1. Latitude-Longitude-Paare mit Geo-Point-Feldtyp

  2. Komplexe Form, definiert inGeoJSON unter Verwendung des Geoform-Feldtyps

Sehen wir uns die einzelnen Kategorien genauer an:

2.1. Geopunktdatentyp

Der Geo-Punkt-Feldtyp akzeptiert Breiten- und Längengradpaare, die verwendet werden können, um:

  • Finden Sie Punkte innerhalb eines bestimmten Abstands zum Mittelpunkt

  • Finden Sie Punkte in einem Feld oder einem Polygon

  • Aggregieren Sie Dokumente geografisch oder nach Entfernung vom Mittelpunkt

  • Dokumente nach Entfernung sortieren

Unten finden Sie eine Beispielzuordnung für das Feld zum Speichern von Geopunktdaten:

PUT /index_name
{
    "mappings": {
        "TYPE_NAME": {
            "properties": {
                "location": {
                    "type": "geo_point"
                }
            }
        }
    }
}

Wie wir aus dem obigen Beispiel sehen können, isttype fürlocation Feldgeo_point. Somit können wir jetzt ein Breiten- und Längengradpaar inlocation im Standortfeld angeben.

2.2. Geoform-Datentyp

Im Gegensatz zugeo-point bietetgeo shape die Funktionalität zum Speichern und Durchsuchen komplexer Formen wie Polygon und Rechteck. Der DatentypGeo shapemuss verwendet werden, wenn Dokumente durchsucht werden sollen, die andere Formen als Geopunkte enthalten.

Werfen wir einen Blick auf die Zuordnung des Geo-Shape-Datentyps:

PUT /index_name
{
    "mappings": {
        "TYPE_NAME": {
            "properties": {
                "location": {
                    "type": "geo_shape",
                    "tree": "quadtree",
                    "precision": "1m"
                }
            }
        }
    }
}

Die obige Zuordnung indiziert das Positionsfeld mit der Implementierung vonquadtreemit einer Genauigkeit von einem Meter.

Elasticsearch breaks down the provided geo shape into series of geo hashes consisting of small grid-like squares called raster.

Abhängig von unserer Anforderung können wir die Indizierung vongeo shape-Feldern steuern. Wenn wir beispielsweise Dokumente nach Navigation durchsuchen, wird eine Genauigkeit von bis zu einem Meter sehr wichtig, da dies zu einem falschen Pfad führen kann.

Wenn wir nach Sehenswürdigkeiten suchen, kann eine Genauigkeit von bis zu 10-50 Metern akzeptabel sein.

Eine Sache, die wir bei der Indizierung vongeo shape-Daten berücksichtigen müssen, ist, dass wir die Leistung immer mit Genauigkeit beeinträchtigen. Mit höherer Präzision generiert Elasticsearch mehr Begriffe - was zu einer erhöhten Speichernutzung führt. Daher müssen wir bei der Auswahl des Mappings für die Geoform sehr vorsichtig sein.

Weitere Zuordnungsoptionen für den Datentypgeo-shapefinden Sie unterES site.

3. Verschiedene Möglichkeiten zum Speichern von Geopunktdaten

3.1. Latitude Longitude-Objekt

PUT index_name/index_type/1
{
    "location": {
        "lat": 23.02,
        "lon": 72.57
    }
}

Hier wird der Geopunktlocation als Objekt mitlatitude undlongitude als Schlüssel gespeichert.

3.2. Latitude Longitude Pair

{
    "location": "23.02,72.57"
}

Hier wirdlocation als Breiten- und Längengradpaar in einem einfachen Zeichenfolgenformat ausgedrückt. Bitte beachten Sie die Reihenfolge von Breite und Länge im Zeichenkettenformat.

3.3. Geo Hash

{
    "location": "tsj4bys"
}

Wir können Geopunktdaten auch in Form von Geo-Hash bereitstellen, wie im obigen Beispiel gezeigt. Wir könnenonline tool verwenden, um Breiten- und Längengrade in Geo-Hash umzuwandeln.

3.4. Longitude Latitude Array

{
    "location": [72.57, 23.02]
}

Die Reihenfolge von Breiten- und Längengrad wird umgekehrt, wenn Breiten- und Längengrad als Array angegeben werden. Anfangs wurde das Längen- und Breitengradpaar sowohl in einer Zeichenfolge als auch in einem Array verwendet, später jedoch umgekehrt, um dem vonGeoJSON verwendeten Format zu entsprechen.

4. Verschiedene Möglichkeiten zum Speichern von Geoformdaten

4.1. Point

POST /index/type
{
    "location" : {
        "type" : "point",
        "coordinates" : [72.57, 23.02]
    }
}

Hier ist der Geo-Formtyp, den wir einfügen möchten,point. Bitte schauen Sie sich das Feldlocationan. Wir haben ein verschachteltes Objekt, das aus den Felderntype undcoordinates besteht. Diese Metafelder helfen Elasticsearch bei der Identifizierung der Geoform und ihrer tatsächlichen Daten.

4.2. LineString

POST /index/type
{
    "location" : {
        "type" : "linestring",
        "coordinates" : [[Here, we're inserting _linestring_ geo shape. The coordinates for _linestring_ consists of two points i.e. start and endpoint. _LineString_ geo shape is very helpful for navigation use case.

=== *4.3. _Polygon_*

[source,java,gutter:,true]

POST / Index / Typ {"Ort": {"Typ": "Polygon", "Koordinaten": [[10.0, 0.0], [11.0, 0.0], [11.0, 1.0], [10.0, 1.0], [ 10.0, 0.0]]]}}

Here, we're inserting _polygon_ geo shape. Please take a look at the _coordinates_ in above example, _first_ and _last_ coordinates in polygon should always match i.e a closed polygon.

*Elasticsearch also supports other GeoJSON structures as well. A complete list of other supported formats is as below:*

* *_MultiPoint_*
* *_MultiLineString_*
* *_MultiPolygon_*
* *_GeometryCollection_*
* *_Envelope_*
* *_Circle_*

We can find examples of above-supported formats on the official https://www.elastic.co/guide/en/elasticsearch/reference/current/geo-shape.html#input-structure[ES site].

For all structures, the inner _type_ and _coordinates_ are mandatory fields. Also, sorting and retrieving geo shape fields are currently not possible in Elasticsearch due to their complex structure. Thus, the only way to retrieve geo fields is from the source field.

== *5. ElasticSearch Geo Query*

Now, that we know how to insert documents containing geo shapes, let's dive into fetching those records using geo shape queries. But before we start using Geo Queries, we'll need following maven dependencies to support Java API for Geo Queries:

[source,java,gutter:,true]

org.locationtech.spatial4j Spatial4j 0.7 com.vividsolutions < Artefakt-ID> jts 1.13 xerces xercesImpl

We can search for above dependencies in https://search.maven.org/classic/#search%7Cgav%7C1%7Cg%3A%22org.locationtech.spatial4j%22%20AND%20a%3A%22spatial4j%22[Maven Central repository] as well.

Elasticsearch supports different types of geo queries and they are as follow:

=== *5.1. Geo Shape Query*

This requires the _geo_shape_ mapping.

Similar to _geo_shape_ type, _geo_shape_ uses GeoJSON structure to query documents.

Below is sample query to fetch all documents that fall _within_ given top-left and bottom-right coordinates:

[source,java,gutter:,true]

{"query": {"bool": {"must": {"match_all": {}}, "filter": {"geo_shape": {"region": {"shape": {"type": "envelope "," Koordinaten ": [[Relation": "innerhalb von"}}}}}

Here, _relation_ determines *spatial relation operators* used at search time.

Below is the list of supported operators:

* *_INTERSECTS_* – (default) returns all documents whose _geo_shape_ field intersects the query geometry
* *_DISJOINT_* – retrieves all documents whose _geo_shape_ field has nothing in common with the query geometry
* *_WITHIN_* – gets all documents whose _geo_shape_ field is within the query geometry
* *_CONTAINS_* – returns all documents whose _geo_shape_ field contains the query geometry

Similarly, we can query using different GeoJSON shapes.

Java code for above query is as below:

[source,java,gutter:,true]

QueryBuilders .geoShapeQuery ("region", ShapeBuilders.newEnvelope (neue Koordinate (75.00, 25.0), neue Koordinate (80.1, 30.2)) .relation (ShapeRelation.WITHIN);

=== *5.2. Geo Bounding Box Query*

Geo Bounding Box query is used to fetch all the documents based on point location. Below is a sample bounding box query:

[source,java,gutter:,true]

{"query": {"bool": {"must": {"match_all": {}}, "filter": {"geo_bounding_box": {"location": {"bottom_left": [28.3, 30.5], " top_right ": [31.8, 32.12]}}}}}}

Java code for above bounding box query is as below:

[source,java,gutter:,true]

QueryBuilders .geoBoundingBoxQuery ("location"). SetCorners (31.8, 30.5, 28.3, 32.12);

Geo Bounding Box query supports similar formats like we have in _geo_point_ data type. Sample queries for supported formats can be found on the https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-geo-bounding-box-query.html#_accepted_formats[official site].

=== *5.3. Geo Distance Query*

Geo distance query is used to filter all documents that come with the specified range of the point.

Here's a sample _geo_distance_ query:

[source,java,gutter:,true]

{"query": {"bool": {"must": {"match_all": {}}, "filter": {"geo_distance": {"distance": "10miles", "location": [31.131,29.976 ]}}}}}

And here's the Java code for above query:

[source,java,gutter:,true]

QueryBuilders .geoDistanceQuery ("location") .point (29.976, 31.131) .distance (10, DistanceUnit.MILES);

Similar to _geo_point,_ geo distance query also supports multiple formats for passing location coordinates. More details on supported formats can be found at the https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-geo-distance-query.html#_accepted_formats_2[official site].

=== *5.4. Geo _Polygon_ Query*

A query to filter all records that have points that fall within the given polygon of points.

Let's have a quick look at a sample query:

[source,java,gutter:,true]

{"query": {"bool": {"must": {"match_all": {}}, "filter": {"geo_polygon": {"location": {"points": [{"lat": 22.733 , "lon": 68.859}, {"lat": 24.733, "lon": 68.859}, {"lat": 23, "lon": 70.859}]}}}}}

And at the Java code for this query:

[source,java,gutter:,true]

List allPoints = new ArrayList (); allPoints.add (neuer GeoPoint (22.733, 68.859)); allPoints.add (neuer GeoPoint (24.733, 68.859)); allPoints.add (neuer GeoPoint (23, 70.859));

QueryBuilders.geoPolygonQuery("location", allPoints);

Geo Polygon Query also supports formats mentioned below:

* lat-long as an array: [lon, lat]
* lat-long as a string: “lat, lon”
* geo hash

_geo_point_ data type is mandatory in order to use this query.

== *6. Conclusion*

In this article, we discussed different mapping options for indexing geo data i.e _geo_point_ and _geo_shape_.

We also went through different ways to store _geo-data_ and finally, we observed geo-queries and Java API to filter results using geo queries.

As always, the code is available https://github.com/eugenp/tutorials/tree/master/persistence-modules/spring-data-elasticsearch[in this GitHub project].