1. Введение
Elasticsearch наиболее известен своими возможностями полнотекстового поиска, но также имеет полную геопространственную поддержку.
Мы можем узнать больше о настройке Elasticsearch и начале работы в этой предыдущей статье .
Давайте посмотрим, как мы можем сохранять геоданные в Elasticsearch и как мы можем искать эти данные с помощью геозапросов.
2. Тип географических данных
Чтобы включить гео-запросы, нам нужно создать сопоставление индекса вручную и явно задать сопоставление полей.
Динамическое сопоставление не будет работать при настройке сопоставления для геотипов.
Elasticsearch предлагает два способа представления геоданных:
- Пары широта-долгота с использованием типа поля геоточки
- Сложная форма, определенная в
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 .