Spec-Zone.ru › Elasticsearch 8
›Руководство по Elasticsearch [8.17] ›Агрегации ›Агрегации метрик

Агрегация Geo-line

Агрегация geo_line агрегирует все geo_point значения внутри корзины в LineString, отсортированную по выбранному полю sort. Это sort может быть, например, полем даты. Возвращаемая корзина является допустимым GeoJSON-признаком, представляющим геометрию линии.

resp = client.indices.create(
    index="test",
    mappings={
        "properties": {
            "my_location": {
                "type": "geo_point"
            },
            "group": {
                "type": "keyword"
            },
            "@timestamp": {
                "type": "date"
            }
        }
    },
)
print(resp)

resp1 = client.bulk(
    index="test",
    refresh=True,
    operations=[
        {
            "index": {}
        },
        {
            "my_location": {
                "lat": 52.373184,
                "lon": 4.889187
            },
            "@timestamp": "2023-01-02T09:00:00Z"
        },
        {
            "index": {}
        },
        {
            "my_location": {
                "lat": 52.370159,
                "lon": 4.885057
            },
            "@timestamp": "2023-01-02T10:00:00Z"
        },
        {
            "index": {}
        },
        {
            "my_location": {
                "lat": 52.369219,
                "lon": 4.901618
            },
            "@timestamp": "2023-01-02T13:00:00Z"
        },
        {
            "index": {}
        },
        {
            "my_location": {
                "lat": 52.374081,
                "lon": 4.91235
            },
            "@timestamp": "2023-01-02T16:00:00Z"
        },
        {
            "index": {}
        },
        {
            "my_location": {
                "lat": 52.371667,
                "lon": 4.914722
            },
            "@timestamp": "2023-01-03T12:00:00Z"
        }
    ],
)
print(resp1)

resp2 = client.search(
    index="test",
    filter_path="aggregations",
    aggs={
        "line": {
            "geo_line": {
                "point": {
                    "field": "my_location"
                },
                "sort": {
                    "field": "@timestamp"
                }
            }
        }
    },
)
print(resp2)
response = client.indices.create(
  index: 'test',
  body: {
    mappings: {
      properties: {
        my_location: {
          type: 'geo_point'
        },
        group: {
          type: 'keyword'
        },
        "@timestamp": {
          type: 'date'
        }
      }
    }
  }
)
puts response

response = client.bulk(
  index: 'test',
  refresh: true,
  body: [
    {
      index: {}
    },
    {
      my_location: {
        lat: 52.373184,
        lon: 4.889187
      },
      "@timestamp": '2023-01-02T09:00:00Z'
    },
    {
      index: {}
    },
    {
      my_location: {
        lat: 52.370159,
        lon: 4.885057
      },
      "@timestamp": '2023-01-02T10:00:00Z'
    },
    {
      index: {}
    },
    {
      my_location: {
        lat: 52.369219,
        lon: 4.901618
      },
      "@timestamp": '2023-01-02T13:00:00Z'
    },
    {
      index: {}
    },
    {
      my_location: {
        lat: 52.374081,
        lon: 4.91235
      },
      "@timestamp": '2023-01-02T16:00:00Z'
    },
    {
      index: {}
    },
    {
      my_location: {
        lat: 52.371667,
        lon: 4.914722
      },
      "@timestamp": '2023-01-03T12:00:00Z'
    }
  ]
)
puts response

response = client.search(
  index: 'test',
  filter_path: 'aggregations',
  body: {
    aggregations: {
      line: {
        geo_line: {
          point: {
            field: 'my_location'
          },
          sort: {
            field: '@timestamp'
          }
        }
      }
    }
  }
)
puts response
const response = await client.indices.create({
  index: "test",
  mappings: {
    properties: {
      my_location: {
        type: "geo_point",
      },
      group: {
        type: "keyword",
      },
      "@timestamp": {
        type: "date",
      },
    },
  },
});
console.log(response);

const response1 = await client.bulk({
  index: "test",
  refresh: "true",
  operations: [
    {
      index: {},
    },
    {
      my_location: {
        lat: 52.373184,
        lon: 4.889187,
      },
      "@timestamp": "2023-01-02T09:00:00Z",
    },
    {
      index: {},
    },
    {
      my_location: {
        lat: 52.370159,
        lon: 4.885057,
      },
      "@timestamp": "2023-01-02T10:00:00Z",
    },
    {
      index: {},
    },
    {
      my_location: {
        lat: 52.369219,
        lon: 4.901618,
      },
      "@timestamp": "2023-01-02T13:00:00Z",
    },
    {
      index: {},
    },
    {
      my_location: {
        lat: 52.374081,
        lon: 4.91235,
      },
      "@timestamp": "2023-01-02T16:00:00Z",
    },
    {
      index: {},
    },
    {
      my_location: {
        lat: 52.371667,
        lon: 4.914722,
      },
      "@timestamp": "2023-01-03T12:00:00Z",
    },
  ],
});
console.log(response1);

const response2 = await client.search({
  index: "test",
  filter_path: "aggregations",
  aggs: {
    line: {
      geo_line: {
        point: {
          field: "my_location",
        },
        sort: {
          field: "@timestamp",
        },
      },
    },
  },
});
console.log(response2);
PUT test
{
    "mappings": {
        "properties": {
            "my_location": { "type": "geo_point" },
            "group":       { "type": "keyword" },
            "@timestamp":  { "type": "date" }
        }
    }
}

POST /test/_bulk?refresh
{"index":{}}
{"my_location": {"lat":52.373184, "lon":4.889187}, "@timestamp": "2023-01-02T09:00:00Z"}
{"index":{}}
{"my_location": {"lat":52.370159, "lon":4.885057}, "@timestamp": "2023-01-02T10:00:00Z"}
{"index":{}}
{"my_location": {"lat":52.369219, "lon":4.901618}, "@timestamp": "2023-01-02T13:00:00Z"}
{"index":{}}
{"my_location": {"lat":52.374081, "lon":4.912350}, "@timestamp": "2023-01-02T16:00:00Z"}
{"index":{}}
{"my_location": {"lat":52.371667, "lon":4.914722}, "@timestamp": "2023-01-03T12:00:00Z"}

POST /test/_search?filter_path=aggregations
{
  "aggs": {
    "line": {
      "geo_line": {
        "point": {"field": "my_location"},
        "sort":  {"field": "@timestamp"}
      }
    }
  }
}

Что возвращает:

{
  "aggregations": {
    "line": {
      "type": "Feature",
      "geometry": {
        "type": "LineString",
        "coordinates": [
            [ 4.889187, 52.373184 ],
            [ 4.885057, 52.370159 ],
            [ 4.901618, 52.369219 ],
            [ 4.912350, 52.374081 ],
            [ 4.914722, 52.371667 ]
        ]
      },
      "properties": {
        "complete": true
      }
    }
  }
}

Результат GeoJSON Feature содержит как геометрию LineString для пути, сгенерированного агрегацией, так и карту properties. Свойство complete указывает, использовались ли все сопоставленные документы для генерации геометрии. size можно использовать для ограничения количества документов, включённых в агрегацию, что приводит к результатам с complete: false. Точно то, какие документы исключаются из результатов зависит от того, основана ли агрегация на time_series или нет.

Этот результат может отображаться в пользовательском интерфейсе карты:

Kibana map with museum tour of Amsterdam

Параметры

point
(Обязательно)

Этот параметр указывает имя поля geo_point

Пример использования, настроив my_location как поле точки:

"point": {
  "field": "my_location"
}
sort
(Обязательно вне time_series агрегаций)

Этот параметр указывает имя числового поля, которое следует использовать в качестве ключа сортировки для упорядочивания точек. Когда агрегация geo_line вложена внутрь time_series агрегации, это поле по умолчанию равно @timestamp, а любое другое значение приведёт к ошибке.

Пример использования, настроив @timestamp в качестве ключа сортировки:

"sort": {
  "field": "@timestamp"
}
include_sort
(Необязательно, булево, по умолчанию: false) Этот параметр включает, если true, дополнительный массив значений сортировки в свойствах элемента.
sort_order
(Необязательно, строка, по умолчанию: "ASC") Этот параметр принимает одно из двух значений: "ASC", "DESC". Линия сортируется по возрастанию ключа сортировки, если установлено "ASC", и по убыванию при "DESC".
size
(Необязательно, целое число, по умолчанию: 10000) Максимальная длина линии, представленной в агрегации. Допустимые размеры находятся в диапазоне от одного до 10000. В time_series агрегация использует упрощение линии для ограничения размера, в противном случае используется усечение. См. Почему группировать с временными рядами? для обсуждения связанных нюансов.

Группировка

Этот простой пример создаёт одну дорожку для всех данных, выбранных запросом. Однако гораздо чаще требуется сгруппировать данные в несколько дорожек. Например, группировать измерения транспондера полёта по имени полёта перед сортировкой каждого полёта по отметке времени и созданием отдельной дорожки для каждого.

В следующих примерах мы сгруппируем местоположения точек интереса в Амстердаме, Антверпене и Париже. Дорожки будут отсортированы по запланированному порядку посещения музеев и других достопримечательностей.

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

resp = client.indices.create(
    index="tour",
    mappings={
        "properties": {
            "city": {
                "type": "keyword",
                "time_series_dimension": True
            },
            "category": {
                "type": "keyword"
            },
            "route": {
                "type": "long"
            },
            "name": {
                "type": "keyword"
            },
            "location": {
                "type": "geo_point"
            },
            "@timestamp": {
                "type": "date"
            }
        }
    },
    settings={
        "index": {
            "mode": "time_series",
            "routing_path": [
                "city"
            ],
            "time_series": {
                "start_time": "2023-01-01T00:00:00Z",
                "end_time": "2024-01-01T00:00:00Z"
            }
        }
    },
)
print(resp)

resp1 = client.bulk(
    index="tour",
    refresh=True,
    operations=[
        {
            "index": {}
        },
        {
            "@timestamp": "2023-01-02T09:00:00Z",
            "route": 0,
            "location": "POINT(4.889187 52.373184)",
            "city": "Amsterdam",
            "category": "Attraction",
            "name": "Royal Palace Amsterdam"
        },
        {
            "index": {}
        },
        {
            "@timestamp": "2023-01-02T10:00:00Z",
            "route": 1,
            "location": "POINT(4.885057 52.370159)",
            "city": "Amsterdam",
            "category": "Attraction",
            "name": "The Amsterdam Dungeon"
        },
        {
            "index": {}
        },
        {
            "@timestamp": "2023-01-02T13:00:00Z",
            "route": 2,
            "location": "POINT(4.901618 52.369219)",
            "city": "Amsterdam",
            "category": "Museum",
            "name": "Museum Het Rembrandthuis"
        },
        {
            "index": {}
        },
        {
            "@timestamp": "2023-01-02T16:00:00Z",
            "route": 3,
            "location": "POINT(4.912350 52.374081)",
            "city": "Amsterdam",
            "category": "Museum",
            "name": "NEMO Science Museum"
        },
        {
            "index": {}
        },
        {
            "@timestamp": "2023-01-03T12:00:00Z",
            "route": 4,
            "location": "POINT(4.914722 52.371667)",
            "city": "Amsterdam",
            "category": "Museum",
            "name": "Nederlands Scheepvaartmuseum"
        },
        {
            "index": {}
        },
        {
            "@timestamp": "2023-01-04T09:00:00Z",
            "route": 5,
            "location": "POINT(4.401384 51.220292)",
            "city": "Antwerp",
            "category": "Attraction",
            "name": "Cathedral of Our Lady"
        },
        {
            "index": {}
        },
        {
            "@timestamp": "2023-01-04T12:00:00Z",
            "route": 6,
            "location": "POINT(4.405819 51.221758)",
            "city": "Antwerp",
            "category": "Museum",
            "name": "Snijders&Rockoxhuis"
        },
        {
            "index": {}
        },
        {
            "@timestamp": "2023-01-04T15:00:00Z",
            "route": 7,
            "location": "POINT(4.405200 51.222900)",
            "city": "Antwerp",
            "category": "Museum",
            "name": "Letterenhuis"
        },
        {
            "index": {}
        },
        {
            "@timestamp": "2023-01-05T10:00:00Z",
            "route": 8,
            "location": "POINT(2.336389 48.861111)",
            "city": "Paris",
            "category": "Museum",
            "name": "Musée du Louvre"
        },
        {
            "index": {}
        },
        {
            "@timestamp": "2023-01-05T14:00:00Z",
            "route": 9,
            "location": "POINT(2.327000 48.860000)",
            "city": "Paris",
            "category": "Museum",
            "name": "Musée dOrsay"
        }
    ],
)
print(resp1)
response = client.indices.create(
  index: 'tour',
  body: {
    mappings: {
      properties: {
        city: {
          type: 'keyword',
          time_series_dimension: true
        },
        category: {
          type: 'keyword'
        },
        route: {
          type: 'long'
        },
        name: {
          type: 'keyword'
        },
        location: {
          type: 'geo_point'
        },
        "@timestamp": {
          type: 'date'
        }
      }
    },
    settings: {
      index: {
        mode: 'time_series',
        routing_path: [
          'city'
        ],
        time_series: {
          start_time: '2023-01-01T00:00:00Z',
          end_time: '2024-01-01T00:00:00Z'
        }
      }
    }
  }
)
puts response

response = client.bulk(
  index: 'tour',
  refresh: true,
  body: [
    {
      index: {}
    },
    {
      "@timestamp": '2023-01-02T09:00:00Z',
      route: 0,
      location: 'POINT(4.889187 52.373184)',
      city: 'Amsterdam',
      category: 'Attraction',
      name: 'Royal Palace Amsterdam'
    },
    {
      index: {}
    },
    {
      "@timestamp": '2023-01-02T10:00:00Z',
      route: 1,
      location: 'POINT(4.885057 52.370159)',
      city: 'Amsterdam',
      category: 'Attraction',
      name: 'The Amsterdam Dungeon'
    },
    {
      index: {}
    },
    {
      "@timestamp": '2023-01-02T13:00:00Z',
      route: 2,
      location: 'POINT(4.901618 52.369219)',
      city: 'Amsterdam',
      category: 'Museum',
      name: 'Museum Het Rembrandthuis'
    },
    {
      index: {}
    },
    {
      "@timestamp": '2023-01-02T16:00:00Z',
      route: 3,
      location: 'POINT(4.912350 52.374081)',
      city: 'Amsterdam',
      category: 'Museum',
      name: 'NEMO Science Museum'
    },
    {
      index: {}
    },
    {
      "@timestamp": '2023-01-03T12:00:00Z',
      route: 4,
      location: 'POINT(4.914722 52.371667)',
      city: 'Amsterdam',
      category: 'Museum',
      name: 'Nederlands Scheepvaartmuseum'
    },
    {
      index: {}
    },
    {
      "@timestamp": '2023-01-04T09:00:00Z',
      route: 5,
      location: 'POINT(4.401384 51.220292)',
      city: 'Antwerp',
      category: 'Attraction',
      name: 'Cathedral of Our Lady'
    },
    {
      index: {}
    },
    {
      "@timestamp": '2023-01-04T12:00:00Z',
      route: 6,
      location: 'POINT(4.405819 51.221758)',
      city: 'Antwerp',
      category: 'Museum',
      name: 'Snijders&Rockoxhuis'
    },
    {
      index: {}
    },
    {
      "@timestamp": '2023-01-04T15:00:00Z',
      route: 7,
      location: 'POINT(4.405200 51.222900)',
      city: 'Antwerp',
      category: 'Museum',
      name: 'Letterenhuis'
    },
    {
      index: {}
    },
    {
      "@timestamp": '2023-01-05T10:00:00Z',
      route: 8,
      location: 'POINT(2.336389 48.861111)',
      city: 'Paris',
      category: 'Museum',
      name: 'Musée du Louvre'
    },
    {
      index: {}
    },
    {
      "@timestamp": '2023-01-05T14:00:00Z',
      route: 9,
      location: 'POINT(2.327000 48.860000)',
      city: 'Paris',
      category: 'Museum',
      name: 'Musée dOrsay'
    }
  ]
)
puts response
const response = await client.indices.create({
  index: "tour",
  mappings: {
    properties: {
      city: {
        type: "keyword",
        time_series_dimension: true,
      },
      category: {
        type: "keyword",
      },
      route: {
        type: "long",
      },
      name: {
        type: "keyword",
      },
      location: {
        type: "geo_point",
      },
      "@timestamp": {
        type: "date",
      },
    },
  },
  settings: {
    index: {
      mode: "time_series",
      routing_path: ["city"],
      time_series: {
        start_time: "2023-01-01T00:00:00Z",
        end_time: "2024-01-01T00:00:00Z",
      },
    },
  },
});
console.log(response);

const response1 = await client.bulk({
  index: "tour",
  refresh: "true",
  operations: [
    {
      index: {},
    },
    {
      "@timestamp": "2023-01-02T09:00:00Z",
      route: 0,
      location: "POINT(4.889187 52.373184)",
      city: "Amsterdam",
      category: "Attraction",
      name: "Royal Palace Amsterdam",
    },
    {
      index: {},
    },
    {
      "@timestamp": "2023-01-02T10:00:00Z",
      route: 1,
      location: "POINT(4.885057 52.370159)",
      city: "Amsterdam",
      category: "Attraction",
      name: "The Amsterdam Dungeon",
    },
    {
      index: {},
    },
    {
      "@timestamp": "2023-01-02T13:00:00Z",
      route: 2,
      location: "POINT(4.901618 52.369219)",
      city: "Amsterdam",
      category: "Museum",
      name: "Museum Het Rembrandthuis",
    },
    {
      index: {},
    },
    {
      "@timestamp": "2023-01-02T16:00:00Z",
      route: 3,
      location: "POINT(4.912350 52.374081)",
      city: "Amsterdam",
      category: "Museum",
      name: "NEMO Science Museum",
    },
    {
      index: {},
    },
    {
      "@timestamp": "2023-01-03T12:00:00Z",
      route: 4,
      location: "POINT(4.914722 52.371667)",
      city: "Amsterdam",
      category: "Museum",
      name: "Nederlands Scheepvaartmuseum",
    },
    {
      index: {},
    },
    {
      "@timestamp": "2023-01-04T09:00:00Z",
      route: 5,
      location: "POINT(4.401384 51.220292)",
      city: "Antwerp",
      category: "Attraction",
      name: "Cathedral of Our Lady",
    },
    {
      index: {},
    },
    {
      "@timestamp": "2023-01-04T12:00:00Z",
      route: 6,
      location: "POINT(4.405819 51.221758)",
      city: "Antwerp",
      category: "Museum",
      name: "Snijders&Rockoxhuis",
    },
    {
      index: {},
    },
    {
      "@timestamp": "2023-01-04T15:00:00Z",
      route: 7,
      location: "POINT(4.405200 51.222900)",
      city: "Antwerp",
      category: "Museum",
      name: "Letterenhuis",
    },
    {
      index: {},
    },
    {
      "@timestamp": "2023-01-05T10:00:00Z",
      route: 8,
      location: "POINT(2.336389 48.861111)",
      city: "Paris",
      category: "Museum",
      name: "Musée du Louvre",
    },
    {
      index: {},
    },
    {
      "@timestamp": "2023-01-05T14:00:00Z",
      route: 9,
      location: "POINT(2.327000 48.860000)",
      city: "Paris",
      category: "Museum",
      name: "Musée dOrsay",
    },
  ],
});
console.log(response1);
PUT tour
{
    "mappings": {
        "properties": {
            "city": {
                "type": "keyword",
                "time_series_dimension": true
            },
            "category":   { "type": "keyword" },
            "route":      { "type": "long" },
            "name":       { "type": "keyword" },
            "location":   { "type": "geo_point" },
            "@timestamp": { "type": "date" }
        }
    },
    "settings": {
        "index": {
            "mode": "time_series",
            "routing_path": [ "city" ],
            "time_series": {
                "start_time": "2023-01-01T00:00:00Z",
                "end_time": "2024-01-01T00:00:00Z"
            }
        }
    }
}

POST /tour/_bulk?refresh
{"index":{}}
{"@timestamp": "2023-01-02T09:00:00Z", "route": 0, "location": "POINT(4.889187 52.373184)", "city": "Amsterdam", "category": "Attraction", "name": "Royal Palace Amsterdam"}
{"index":{}}
{"@timestamp": "2023-01-02T10:00:00Z", "route": 1, "location": "POINT(4.885057 52.370159)", "city": "Amsterdam", "category": "Attraction", "name": "The Amsterdam Dungeon"}
{"index":{}}
{"@timestamp": "2023-01-02T13:00:00Z", "route": 2, "location": "POINT(4.901618 52.369219)", "city": "Amsterdam", "category": "Museum", "name": "Museum Het Rembrandthuis"}
{"index":{}}
{"@timestamp": "2023-01-02T16:00:00Z", "route": 3, "location": "POINT(4.912350 52.374081)", "city": "Amsterdam", "category": "Museum", "name": "NEMO Science Museum"}
{"index":{}}
{"@timestamp": "2023-01-03T12:00:00Z", "route": 4, "location": "POINT(4.914722 52.371667)", "city": "Amsterdam", "category": "Museum", "name": "Nederlands Scheepvaartmuseum"}
{"index":{}}
{"@timestamp": "2023-01-04T09:00:00Z", "route": 5, "location": "POINT(4.401384 51.220292)", "city": "Antwerp", "category": "Attraction", "name": "Cathedral of Our Lady"}
{"index":{}}
{"@timestamp": "2023-01-04T12:00:00Z", "route": 6, "location": "POINT(4.405819 51.221758)", "city": "Antwerp", "category": "Museum", "name": "Snijders&Rockoxhuis"}
{"index":{}}
{"@timestamp": "2023-01-04T15:00:00Z", "route": 7, "location": "POINT(4.405200 51.222900)", "city": "Antwerp", "category": "Museum", "name": "Letterenhuis"}
{"index":{}}
{"@timestamp": "2023-01-05T10:00:00Z", "route": 8, "location": "POINT(2.336389 48.861111)", "city": "Paris", "category": "Museum", "name": "Musée du Louvre"}
{"index":{}}
{"@timestamp": "2023-01-05T14:00:00Z", "route": 9, "location": "POINT(2.327000 48.860000)", "city": "Paris", "category": "Museum", "name": "Musée dOrsay"}

Группировка с терминами

Используя эти данные, для случая без временных рядов, группировка может быть выполнена с помощью агрегации терминов на основе имени города. Это будет работать независимо от того, определили ли мы индекс tour как индекс временных рядов.

resp = client.search(
    index="tour",
    filter_path="aggregations",
    aggregations={
        "path": {
            "terms": {
                "field": "city"
            },
            "aggregations": {
                "museum_tour": {
                    "geo_line": {
                        "point": {
                            "field": "location"
                        },
                        "sort": {
                            "field": "@timestamp"
                        }
                    }
                }
            }
        }
    },
)
print(resp)
const response = await client.search({
  index: "tour",
  filter_path: "aggregations",
  aggregations: {
    path: {
      terms: {
        field: "city",
      },
      aggregations: {
        museum_tour: {
          geo_line: {
            point: {
              field: "location",
            },
            sort: {
              field: "@timestamp",
            },
          },
        },
      },
    },
  },
});
console.log(response);
POST /tour/_search?filter_path=aggregations
{
  "aggregations": {
    "path": {
      "terms": {"field": "city"},
      "aggregations": {
        "museum_tour": {
          "geo_line": {
            "point": {"field": "location"},
            "sort": {"field": "@timestamp"}
          }
        }
      }
    }
  }
}

Что возвращает:

{
  "aggregations": {
    "path": {
      "doc_count_error_upper_bound": 0,
      "sum_other_doc_count": 0,
      "buckets": [
        {
          "key": "Amsterdam",
          "doc_count": 5,
          "museum_tour": {
            "type": "Feature",
            "geometry": {
              "coordinates": [ [ 4.889187, 52.373184 ], [ 4.885057, 52.370159 ], [ 4.901618, 52.369219 ], [ 4.91235, 52.374081 ], [ 4.914722, 52.371667 ] ],
              "type": "LineString"
            },
            "properties": {
              "complete": true
            }
          }
        },
        {
          "key": "Antwerp",
          "doc_count": 3,
          "museum_tour": {
            "type": "Feature",
            "geometry": {
              "coordinates": [ [ 4.401384, 51.220292 ], [ 4.405819, 51.221758 ], [ 4.4052, 51.2229 ] ],
              "type": "LineString"
            },
            "properties": {
              "complete": true
            }
          }
        },
        {
          "key": "Paris",
          "doc_count": 2,
          "museum_tour": {
            "type": "Feature",
            "geometry": {
              "coordinates": [ [ 2.336389, 48.861111 ], [ 2.327, 48.86 ] ],
              "type": "LineString"
            },
            "properties": {
              "complete": true
            }
          }
        }
      ]
    }
  }
}

Эти результаты содержат массив корзин, где каждая корзина — это JSON-объект с key, показывающим имя поля city, и результатом внутренней агрегации, называемой museum_tour, содержащим GeoJSON Feature, описывающим фактический маршрут между различными достопримечательностями в этом городе. Каждый результат также включает объект properties со значением complete, которое будет false, если геометрия была усечена до пределов, указанных в параметре size. Обратите внимание, что при использовании time_series в следующем примере мы получим те же результаты, но с немного другой структурой.

Группировка с временными рядами

Данная функция находится в техническом предварительном просмотре и может быть изменена или удалена в будущих выпусках. Elastic постарается исправить любые проблемы, но функции в техническом предварительном просмотре не подпадают под SLA поддержки официальных функций GA.

Используя те же данные, что и раньше, мы также можем выполнить группировку с помощью time_series агрегации. Это сгруппирует по TSID, который определяется как сочетание всех полей с time_series_dimension: true, в данном случае то же поле city, используемое в предыдущей агрегации терминов. Этот пример будет работать только в том случае, если мы определили индекс tour как индекс временных рядов с помощью index.mode="time_series".

resp = client.search(
    index="tour",
    filter_path="aggregations",
    aggregations={
        "path": {
            "time_series": {},
            "aggregations": {
                "museum_tour": {
                    "geo_line": {
                        "point": {
                            "field": "location"
                        }
                    }
                }
            }
        }
    },
)
print(resp)
const response = await client.search({
  index: "tour",
  filter_path: "aggregations",
  aggregations: {
    path: {
      time_series: {},
      aggregations: {
        museum_tour: {
          geo_line: {
            point: {
              field: "location",
            },
          },
        },
      },
    },
  },
});
console.log(response);
POST /tour/_search?filter_path=aggregations
{
  "aggregations": {
    "path": {
      "time_series": {},
      "aggregations": {
        "museum_tour": {
          "geo_line": {
            "point": {"field": "location"}
          }
        }
      }
    }
  }
}

Агрегация geo_line больше не требует поля sort при вложении в time_series агрегацию. Это потому, что поле сортировки установлено на @timestamp, по которому все индексы временных рядов предварительно отсортированы. Если вы установите этот параметр и зададите ему значение, отличное от @timestamp, вы получите ошибку.

Этот запрос вернёт:

{
  "aggregations": {
    "path": {
      "buckets": {
        "{city=Paris}": {
          "key": {
            "city": "Paris"
          },
          "doc_count": 2,
          "museum_tour": {
            "type": "Feature",
            "geometry": {
              "coordinates": [ [ 2.336389, 48.861111 ], [ 2.327, 48.86 ] ],
              "type": "LineString"
            },
            "properties": {
              "complete": true
            }
          }
        },
        "{city=Antwerp}": {
          "key": {
            "city": "Antwerp"
          },
          "doc_count": 3,
          "museum_tour": {
            "type": "Feature",
            "geometry": {
              "coordinates": [ [ 4.401384, 51.220292 ], [ 4.405819, 51.221758 ], [ 4.4052, 51.2229 ] ],
              "type": "LineString"
            },
            "properties": {
              "complete": true
            }
          }
        },
        "{city=Amsterdam}": {
          "key": {
            "city": "Amsterdam"
          },
          "doc_count": 5,
          "museum_tour": {
            "type": "Feature",
            "geometry": {
              "coordinates": [ [ 4.889187, 52.373184 ], [ 4.885057, 52.370159 ], [ 4.901618, 52.369219 ], [ 4.91235, 52.374081 ], [ 4.914722, 52.371667 ] ],
              "type": "LineString"
            },
            "properties": {
              "complete": true
            }
          }
        }
      }
    }
  }
}

Эти результаты в основном такие же, как и в предыдущем примере агрегации terms, но с другой структурой. Здесь мы видим, что возвращаемые корзины представляют собой карту, где ключом является внутреннее описание TSID. Этот TSID уникален для каждой уникальной комбинации полей с time_series_dimension: true. Каждая корзина содержит поле key, которое также является картой всех значений измерений для TSID, в данном случае для группировки используется только имя города. Кроме того, есть результат внутренней агрегации, называемый museum_tour, содержащий GeoJSON Feature, описывающий фактический маршрут между различными достопримечательностями в данном городе. Каждый результат также содержит объект properties со значением complete, которое будет ложным, если геометрия была упрощена до пределов, указанных в параметре size.

Почему группировать временные ряды?

При рассмотрении этих примеров вы можете подумать, что между использованием terms и time_series для группировки гео-линий мало различий. Однако существуют важные различия в поведении в этих двух случаях. Индексы временных рядов хранятся в очень определенном порядке на диске. Они предварительно сгруппированы по полям измерения временного ряда и предварительно отсортированы по полю @timestamp. Это позволяет значительно оптимизировать агрегацию geo_line:

  • Та же память, выделенная для первого ведра, может многократно использоваться для всех последующих ведер. Это значительно меньше памяти, чем требуется для случаев, не относящихся к временным рядам, где все ведра собираются одновременно.
  • Не требуется сортировка, так как данные предварительно отсортированы по полю @timestamp. Данные временного ряда естественным образом поступят в коллектор агрегации в порядке DESC. Это означает, что если мы укажем sort_order:ASC (по умолчанию), то мы всё равно собираем данные в порядке DESC, но выполняем эффективную обратную сортировку в памяти перед генерацией окончательной геометрии LineString.
  • Параметр size может использоваться для алгоритма потоковой упрощения линий. Без временных рядов мы вынуждены обрезать данные по умолчанию после 10000 документов на ведро, чтобы предотвратить неограниченный рост использования памяти. Это может привести к обрезанию гео-линий и, следовательно, к потере важных данных. С временными рядами мы можем использовать алгоритм потоковой упрощения линий, сохраняя контроль над использованием памяти и одновременно сохраняя общую форму геометрии. На самом деле, для большинства случаев использования было бы целесообразно установить этот параметр size на значительно меньшее значение и сэкономить ещё больше памяти. Например, если geo_line должен отображаться на карте с определённым разрешением, может быть достаточно упростить до 100 или 200 точек. Это позволит сэкономить память на сервере, в сети и на клиенте.

Примечание: существуют и другие значительные преимущества работы с данными временных рядов и использованием режима индексации time_series. Они обсуждаются в документации по потокам данных временных рядов.

Потоковое упрощение линий

Упрощение линий — отличный способ уменьшить размер конечных результатов, отправляемых клиенту и отображаемых в пользовательском интерфейсе карты. Однако обычно эти алгоритмы используют много памяти для выполнения упрощения, требуя сохранения всей геометрии в памяти вместе с вспомогательными данными для самого упрощения. Использование алгоритма потокового упрощения линий позволяет минимизировать использование памяти во время процесса упрощения, ограничив память границами, определёнными для упрощённой геометрии. Это возможно только в том случае, если сортировка не требуется, что является случаем, когда группировка выполняется с помощью time_series агрегации, работающей над индексом с режимом индексации time_series.

В этих условиях агрегация geo_line выделяет память на величину, указанную в size, и затем заполняет эту память поступающими документами. После того, как память полностью заполнится, документы из внутри линии удаляются по мере добавления новых документов. Выбор удаляемого документа производится таким образом, чтобы минимизировать визуальное воздействие на геометрию. Этот процесс использует алгоритм Висвалингам—Уайатт. По существу, это означает, что точки удаляются, если у них минимальная площадь треугольника, причём треугольник определяется точкой, рассматриваемой в текущий момент, и двумя точками до и после неё на линии. Кроме того, мы рассчитываем площадь, используя сферические координаты, чтобы никакие плоские искажения не повлияли на выбор.

Чтобы продемонстрировать, насколько лучше упрощение линий по сравнению с усечением, рассмотрим этот пример северного побережья острова Кодьяк. Данные содержат всего 209 точек, но если мы хотим установить size на 100, то мы получаем значительное усечение.

North short of Kodiak Island truncated to 100 points

Серые линия — это вся геометрия из 209 точек, а синяя линия — это первые 100 точек, что сильно отличается от исходной геометрии.

Теперь рассмотрим ту же геометрию, упрощённую до 100 точек.

North short of Kodiak Island simplified to 100 points

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

© 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-metrics-geo-line.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API