Búsqueda geoespacial de Elasticsearch con ES|QL

Búsqueda geoespacial en el lenguaje de consultas Elasticsearch (ES|QL). Elasticsearch cuenta con poderosas características de búsqueda geoespacial, que ahora están llegando a ES|QL por una facilidad de uso mucho mejorada y familiaridad con el OGC.

Experimenta con Elasticsearch: Sumérgete en nuestros cuadernos de muestra, inicia una prueba gratuita del cloud o prueba Elastic en tu máquina local ahora.

Elasticsearch tuvo poderosos capacidades de búsqueda y análisis geoespacial durante muchos años, pero la API era bastante diferente de lo que los usuarios típicos de SIG estaban acostumbrados. En el último año hemos agregado el ES|Lenguaje de consulta QL, un lenguaje de consulta por tubes tan fácil, o incluso más sencillo, que SQL. Es especialmente adecuado para los casos de búsqueda y observabilidad en los que Elastic destaca. También estamos agregando soporte para búsqueda y análisis geoespacial dentro de ES|QL, lo que lo hace mucho más fácil de usar, especialmente para usuarios que vienen de comunidades SQL o GIS .

Elasticsearch 8.12 y 8.13 llevaron soporte básico para tipos geoespaciales a ES|QL. Esto se mejoró significativamente con la incorporación de capacidades de búsqueda geoespacial en la versión 8.14. Más importante aún, este soporte fue diseñado para ajustar estrechamente al estándar Simple Feature Access del Open Geospatial Consortium (OGC) empleado por otras bases de datos espaciales como PostGIS, facilitando mucho su uso para expertos en SIG familiarizados con estos estándares.

En este blog, te mostraremos cómo usar ES|QL para realizar búsquedas geoespaciales y cómo se compara con los equivalentes SQL y Query DSL. También te mostraremos cómo usar ES|QL para realizar uniones espaciales y cómo visualizar los resultados en Kibana Maps. Ten en cuenta que todas las funciones descritas aquí están en "vista previa técnica", y nos encantaría conocer vuestros comentarios sobre cómo podemos mejorarlas.

Búsqueda de datos geoespaciales

Empecemos con una consulta de ejemplo:

Esto realiza una búsqueda de cualquier polígono de límite de ciudad que intersecte con un polígono rectangular alrededor del Aeropuerto Internacional Sanya Phoenix (SYX).

En un conjunto de datos de ejemplo de aeropuertos, ciudades y límites urbanos, esta búsqueda encuentra el polígono que se cruza y devuelve los campos deseados del documento correspondiente:

Abbrevaeropuertoregiónciudadcity_location
SYXSanya Phoenix Internacional天涯区SanyaPOINT(109.5036 18.2533)

¡Eso fue fácil! Ahora compáralo con el tradicional DSL de Elasticsearch Query para la misma consulta:

Ambas consultas son bastante claras en su intención, pero el ES|La consulta QL se parece mucho a SQL. La misma consulta en PostGIS es la siguiente:

Mira atrás en el ES|Ejemplo de QL. Muy parecido, ¿verdad?

Comprobamos que los usuarios actuales de la API de Elasticsearch encuentran ES|QL mucho más fácil de usar. Ahora esperamos que los usuarios existentes de SQL, especialmente los de SQL Espacial, descubran que ES|QL se siente muy familiar con lo que están acostumbrados a ver.

¿Por qué no SQL?

¿Y qué pasa con Elasticsearch SQL? Lleva un tiempo existiendo y tiene algunas características geoespaciales. Sin embargo, Elasticsearch SQL se escribía como un envoltorio sobre la API original de Consultas, lo que significaba que solo se soportaban consultas que podían transpilarse a la API original. ES|QL no tiene esta limitación. Ser una pila completamente nueva permite muchas optimizaciones que no eran posibles en SQL. Nuestros benchmarks muestran ES|QL suele ser muy rápido que la API de Consultas, ¡especialmente con agregaciones!

Diferencias con SQL

Claramente, por el ejemplo anterior, ES|QL es algo similar a SQL, pero hay algunas diferencias importantes. Por ejemplo, ES|QL es un lenguaje de consulta por pipes, que comienza con un comando fuente como FROM y luego encadena todos los comandos posteriores junto con el pipe | carácter. Esto facilita mucho entender cómo cada comando recibe una tabla de datos y realiza alguna acción sobre esa tabla, como filtrar con WHERE, agregar columnas con EVAL, o realizar agregaciones con STATS. En lugar de comenzar con SELECT para definir las columnas de salida finales, puede haber uno o más comandos KEEP , siendo el último el que especifica los resultados finales. Esta estructura simplifica el razonamiento sobre la consulta.

Centrándonos en el comando WHERE del ejemplo anterior, podemos ver que se parece bastante al ejemplo de PostGIS:

ES|QL

PostGIS

Aparte de la diferencia en los caracteres de comillas de cadena, la mayor diferencia está en cómo tipificamos la cadena a un tipo espacial. En PostGIS, usamos el sufijo ::geometry , mientras que en ES|QL, usamos el sufijo ::geo_shape . Esto se debe a que ES|QL se ejecuta dentro de Elasticsearch, y el operador de typecasting :: puede usar para convertir una cadena a cualquiera de los ES| soportadosQL tipia, en este caso, un geo_shape. Además, los tipos geo_shape y geo_point en Elasticsearch implican el sistema de coordenadas espaciales conocido como WGS84, más comúnmente conocido con el número SRID 4326. En PostGIS, esto debe ser explícito, de ahí el uso del prefijo SRID=4326; a la cadena WKT. Si se elimina ese prefijo, el SRID se pondrá en 0, que es más parecido a los tipos de Elasticsearch cartesian_point y cartesian_shape, que no están vinculados a ningún sistema de coordenadas específico.

Ambos ES|QL y PostGIS también proporcionan sintaxis de funciones de conversión de tipos:

ES|QL

PostGIS

Funciones OGC

Elasticsearch 8.14 introduce las siguientes cuatro funciones de búsqueda espacial OGC:

ES|QLPostGISDescripción
ST_INTERSECTSST_IntersectsDevuelve verdadero si dos geometrías se cruzan, y falso en caso contrario.
ST_DISJOINTST_DisjointDevuelve verdadero si dos geometrías no se intersectan, y falso en caso contrario. El inverso de ST_INTERSECTS.
ST_CONTAINSST_ContainsDevuelve verdadero si una geometría contiene otra, y falso en caso contrario.
ST_WITHINST_WithinDevuelve verdadero si una geometría está dentro de otra, y falso en caso contrario. El inverso de ST_CONTAINS.

Estas funciones se comportan de forma similar a sus contrapartes PostGIS y se emplean de la misma manera. Por ejemplo, ST_INTERSECTS devuelve verdadero si dos geometrías se intersectan y falso en caso contrario. Si sigues los enlaces de documentación de la tabla anterior, puede que notes que todos los ES|Los ejemplos de QL están dentro de una cláusula de WHERE luego de una cláusula de FROM , mientras que todos los ejemplos de PostGIS usan geometrías literales. De hecho, ambas plataformas soportan el uso de las funciones en cualquier parte de la consulta donde tengan sentido.

El primer ejemplo en la documentación de PostGIS para ST_INTERSECTS es:

El ES|El equivalente en QL de esto sería:

Fíjate en que no especificamos el SRID en el ejemplo de PostGIS. Esto se debe a que en PostGIS, al usar el tipo geometry , todos los cálculos se realizan en un sistema de coordenadas planas, por lo que si ambas geometrías tienen el mismo SRID, no importa cuál sea el SRID. En Elasticsearch, esto también es cierto para la mayoría de las funciones, sin embargo, hay excepciones donde geo_shape y geo_point emplean cálculos esféricos, como veremos en el próximo blog sobre búsqueda por distancia espacial.

ES|Versatilidad en QL

Vimos ejemplos arriba de usar funciones espaciales en cláusulas WHERE y en comandos ROW . ¿Dónde más tendrían sentido? Un lugar muy útil es en el comando EVAL . Este comando te permite evaluar una expresión y devolver el resultado. Por ejemplo, determinemos si los centroides de todos los aeropuertos agrupados por el nombre de sus países están dentro de un límite que delimita el país:

Se esperan los resultados: el centroide de los aeropuertos del Reino Unido está dentro del límite del Reino Unido, y no dentro del límite de Islandia, y viceversa:

centroideRecuentoin_ukin_icelandwithin_ukwithin_iceland
POINT (-21.946634463965893 64.13187285885215)1falsotruefalsotrue
POINT (-2.597342072712148 54.33551226578214)17truefalsotruefalso
POINT (0.04453958108176276 23.74658354606057)873falsofalsofalsofalso

De hecho, estas funciones pueden usar en cualquier parte de la consulta donde su firma tenga sentido. Todos toman dos argumentos, que son o bien un objeto espacial literal o un campo de un tipo espacial, y todos devuelven un valor booleano. Una consideración importante es que el sistema de referencia de coordenadas (CRS) de las geometrías debe coincidir, o se devolverá un error. Esto significa que no puedes mezclar geo_shape y cartesian_shape tipos en la misma llamada de función. Sin embargo, puedes mezclar geo_point y geo_shape tipos, ya que el tipo geo_point es un caso especial del tipo geo_shape , y ambos comparten el mismo sistema de referencia de coordenadas. La documentación de cada una de las funciones definidas anteriormente lista las combinaciones de tipos soportadas.

Además, cualquiera de los dos argumentos puede ser un literal espacial o un campo, en cualquiera de los dos ordenes. Incluso puedes especificar dos campos, dos literales, un campo y un literal, o un literal y un campo. El único requisito es que los tipos sean compatibles. Por ejemplo, esta consulta compara dos campos en el mismo índice:

La consulta básicamente pregunta si la ubicación de la ciudad está dentro del límite de la ciudad, lo cual generalmente debería ser cierto, pero siempre hay excepciones:

cardinalidadRecuentoin_city
poco29falso
mucho740true

Una pregunta mucho más interesante sería si la ubicación del aeropuerto está dentro de los límites de la ciudad a la que sirve el aeropuerto. Sin embargo, la ubicación del aeropuerto se encuentra en un índice diferente al que contiene los límites de la ciudad. Esto requiere un método para consultar y correlacionar eficazmente los datos de estos dos índices separados.

Unirías espaciales

ES|QL no soporta comandos JOIN, pero puedes lograr un caso especial de unión usando el comando ENRICH, que se comporta de forma similar a una 'unión izquierda' en SQL. Este comando funciona de forma similar a una 'unión por la izquierda' en SQL, permitiéndote enriquecer resultados de un índice con datos de otro índice basar en una relación espacial entre los dos conjuntos de datos.

Por ejemplo, enriquezcamos los resultados de una tabla de aeropuertos con información adicional sobre la ciudad a la que sirven encontrando el límite de la ciudad que contiene la ubicación del aeropuerto, y luego realicemos algunas estadísticas sobre los resultados:

Esto devuelve las 5 regiones con más aeropuertos, junto con el centroide de todos los aeropuertos que tienen regiones coincidentes, y el rango en longitud de la representación WKT de los límites de la ciudad dentro de esas regiones:

centroideRecuentomin_wktmax_wktregión
PUNTO (-32.56093470960719 32.598117914802714)90207207nulo
POINT (-73.94515332765877 40.70366442203522)9438438Ciudad de Nueva York
POINT (-83.10398317873478 42.300230911932886)9473473Detroit
POINT (-156.3020245861262 20.176383580081165)5307803Hawai
POINT (-73.88902732171118 45.57078813901171)4837837Montréal

Entonces, ¿qué pasó realmente aquí? ¿Dónde ocurrió la supuesta JOIN ? El núcleo de la consulta reside en el comando ENRICH :

Este comando instruye a Elasticsearch para enriquecer los resultados recuperados del índice de airports y realizar una unión intersects entre el campo city_location del índice original y el campo city_boundary del índice de airport_city_boundaries , que usamos en algunos ejemplos anteriores. Pero parte de esta información no es claramente visible en esta consulta. Lo que sí vemos es el nombre de una política de enriquecimiento city_boundaries, y la información que falta está encapsulada dentro de esa definición de política.

Aquí podemos ver que realizará una consulta geo_match (intersects es el valor predeterminado), el campo a emparejar es city_boundary, y los enrich_fields son los campos que queremos agregar al documento original. Uno de esos campos, el region , se usó como clave de agrupación para el comando STATS , algo que no podríamos hacer sin esta capacidad de 'unión izquierda'. Para más información sobre las pólizas de enriquecimiento, consulte la documentación de enriquecimiento. Al leer esos documentos, notarás que describen el uso de los índices enrich para enriquecer datos en tiempo de indexación, configurando pipelines de ingest. Esto no es necesario para ES|QL, ya que el comando ENRICH funciona en el momento de la consulta. Basta con preparar el índice de enriquecimiento con los datos y la política de enriquecimiento necesarios, y luego usar el comando ENRICH en tu ES|Consultas QL.

También puede que notes que la región más común era null. ¿Qué podría implicar esto? Recuerda que comparé este comando con un 'left join' en SQL, lo que significa que si no se encuentra ningún límite de ciudad coincidente para un aeropuerto, el aeropuerto sigue devolver pero con valores null para los campos del índice de airport_city_boundaries . Resulta que había 89 aeropuertos que no encontraron city_boundarycoincidentes, y un aeropuerto con un region campo de null. Esto llevó a un recuento de 90 aeropuertos sin region en los resultados. Otro detalle interesante es la necesidad del comando MV_EXPAND . Esto es necesario porque el comando ENRICH puede devolver múltiples resultados por cada fila de entrada, y MV_EXPAND ayuda a separar estos resultados en varias filas, una para cada resultado. Esto también aclara por qué "Hawái" muestra resultados min_wkt y max_wkt diferentes: había varias regiones con el mismo nombre pero diferentes límites.

Kibana Maps

Kibana agregó soporte para Spatial ES|QL en la aplicación de Mapas. Esto significa que ahora puedes usar ES|QL para buscar datos geoespaciales en Elasticsearch y visualizar los resultados en un mapa.

Hay una nueva opción de capa en el menú de agregar capas, llamada "ES|QL". Como todas las características geoespaciales descritas hasta ahora, esto está en "vista previa técnica". Seleccionar esta opción te permite agregar una capa al mapa basada en los resultados de un ES|Consulta QL. Por ejemplo, podrías agregar una capa al mapa que muestre todos los aeropuertos del mundo.

O podrías agregar una capa que muestre los polígonos del índice de airport_city_boundaries , o mejor aún, ¿qué tal esa compleja consulta de ENRICH arriba que genera estadísticas sobre cuántos aeropuertos hay en cada región?

¿Qué sigue ahora?

Quizá notaste que en dos de los ejemplos anteriores incluimos otra función espacial ST_CENTROID_AGG. Esta es una función agregadora empleada en el comando STATS , y la primera de muchas funciones de análisis espacial que planeamos agregar a ES|QL. ¡Escribiremos sobre ello en el blog cuando tengamos más que mostrar!

Antes de eso, queremos contarte más sobre una función especialmente emocionante en la que trabajamos: la capacidad de realizar búsquedas espaciales, una de las funciones de búsqueda espacial más empleadas en Elasticsearch. ¿Te imaginas cómo sería la sintaxis de las búsquedas a distancia? ¿Quizá similar a una función OGC? ¡Permanece atento al próximo blog de este serial para descubrirlo!

Alerta de spoilers: Elasticsearch 8.15 acaba de ser lanzado, y la búsqueda por distancia espacial con ES|¡QL está incluido!

Contenido relacionado

¿Estás listo para crear experiencias de búsqueda de última generación?

No se logra una búsqueda suficientemente avanzada con los esfuerzos de uno. Elasticsearch está impulsado por científicos de datos, operaciones de ML, ingenieros y muchos más que son tan apasionados por la búsqueda como tú. Conectemos y trabajemos juntos para crear la experiencia mágica de búsqueda que te dará los resultados que deseas.

Pruébalo tú mismo