Перейти к основному содержимому

Геопространственная поддержка в ElasticSearch

· 7 мин. чтения

1. Введение

Elasticsearch наиболее известен своими возможностями полнотекстового поиска, но также имеет полную геопространственную поддержку.

Мы можем узнать больше о настройке Elasticsearch и начале работы в этой предыдущей статье .

Давайте посмотрим, как мы можем сохранять геоданные в Elasticsearch и как мы можем искать эти данные с помощью геозапросов.

2. Тип географических данных

Чтобы включить гео-запросы, нам нужно создать сопоставление индекса вручную и явно задать сопоставление полей.

Динамическое сопоставление не будет работать при настройке сопоставления для геотипов.

Elasticsearch предлагает два способа представления геоданных:

  1. Пары широта-долгота с использованием типа поля геоточки
  2. Сложная форма, определенная в GeoJSON с использованием типа поля гео-формы

Давайте более подробно рассмотрим каждую из вышеперечисленных категорий:

2.1. Тип данных геоточки

Тип поля Geo-point принимает пары широта-долгота, которые можно использовать для:

  • Найти точки на определенном расстоянии от центральной точки
  • Поиск точек внутри прямоугольника или многоугольника
  • Агрегируйте документы географически или по расстоянию от центральной точки
  • Сортировать документы по расстоянию

Ниже приведен пример сопоставления поля для сохранения данных геоточки:

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

Как видно из приведенного выше примера, тип поля местоположенияgeo_point . Таким образом, теперь мы можем указать пару широта-долгота в поле местоположения .

2.2. Тип данных Geo Shape

В отличие от геоточки , геофигуры позволяют сохранять и искать сложные фигуры, такие как многоугольник и прямоугольник. Тип данных Geo shape должен использоваться, когда мы хотим искать документы, содержащие фигуры, отличные от географических точек.

Давайте посмотрим на сопоставление для типа данных геофигуры:

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

Последние версии Elasticsearch разбивают предоставленную гео-форму на треугольную сетку . Согласно официальной документации , это обеспечивает почти идеальное пространственное разрешение.

3. Различные способы сохранения данных геоточки

3.1. Широта Долгота Объект

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

Здесь местоположение геоточки сохраняется как объект с широтой и долготой в качестве ключей.

3.2. Пара широты и долготы

{
"location": "23.02,72.57"
}

Здесь местоположение выражается парой широта-долгота в формате простой строки. Обратите внимание, последовательность широты и долготы в строковом формате.

3.3. Гео Хэш

{
"location": "tsj4bys"
}

Мы также можем предоставить данные геоточки в виде геохэша, как показано в примере выше. Мы можем использовать онлайн-инструмент для преобразования широты-долготы в гео-хеш.

3.4. Массив долготы и широты

{
"location": [72.57, 23.02]
}

Последовательность широта-долгота меняется на противоположную, когда широта и долгота предоставляются в виде массива. Первоначально пара широта-долгота использовалась как в строке, так и в массиве, но позже она была изменена, чтобы соответствовать формату, используемому GeoJSON .

4. Различные способы сохранения данных Geo Shape

4.1. Точка

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

Здесь тип геофигуры, которую мы пытаемся вставить, — это точка . Пожалуйста, взгляните на поле местоположения , у нас есть вложенный объект, состоящий из типов полей и координат . Эти метаполя помогают Elasticsearch идентифицировать геоформу и ее фактические данные.

4.2. LineString

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

Здесь мы вставляем гео-форму линии . Координаты для linestring состоят из двух точек, т.е. начальной и конечной точки. Геоформа LineString очень полезна для вариантов использования навигации.

4.3. Полигон

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

Здесь мы вставляем геоформу многоугольника . Пожалуйста, взгляните на координаты в приведенном выше примере, первая и последняя координаты в многоугольнике всегда должны совпадать, т.е. замкнутый многоугольник.

Elasticsearch также поддерживает другие структуры GeoJSON. Полный список других поддерживаемых форматов приведен ниже:

  • Многоточечный
  • MultiLineString
  • Мультиполигон
  • ГеометрияКоллекция
  • Конверт
  • Круг

Мы можем найти примеры поддерживаемых выше форматов на официальном сайте ES .

Для всех структур внутренний тип и координаты являются обязательными полями. Кроме того, сортировка и извлечение полей геофигур в настоящее время невозможны в Elasticsearch из-за их сложной структуры. Таким образом, единственный способ получить геополя — из исходного поля.

5. Гео-запрос ElasticSearch

Теперь, когда мы знаем, как вставлять документы, содержащие геофигуры, давайте углубимся в получение этих записей с помощью запросов геофигур. Но прежде чем мы начнем использовать Geo Queries, нам понадобятся следующие зависимости maven для поддержки Java API для Geo Queries:

<dependency>
<groupId>org.locationtech.spatial4j</groupId>
<artifactId>spatial4j</artifactId>
<version>0.7</version>
</dependency>
<dependency>
<groupId>com.vividsolutions</groupId>
<artifactId>jts</artifactId>
<version>1.13</version>
<exclusions>
<exclusion>
<groupId>xerces</groupId>
<artifactId>xercesImpl</artifactId>
</exclusion>
</exclusions>
</dependency>

Мы также можем искать вышеуказанные зависимости в репозитории Maven Central .

Elasticsearch поддерживает различные типы гео-запросов, и они следующие:

5.1. Запрос геоформы

Для этого требуется отображение geo_shape .

Подобно типу geo_shape , geo_shape использует структуру GeoJSON для запроса документов.

Ниже приведен пример запроса для получения всех документов, попадающих в заданные координаты верхнего левого и нижнего правого угла:

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

Здесь отношение определяет операторы пространственного отношения, используемые во время поиска.

Ниже приведен список поддерживаемых операторов:

  • INTERSECTS — (по умолчанию) возвращает все документы, поле geo_shape которых пересекает геометрию запроса.
  • DISJOINT — извлекает все документы, поле geo_shape которых не имеет ничего общего с геометрией запроса.
  • WITHIN — получает все документы, поле geo_shape которых находится в пределах геометрии запроса.
  • CONTAINS — возвращает все документы, поле geo_shape которых содержит геометрию запроса

Точно так же мы можем запрашивать, используя различные формы GeoJSON.

Код Java для вышеуказанного запроса выглядит следующим образом:

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": {
"bool" : {
"must" : {
"match_all" : {}
},
"filter" : {
"geo_bounding_box" : {
"location" : {
"bottom_left" : [28.3, 30.5],
"top_right" : [31.8, 32.12]
}
}
}
}
}
}

Код Java для вышеуказанного запроса ограничивающей рамки выглядит следующим образом:

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

Запрос Geo Bounding Box поддерживает те же форматы, что и в типе данных geo_point . Примеры запросов для поддерживаемых форматов можно найти на официальном сайте .

5.3. Запрос географического расстояния

Запрос географического расстояния используется для фильтрации всех документов, которые приходят с указанным диапазоном точки.

Вот пример запроса geo_distance :

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

И вот код Java для вышеуказанного запроса:

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

Подобно geo_point, запрос географического расстояния также поддерживает несколько форматов для передачи координат местоположения. Подробнее о поддерживаемых форматах можно узнать на официальном сайте .

5.4. Запрос геополигона _

Запрос для фильтрации всех записей, имеющих точки, попадающие в заданный полигон точек.

Давайте быстро рассмотрим пример запроса:

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

И в коде Java для этого запроса:

List<GeoPoint> allPoints = new ArrayList<GeoPoint>(); 
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 также поддерживает форматы, указанные ниже:

  • lat-long как массив: [lon, lat]
  • lat-long как строка: «lat, lon»
  • гео хэш

Тип данных geo_point является обязательным для использования этого запроса.

6. Заключение

В этой статье мы обсудили различные варианты сопоставления для индексации геоданных, например , geo_point и geo_shape .

Мы также использовали различные способы хранения геоданных и, наконец, рассмотрели геозапросы и Java API для фильтрации результатов с помощью геозапросов.

Как всегда, код доступен в этом проекте GitHub .