Агрегация по сетке геохэшей
Многокорзинная агрегация, которая группирует значения geo_point и geo_shape в корзины, представляющие сетку. Полученная сетка может быть разреженной и содержать только ячейки, содержащие соответствующие данные. Каждая ячейка обозначается с помощью геохэша, точность которого задаётся пользователем.
- Геохэши высокой точности имеют длинную строку и представляют ячейки, охватывающие небольшую область.
- Геохэши низкой точности имеют короткую строку и представляют ячейки, каждая из которых охватывает большую область.
Геохэши, используемые в этой агрегации, могут иметь точность от 1 до 12.
Геохэш наивысшей точности длиной 12 создаёт ячейки, покрывающие площадь менее одного квадратного метра, поэтому запросы высокой точности могут быть очень затратными в плане использования ОЗУ и размера результатов. Пожалуйста, смотрите пример ниже о том, как сначала отфильтровать агрегацию до меньшей географической области, прежде чем запрашивать высокую детализацию.
Можно использовать только geohash_grid для агрегирования явно сопоставленного поля geo_point или geo_shape. Если поле geo_point содержит массив, geohash_grid агрегирует все значения массива.
Простой запрос с низкой точностью
resp = client.indices.create(
index="museums",
mappings={
"properties": {
"location": {
"type": "geo_point"
}
}
},
)
print(resp)
resp1 = client.bulk(
index="museums",
refresh=True,
operations=[
{
"index": {
"_id": 1
}
},
{
"location": "POINT (4.912350 52.374081)",
"name": "NEMO Science Museum"
},
{
"index": {
"_id": 2
}
},
{
"location": "POINT (4.901618 52.369219)",
"name": "Museum Het Rembrandthuis"
},
{
"index": {
"_id": 3
}
},
{
"location": "POINT (4.914722 52.371667)",
"name": "Nederlands Scheepvaartmuseum"
},
{
"index": {
"_id": 4
}
},
{
"location": "POINT (4.405200 51.222900)",
"name": "Letterenhuis"
},
{
"index": {
"_id": 5
}
},
{
"location": "POINT (2.336389 48.861111)",
"name": "Musée du Louvre"
},
{
"index": {
"_id": 6
}
},
{
"location": "POINT (2.327000 48.860000)",
"name": "Musée d'Orsay"
}
],
)
print(resp1)
resp2 = client.search(
index="museums",
size="0",
aggregations={
"large-grid": {
"geohash_grid": {
"field": "location",
"precision": 3
}
}
},
)
print(resp2) response = client.indices.create(
index: 'museums',
body: {
mappings: {
properties: {
location: {
type: 'geo_point'
}
}
}
}
)
puts response
response = client.bulk(
index: 'museums',
refresh: true,
body: [
{
index: {
_id: 1
}
},
{
location: 'POINT (4.912350 52.374081)',
name: 'NEMO Science Museum'
},
{
index: {
_id: 2
}
},
{
location: 'POINT (4.901618 52.369219)',
name: 'Museum Het Rembrandthuis'
},
{
index: {
_id: 3
}
},
{
location: 'POINT (4.914722 52.371667)',
name: 'Nederlands Scheepvaartmuseum'
},
{
index: {
_id: 4
}
},
{
location: 'POINT (4.405200 51.222900)',
name: 'Letterenhuis'
},
{
index: {
_id: 5
}
},
{
location: 'POINT (2.336389 48.861111)',
name: 'Musée du Louvre'
},
{
index: {
_id: 6
}
},
{
location: 'POINT (2.327000 48.860000)',
name: "Musée d'Orsay"
}
]
)
puts response
response = client.search(
index: 'museums',
size: 0,
body: {
aggregations: {
"large-grid": {
geohash_grid: {
field: 'location',
precision: 3
}
}
}
}
)
puts response const response = await client.indices.create({
index: "museums",
mappings: {
properties: {
location: {
type: "geo_point",
},
},
},
});
console.log(response);
const response1 = await client.bulk({
index: "museums",
refresh: "true",
operations: [
{
index: {
_id: 1,
},
},
{
location: "POINT (4.912350 52.374081)",
name: "NEMO Science Museum",
},
{
index: {
_id: 2,
},
},
{
location: "POINT (4.901618 52.369219)",
name: "Museum Het Rembrandthuis",
},
{
index: {
_id: 3,
},
},
{
location: "POINT (4.914722 52.371667)",
name: "Nederlands Scheepvaartmuseum",
},
{
index: {
_id: 4,
},
},
{
location: "POINT (4.405200 51.222900)",
name: "Letterenhuis",
},
{
index: {
_id: 5,
},
},
{
location: "POINT (2.336389 48.861111)",
name: "Musée du Louvre",
},
{
index: {
_id: 6,
},
},
{
location: "POINT (2.327000 48.860000)",
name: "Musée d'Orsay",
},
],
});
console.log(response1);
const response2 = await client.search({
index: "museums",
size: 0,
aggregations: {
"large-grid": {
geohash_grid: {
field: "location",
precision: 3,
},
},
},
});
console.log(response2); PUT /museums
{
"mappings": {
"properties": {
"location": {
"type": "geo_point"
}
}
}
}
POST /museums/_bulk?refresh
{"index":{"_id":1}}
{"location": "POINT (4.912350 52.374081)", "name": "NEMO Science Museum"}
{"index":{"_id":2}}
{"location": "POINT (4.901618 52.369219)", "name": "Museum Het Rembrandthuis"}
{"index":{"_id":3}}
{"location": "POINT (4.914722 52.371667)", "name": "Nederlands Scheepvaartmuseum"}
{"index":{"_id":4}}
{"location": "POINT (4.405200 51.222900)", "name": "Letterenhuis"}
{"index":{"_id":5}}
{"location": "POINT (2.336389 48.861111)", "name": "Musée du Louvre"}
{"index":{"_id":6}}
{"location": "POINT (2.327000 48.860000)", "name": "Musée d'Orsay"}
POST /museums/_search?size=0
{
"aggregations": {
"large-grid": {
"geohash_grid": {
"field": "location",
"precision": 3
}
}
}
} Ответ:
{
...
"aggregations": {
"large-grid": {
"buckets": [
{
"key": "u17",
"doc_count": 3
},
{
"key": "u09",
"doc_count": 2
},
{
"key": "u15",
"doc_count": 1
}
]
}
}
} Запросы высокой точности
При запросе детальных корзин (как правило, для отображения «приближенного» масштаба карты) следует применять фильтр, такой как geo_bounding_box, чтобы сузить область, иначе может быть создано и возвращено потенциально миллионы корзин.
resp = client.search(
index="museums",
size="0",
aggregations={
"zoomed-in": {
"filter": {
"geo_bounding_box": {
"location": {
"top_left": "POINT (4.9 52.4)",
"bottom_right": "POINT (5.0 52.3)"
}
}
},
"aggregations": {
"zoom1": {
"geohash_grid": {
"field": "location",
"precision": 8
}
}
}
}
},
)
print(resp) response = client.search(
index: 'museums',
size: 0,
body: {
aggregations: {
"zoomed-in": {
filter: {
geo_bounding_box: {
location: {
top_left: 'POINT (4.9 52.4)',
bottom_right: 'POINT (5.0 52.3)'
}
}
},
aggregations: {
"zoom1": {
geohash_grid: {
field: 'location',
precision: 8
}
}
}
}
}
}
)
puts response const response = await client.search({
index: "museums",
size: 0,
aggregations: {
"zoomed-in": {
filter: {
geo_bounding_box: {
location: {
top_left: "POINT (4.9 52.4)",
bottom_right: "POINT (5.0 52.3)",
},
},
},
aggregations: {
zoom1: {
geohash_grid: {
field: "location",
precision: 8,
},
},
},
},
},
});
console.log(response); POST /museums/_search?size=0
{
"aggregations": {
"zoomed-in": {
"filter": {
"geo_bounding_box": {
"location": {
"top_left": "POINT (4.9 52.4)",
"bottom_right": "POINT (5.0 52.3)"
}
}
},
"aggregations": {
"zoom1": {
"geohash_grid": {
"field": "location",
"precision": 8
}
}
}
}
}
} Геохэши, возвращаемые агрегацией geohash_grid, также могут использоваться для приближения. Чтобы приблизиться к первому геохэшу u17, возвращённому в предыдущем примере, он должен быть указан как угол top_left и bottom_right.
resp = client.search(
index="museums",
size="0",
aggregations={
"zoomed-in": {
"filter": {
"geo_bounding_box": {
"location": {
"top_left": "u17",
"bottom_right": "u17"
}
}
},
"aggregations": {
"zoom1": {
"geohash_grid": {
"field": "location",
"precision": 8
}
}
}
}
},
)
print(resp) response = client.search(
index: 'museums',
size: 0,
body: {
aggregations: {
"zoomed-in": {
filter: {
geo_bounding_box: {
location: {
top_left: 'u17',
bottom_right: 'u17'
}
}
},
aggregations: {
"zoom1": {
geohash_grid: {
field: 'location',
precision: 8
}
}
}
}
}
}
)
puts response const response = await client.search({
index: "museums",
size: 0,
aggregations: {
"zoomed-in": {
filter: {
geo_bounding_box: {
location: {
top_left: "u17",
bottom_right: "u17",
},
},
},
aggregations: {
zoom1: {
geohash_grid: {
field: "location",
precision: 8,
},
},
},
},
},
});
console.log(response); POST /museums/_search?size=0
{
"aggregations": {
"zoomed-in": {
"filter": {
"geo_bounding_box": {
"location": {
"top_left": "u17",
"bottom_right": "u17"
}
}
},
"aggregations": {
"zoom1": {
"geohash_grid": {
"field": "location",
"precision": 8
}
}
}
}
}
} {
...
"aggregations": {
"zoomed-in": {
"doc_count": 3,
"zoom1": {
"buckets": [
{
"key": "u173zy3j",
"doc_count": 1
},
{
"key": "u173zvfz",
"doc_count": 1
},
{
"key": "u173zt90",
"doc_count": 1
}
]
}
}
}
} Для «приближения» на системах, которые не поддерживают геохэши, ключи корзин должны быть переведены в прямоугольники с помощью одной из доступных библиотек геохэшей. Например, для JavaScript можно использовать библиотеку node-geohash:
var geohash = require('ngeohash');
// bbox will contain [ 52.03125, 4.21875, 53.4375, 5.625 ]
// [ minlat, minlon, maxlat, maxlon]
var bbox = geohash.decode_bbox('u17'); Запросы с дополнительной фильтрацией по прямоугольнику
Агрегация geohash_grid поддерживает необязательный параметр bounds, который ограничивает рассматриваемые ячейки теми, которые пересекают заданные границы. Параметр bounds принимает прямоугольник в тех же поддерживаемых форматах, что и прямоугольник, указанный в запросе геометрической фильтрации по прямоугольнику. Этот прямоугольник можно использовать с или без дополнительного запроса geo_bounding_box, фильтрующего точки перед агрегацией. Это независимый прямоугольник, который может пересекаться с, быть равен или не пересекаться с любыми дополнительными запросами geo_bounding_box, определёнными в контексте агрегации.
resp = client.search(
index="museums",
size="0",
aggregations={
"tiles-in-bounds": {
"geohash_grid": {
"field": "location",
"precision": 8,
"bounds": {
"top_left": "POINT (4.21875 53.4375)",
"bottom_right": "POINT (5.625 52.03125)"
}
}
}
},
)
print(resp) response = client.search(
index: 'museums',
size: 0,
body: {
aggregations: {
"tiles-in-bounds": {
geohash_grid: {
field: 'location',
precision: 8,
bounds: {
top_left: 'POINT (4.21875 53.4375)',
bottom_right: 'POINT (5.625 52.03125)'
}
}
}
}
}
)
puts response const response = await client.search({
index: "museums",
size: 0,
aggregations: {
"tiles-in-bounds": {
geohash_grid: {
field: "location",
precision: 8,
bounds: {
top_left: "POINT (4.21875 53.4375)",
bottom_right: "POINT (5.625 52.03125)",
},
},
},
},
});
console.log(response); POST /museums/_search?size=0
{
"aggregations": {
"tiles-in-bounds": {
"geohash_grid": {
"field": "location",
"precision": 8,
"bounds": {
"top_left": "POINT (4.21875 53.4375)",
"bottom_right": "POINT (5.625 52.03125)"
}
}
}
}
} {
...
"aggregations": {
"tiles-in-bounds": {
"buckets": [
{
"key": "u173zy3j",
"doc_count": 1
},
{
"key": "u173zvfz",
"doc_count": 1
},
{
"key": "u173zt90",
"doc_count": 1
}
]
}
}
} Размеры ячеек на экваторе
В таблице ниже показаны метрические размеры ячеек, покрываемых геохэшами различной длины. Размеры ячеек изменяются в зависимости от широты, поэтому таблица отражает наихудший случай на экваторе.
| Длина геохэша | Ширина x высота ячейки |
| 1 | 5009,4км x 4992,6км |
| 2 | 1252,3км x 624,1км |
| 3 | 156,5км x 156км |
| 4 | 39,1км x 19,5км |
| 5 | 4,9км x 4,9км |
| 6 | 1,2км x 609,4м |
| 7 | 152,9м x 152,4м |
| 8 | 38,2м x 19м |
| 9 | 4,8м x 4,8м |
| 10 | 1,2м x 59,5см |
| 11 | 14,9см x 14,9см |
| 12 | 3,7см x 1,9см |
Агрегирование полей geo_shape
Агрегирование по полям типа Geoshape работает так же, как и для точек, за исключением того, что одну фигуру можно учитывать в нескольких тайлах. Фигура будет учитываться в подсчёте соответствующих значений, если любая её часть пересекает данный тайл. Ниже представлена иллюстрация:
Параметры
| field | Обязательно. Поле, содержащее индексированные значения geo-point или geo-shape. Должно быть явно сопоставлено как поле |
| precision | Необязательно. Длина строки геохэшей, используемых для определения ячеек/корзин в результатах. По умолчанию 5. Точность может быть задана в виде целочисленных значений, упомянутых выше. Значения вне диапазона [1,12] будут отклонены. Также точность можно приблизить по расстоянию, например, "1км", "10м". Уровень точности рассчитывается таким образом, чтобы ячейки не превышали заданный размер (диагональ) требуемой точности. Если это приведёт к уровням точности выше, чем поддерживаемые 12 уровней (например, для расстояний <5,6см), значение отклоняется. |
| bounds | Необязательно. Прямоугольник для фильтрации точек в корзине. |
| size | Необязательно. Максимальное количество корзин геохэшей для возврата (по умолчанию 10 000). При усечении результатов корзины упорядочиваются в зависимости от объёма документов, которые они содержат. |
| shard_size | Необязательно. Для более точного подсчёта верхних ячеек, возвращаемых в конечном результате, агрегация по умолчанию возвращает |
© 2023-2025 Elasticsearch
As of September 2024, Elasticsearch is available under a choice of three licenses: the Server Side Public License (SSPL), the Elastic License, or the AGPLv3 (OSI approved).
Elasticsearch and the Elasticsearch logo are trademarks of Elasticsearch B.V., registered in the U.S. and in other countries.
https://www.elastic.co/guide/en/elasticsearch/reference/8.17/search-aggregations-bucket-geohashgrid-aggregation.html