Supporto geospaziale in ElasticSearch

1. Introduzione

Elasticsearch è meglio conosciuto per le sue capacità di ricerca full-text, ma offre anche un supporto geospaziale completo.

Possiamo trovare ulteriori informazioni sulla configurazione di Elasticsearch e su come iniziare in questo articolo precedente.

Diamo un'occhiata a come possiamo salvare i dati geografici in Elasticsearch e come possiamo cercare quei dati usando le query geografiche.

2. Tipo di dati geografici

Per abilitare le query geografiche, è necessario creare manualmente la mappatura dell'indice e impostare in modo esplicito la mappatura del campo.

La mappatura dinamica non funzionerà durante l'impostazione della mappatura per i tipi geografici.

Elasticsearch offre due modi per rappresentare i dati geografici:

  1. Coppie latitudine-longitudine che utilizzano il tipo di campo punto geografico
  2. Forma complessa definita in GeoJSON utilizzando il tipo di campo forma geografica

Diamo uno sguardo più approfondito a ciascuna delle categorie precedenti:

2.1. Tipo di dati punto geografico

Il tipo di campo punto geografico accetta coppie latitudine-longitudine che possono essere utilizzate per:

  • Trova punti entro una certa distanza dal punto centrale
  • Trova punti all'interno di una casella o un poligono
  • Documenti aggregati geograficamente o per distanza dal punto centrale
  • Ordina i documenti per distanza

Di seguito è riportata una mappatura di esempio per il campo in cui salvare i dati del punto geografico:

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

Come possiamo vedere dall'esempio sopra, il tipo per il campo posizione è geo_point . Quindi, ora possiamo fornire la coppia latitudine-longitudine nella posizione nel campo della posizione.

2.2. Tipo di dati forma geografica

A differenza del punto geografico , la forma geografica fornisce la funzionalità per salvare e cercare forme complesse come poligono e rettangolo. Il tipo di dati della forma geografica deve essere utilizzato quando si desidera cercare documenti che contengono forme diverse dai punti geografici.

Diamo un'occhiata alla mappatura per il tipo di dati di forma geografica:

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

Le versioni recenti di Elasticsearch scompongono la forma geografica fornita in una mesh triangolare . Secondo la documentazione ufficiale, ciò fornisce una risoluzione spaziale quasi perfetta.

3. Diversi modi per salvare i dati dei punti geografici

3.1. Oggetto Latitudine Longitudine

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

Qui, la posizione del punto geografico viene salvata come oggetto con latitudine e longitudine come chiavi.

3.2. Latitudine Longitudine Coppia

{ "location": "23.02,72.57" }

Qui, la posizione è espressa come coppia latitudine-longitudine in un formato stringa semplice. Si prega di notare, la sequenza di latitudine e longitudine in formato stringa.

3.3. Geo Hash

{ "location": "tsj4bys" }

Possiamo anche fornire dati sui punti geografici sotto forma di hash geografico come mostrato nell'esempio sopra. Possiamo utilizzare lo strumento online per convertire latitudine-longitudine in hash geografico.

3.4. Longitude Latitude Array

{ "location": [72.57, 23.02] }

La sequenza di latitudine-longitudine viene invertita quando latitudine e longitudine vengono fornite come array. Inizialmente, la coppia latitudine-longitudine è stata utilizzata sia nella stringa che in un array, ma in seguito è stata invertita per corrispondere al formato utilizzato da GeoJSON.

4. Diversi modi per salvare i dati di forma geografica

4.1. Punto

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

Qui, il tipo di forma geografica che stiamo cercando di inserire è un punto . Dai un'occhiata al campo posizione , abbiamo un oggetto annidato composto da tipo di campo e coordinate . Questi metacampi aiutano Elasticsearch a identificare la forma geografica e i suoi dati effettivi.

4.2. LineString

POST /index/type { "location" : { "type" : "linestring", "coordinates" : [[77.57, 23.02], [77.59, 23.05]] } }

Qui, stiamo inserendo la forma geografica della stringa lineare. Le coordinate per la stringa lineare sono costituite da due punti, ovvero punto iniziale e punto finale. La forma geografica LineString è molto utile per i casi d'uso di navigazione.

4.3. Poligono

POST /index/type { "location" : { "type" : "polygon", "coordinates" : [ [ [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 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:

 org.locationtech.spatial4j spatial4j 0.7   com.vividsolutions jts 1.13   xerces xercesImpl   

We can search for the above dependencies in 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 a sample query to fetch all documents that fall within given top-left and bottom-right coordinates:

{ "query":{ "bool": { "must": { "match_all": {} }, "filter": { "geo_shape": { "region": { "shape": { "type": "envelope", "coordinates" : [[75.00, 25.0], [80.1, 30.2]] }, "relation": "within" } } } } } }

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:

Coordinate topLeft = new Coordinate(74, 31.2); Coordinate bottomRight = new Coordinate(81.1, 24); GeoShapeQueryBuilder qb = QueryBuilders.geoShapeQuery("region", new EnvelopeBuilder(topLeft, bottomRight).buildGeometry()); qb.relation(ShapeRelation.INTERSECTS);

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:

{ "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:

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

Geo Bounding Box query supports similar formats as we have in geo_point data type. Sample queries for supported formats can be found on the 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:

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

And here's the Java code for above query:

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 official site.

5.4. Geo Polygon Query

Una query per filtrare tutti i record che hanno punti che rientrano nel poligono di punti specificato.

Diamo una rapida occhiata a una query di esempio:

{ "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} ] } } } } } }

E al codice Java per questa query:

List allPoints = new ArrayList(); allPoints.add(new GeoPoint(22.733, 68.859)); allPoints.add(new GeoPoint(24.733, 68.859)); allPoints.add(new GeoPoint(23, 70.859)); QueryBuilders.geoPolygonQuery("location", allPoints);

Geo Polygon Query supporta anche i formati indicati di seguito:

  • lat-long as an array: [lon, lat]
  • lat-long as a string: "lat, lon"
  • geo hash

Il tipo di dati geo_point è obbligatorio per utilizzare questa query.

6. Conclusione

In questo articolo, abbiamo discusso diverse opzioni di mappatura per l'indicizzazione dei dati geografici, ad esempio geo_point e geo_shape .

Abbiamo anche seguito diversi modi per archiviare i dati geografici e, infine, abbiamo osservato le query geografiche e l'API Java per filtrare i risultati utilizzando le query geografiche.

Come sempre, il codice è disponibile in questo progetto GitHub.