Suporte geoespacial no ElasticSearch
1. Introdução
O Elasticsearch é mais conhecido por seus recursos de pesquisa de texto completo, mas também oferece suporte geoespacial completo.
Podemos descobrir mais sobre como configurar o Elasticsearch e começar nesteprevious article.
Vamos dar uma olhada em como podemos salvar geo-dados no Elasticsearch e como podemos pesquisar esses dados usando geo queries.
2. Tipo de dados geográficos
Para ativar as consultas geográficas, precisamos criar o mapeamento do índice manualmente e definir explicitamente o mapeamento do campo.
O mapeamento dinâmico não funciona ao definir o mapeamento para tipos de localização.
O Elasticsearch oferece duas maneiras de representar dados geográficos:
-
Pares de latitude-longitude usando o tipo de campo de ponto geográfico
-
Forma complexa definida emGeoJSON usando o tipo de campo geo-shape
Vamos dar uma olhada mais profunda em cada uma das categorias acima:
2.1. Tipo de Dados Geo Point
O tipo de campo de ponto geográfico aceita pares de latitude-longitude que podem ser usados para:
-
Encontre pontos a uma certa distância do ponto central
-
Encontre pontos em uma caixa ou polígono
-
Agregue documentos geograficamente ou à distância do ponto central
-
Classificar documentos por distância
Abaixo está um exemplo de mapeamento para o campo para salvar dados de pontos geográficos:
PUT /index_name
{
"mappings": {
"TYPE_NAME": {
"properties": {
"location": {
"type": "geo_point"
}
}
}
}
}
Como podemos ver no exemplo acima,type para o campolocation égeo_point. Assim, agora podemos fornecer o par latitude-longitude emlocation no campo de localização.
2.2. Tipo de Dados Geo Shape
Ao contrário degeo-point,geo shape fornece a funcionalidade de salvar e pesquisar formas complexas como polígono e retângulo. O tipo de dadosGeo shape deve ser usado quando queremos pesquisar documentos que contenham formas diferentes de pontos geográficos.
Vamos dar uma olhada no mapeamento para o tipo de dados de forma geográfica:
PUT /index_name
{
"mappings": {
"TYPE_NAME": {
"properties": {
"location": {
"type": "geo_shape",
"tree": "quadtree",
"precision": "1m"
}
}
}
}
}
O mapeamento acima indexará o campo de localização com implementação dequadtree com a precisão de um metro.
Elasticsearch breaks down the provided geo shape into series of geo hashes consisting of small grid-like squares called raster.
Dependendo do nosso requisito, podemos controlar a indexação dos camposgeo shape. Por exemplo, quando estamos pesquisando documentos para navegação, a precisão de até um metro se torna muito crítica, pois pode levar a um caminho incorreto.
Considerando que, se estivermos procurando alguns locais turísticos, uma precisão de até 10-50 metros pode ser aceitável.
Uma coisa que precisamos ter em mente ao indexar dados degeo shape é que estamos sempre comprometendo o desempenho com precisão. Com maior precisão, o Elasticsearch gera mais termos - o que leva ao aumento do uso de memória. Portanto, precisamos ser muito cautelosos ao selecionar o mapeamento para a forma geográfica.
Podemos encontrar mais opções de mapeamento para o tipo de dadosgeo-shape emES site oficial.
3. Diferentes maneiras de salvar dados de pontos geográficos
3.1. Objeto Latitude Longitude
PUT index_name/index_type/1
{
"location": {
"lat": 23.02,
"lon": 72.57
}
}
Aqui, o geo-pontolocation é salvo como um objeto comlatitudeelongitude como chaves.
3.2. Par de latitude e longitude
{
"location": "23.02,72.57"
}
Aqui,location é expresso como um par latitude-longitude em um formato de string simples. Observe a sequência de latitude e longitude no formato de sequência.
3.3. Geo Hash
{
"location": "tsj4bys"
}
Também podemos fornecer dados de ponto geográfico na forma de hash geográfico, conforme mostrado no exemplo acima. Podemos usaronline tool para converter latitude-longitude em geo hash.
3.4. Matriz de longitude e latitude
{
"location": [72.57, 23.02]
}
A sequência de latitude-longitude é revertida quando a latitude e a longitude são fornecidas como uma matriz. Inicialmente, o par latitude-longitude foi usado tanto na string quanto em uma matriz, mas depois foi revertido para corresponder ao formato usado porGeoJSON.
4. Diferentes maneiras de salvar dados de Geo Shape
4.1. Point
POST /index/type
{
"location" : {
"type" : "point",
"coordinates" : [72.57, 23.02]
}
}
Aqui, o tipo de forma geográfica que estamos tentando inserir é umpoint. Por favor, dê uma olhada no campolocation, temos um objeto aninhado que consiste nos campostypeecoordinates. Esses meta-campos ajudam a Elasticsaerch a identificar a forma geográfica e seus dados reais.
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 / índice / tipo {"local": {"tipo": "polígono", "coordenadas": [[[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]
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 "," coordenadas ": [[relação": "dentro de"}}}}}}
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 (nova coordenada (75.00, 25.0), nova coordenada (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
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].