Spec-Zone.ru › Elasticsearch 7
›Elasticsearch Guide [7.17] ›Агрегации ›Агрегации по корзинам

Агрегация по уникальным значениям

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

Пример:

GET /_search
{
  "aggs": {
    "genres": {
      "terms": { "field": "genre" }
    }
  }
}

Ответ:

{
  ...
  "aggregations": {
    "genres": {
      "doc_count_error_upper_bound": 0,   
      "sum_other_doc_count": 0,           
      "buckets": [                        
        {
          "key": "electronic",
          "doc_count": 6
        },
        {
          "key": "rock",
          "doc_count": 3
        },
        {
          "key": "jazz",
          "doc_count": 2
        }
      ]
    }
  }
}

верхняя граница ошибки подсчета документов для каждого значения, см. ниже

если есть много уникальных значений, Elasticsearch возвращает только самые популярные; это число равно сумме подсчетов документов для всех корзин, которые не входят в ответ

список самых популярных корзин, значение top определяется параметром сортировки

Поле field может быть строковым, числовым, IP, логическим или двоичным.

По умолчанию, агрегация terms не может быть применена к полю text. Используйте подполе keyword multi-field вместо этого. Также можно включить fielddata для поля text, чтобы создавать корзины для обработанных значений поля. Включение fielddata может значительно увеличить потребление памяти.

Размер

По умолчанию, агрегация terms возвращает 10 самых популярных значений. Используйте параметр size, чтобы вернуть больше значений, до ограничения search.max_buckets.

Если в данных содержится 100 или 1000 уникальных значений, можно увеличить size агрегации terms, чтобы вернуть их все. Если у вас больше уникальных значений и вам нужны все, используйте агрегацию composite вместо этого.

Более высокие значения size требуют больше памяти для вычислений и приближаются к пределу max_buckets. Вы поймёте, что значение слишком велико, если запрос завершится сообщением об ошибке max_buckets.

Размер фрагмента

Для получения более точных результатов, агрегация terms извлекает больше чем только самые популярные size значения с каждого фрагмента. Она извлекает самые популярные shard_size значений, по умолчанию size * 1.5 + 10.

Это необходимо для обработки случаев, когда одно значение имеет много документов на одном фрагменте, но находится ниже порога size на других фрагментах. Если каждый фрагмент возвращал только size значений, агрегация возвращала бы неполный подсчет документов для значения. Поэтому terms возвращает больше значений, пытаясь найти пропущенные. Это помогает, но все еще возможно получить неполный подсчет документов для значения. Это зависит от того, насколько значения распределены по фрагментам.

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

Это руководство относится только к агрегации terms по умолчанию сортировке order. Если вы сортируете по другим критериям, кроме количества документов в порядке убывания, см. Порядок.

shard_size не может быть меньше size (так как это не имеет смысла). Если это так, Elasticsearch переопределит его и установит равным size.

Ошибка подсчета документов

Даже с большим значением shard_size, значения doc_count для агрегации terms могут быть приблизительными. В результате любые вложенные агрегации на агрегации terms также могут быть приблизительными.

sum_other_doc_count — это количество документов, которые не вошли в топ size значений. Если это больше, чем 0, можно быть уверенным, что агрегация terms пришлось отбросить некоторые корзины, либо потому, что они не поместились в size на координационном узле, либо потому, что они не поместились в shard_size на узле данных.

Ошибка подсчета документов на уровне корзины

Если вы установите параметр show_term_doc_count_error в значение true, агрегация terms будет включать doc_count_error_upper_bound, которое является верхней границей ошибки в doc_count, возвращенные каждым фрагментом. Это сумма размера самой большой корзины на каждом фрагменте, которая не поместилась в shard_size.

Представьте, что есть одна корзина, очень большая на одном фрагменте и немного за пределами shard_size на всех остальных фрагментах. В этом случае агрегация terms вернет корзину, потому что она большая, но пропустит данные из многих документов на фрагментах, где значение не достигло порога shard_size. doc_count_error_upper_bound — это максимальное количество этих пропущенных документов.

GET /_search
{
  "aggs": {
    "products": {
      "terms": {
        "field": "product",
        "size": 5,
        "show_term_doc_count_error": true
      }
    }
  }
}

Эти ошибки могут быть вычислены только таким образом, когда значения сортируются по убыванию количества документов. Когда агрегация сортируется по значениям самих значений (в порядке возрастания или убывания), ошибка в подсчете документов отсутствует, так как если фрагмент не возвращает определенное значение, присутствующее в результатах с другого фрагмента, это значит, что этого значения нет в его индексе. Если агрегация отсортирована по под-агрегации или по возрастанию количества документов, ошибка в подсчете документов не может быть определена и получает значение -1, чтобы указать это.

Сортировка

По умолчанию агрегация terms сортирует термины по убыванию количества документов _count. Используйте параметр order для указания другого порядка сортировки.

Избегайте использования "order": { "_count": "asc" }. Если вам нужно найти редкие термины, используйте агрегацию rare_terms вместо неё. Из-за способа получения терминов агрегацией terms из фрагментов, сортировка по возрастанию количества документов часто приводит к неточным результатам.

Если вам действительно нужно, вот как отсортировать по возрастанию количества документов:

GET /_search
{
  "aggs": {
    "genres": {
      "terms": {
        "field": "genre",
        "order": { "_count": "asc" }
      }
    }
  }
}

Сортировка корзинок в алфавитном порядке по их терминам в порядке возрастания:

GET /_search
{
  "aggs": {
    "genres": {
      "terms": {
        "field": "genre",
        "order": { "_key": "asc" }
      }
    }
  }
}

Протестируйте все сортировки на под-агрегациях перед использованием в производстве. Сортировка по под-агрегации может привести к ошибкам или неточным результатам. Например, из-за способа получения результатов агрегацией terms из фрагментов, сортировка по под-агрегации max в возрастающем порядке часто приводит к неточным результатам. Однако сортировка по под-агрегации max в убывающем порядке, как правило, безопасна.

Сортировка корзинок по под-агрегациям метрик с одним значением (определённым по имени агрегации):

GET /_search
{
  "aggs": {
    "genres": {
      "terms": {
        "field": "genre",
        "order": { "max_play_count": "desc" }
      },
      "aggs": {
        "max_play_count": { "max": { "field": "play_count" } }
      }
    }
  }
}

Сортировка корзинок по под-агрегациям метрик с несколькими значениями (определённым по имени агрегации):

GET /_search
{
  "aggs": {
    "genres": {
      "terms": {
        "field": "genre",
        "order": { "playback_stats.max": "desc" }
      },
      "aggs": {
        "playback_stats": { "stats": { "field": "play_count" } }
      }
    }
  }
}

Агрегации-потоки не могут быть использованы для сортировки

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

Также можно сортировать корзинки на основе «более глубокой» агрегации в иерархии. Это поддерживается, если пути к агрегациям являются агрегациями с единственной корзинкой, где последняя агрегация в пути может быть либо агрегацией с единственной корзинкой, либо метрической. Если это агрегация с единственной корзинкой, порядок определяется количеством документов в корзине (т.е. doc_count), если это метрическая, применяются те же правила, что и выше (где путь должен указывать имя метрики для сортировки в случае многозначной метрики, а в случае однозначной метрики сортировка будет применена к этому значению).

Путь должен быть определён в следующем формате:

AGG_SEPARATOR       =  '>' ;
METRIC_SEPARATOR    =  '.' ;
AGG_NAME            =  <the name of the aggregation> ;
METRIC              =  <the name of the metric (in case of multi-value metrics aggregation)> ;
PATH                =  <AGG_NAME> [ <AGG_SEPARATOR>, <AGG_NAME> ]* [ <METRIC_SEPARATOR>, <METRIC> ] ;
GET /_search
{
  "aggs": {
    "countries": {
      "terms": {
        "field": "artist.country",
        "order": { "rock>playback_stats.avg": "desc" }
      },
      "aggs": {
        "rock": {
          "filter": { "term": { "genre": "rock" } },
          "aggs": {
            "playback_stats": { "stats": { "field": "play_count" } }
          }
        }
      }
    }
  }
}

Вышеупомянутое отсортирует корзинки стран артистов на основе среднего количества воспроизведений среди песен в стиле рок.

Для сортировки по нескольким критериям можно использовать массив критериев сортировки, как в следующем примере:

GET /_search
{
  "aggs": {
    "countries": {
      "terms": {
        "field": "artist.country",
        "order": [ { "rock>playback_stats.avg": "desc" }, { "_count": "desc" } ]
      },
      "aggs": {
        "rock": {
          "filter": { "term": { "genre": "rock" } },
          "aggs": {
            "playback_stats": { "stats": { "field": "play_count" } }
          }
        }
      }
    }
  }
}

Вышеупомянутое отсортирует корзинки стран артистов на основе среднего количества воспроизведений среди песен в стиле рок, а затем по их doc_count в порядке убывания.

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

Минимальное количество документов

Возможна фильтрация терминов, которые соответствуют большему, чем заданное, количеству вхождений, с помощью параметра min_doc_count:

GET /_search
{
  "aggs": {
    "tags": {
      "terms": {
        "field": "tags",
        "min_doc_count": 10
      }
    }
  }
}

Вышеприведённая агрегация вернёт те метки, которые были найдены в 10 и более вхождениях. Значение по умолчанию — 1.

Термины собираются и сортируются на уровне фрагмента и объединяются с терминами, собранными из других фрагментов на втором этапе. Однако фрагмент не имеет информации о глобальном количестве документов. Решение о добавлении термина в список кандидатов зависит только от вычисленного на фрагменте порядка с использованием локальных частот фрагмента. Критерий min_doc_count применяется только после объединения локальных статистик терминов всех фрагментов. Таким образом, решение о добавлении термина в качестве кандидата принимается без уверенности в том, достигнет ли термин фактически необходимого min_doc_count. Это может привести к тому, что многие (глобально) часто встречающиеся термины будут отсутствовать в окончательном результате, если список кандидатов заполнили низкочастотные термины. Чтобы этого избежать, можно увеличить параметр shard_size, чтобы разрешить больше кандидатов в фрагментах. Однако это увеличивает потребление памяти и сетевой трафик.

shard_min_doc_count

Параметр shard_min_doc_count регулирует уверенность фрагмента в том, что термин должен быть добавлен в список кандидатов или нет, относительно min_doc_count. Термины будут учитываться только в том случае, если их локальная частота в наборе выше, чем shard_min_doc_count. Если ваш словарь содержит много низкочастотных терминов, и вас не интересуют такие термины (например, опечатки), то вы можете установить параметр shard_min_doc_count для фильтрации терминов-кандидатов на уровне фрагмента, которые с достаточной уверенностью не достигнут необходимого min_doc_count даже после объединения локальных подсчётов. shard_min_doc_count по умолчанию установлено в значение 0 и не оказывает никакого влияния, если вы его не зададите явно.

Установка min_doc_count=0 также вернёт корзины для терминов, которые не соответствовали ни одному вхождению. Однако некоторые из возвращённых терминов, которые имеют счёт документов ноль, могут относиться только к удалённым документам или документам других типов, поэтому нет гарантии, что запрос match_all найдёт положительное количество документов для этих терминов.

Когда сортировка НЕ по doc_count по убыванию, высокие значения min_doc_count могут вернуть количество корзинок меньше, чем size, потому что не было собрано достаточно данных из фрагментов. Отсутствующие корзинки можно получить, увеличив shard_size. Установка shard_min_doc_count слишком высокой приведёт к фильтрации терминов на уровне фрагмента. Это значение должно быть намного меньше, чем min_doc_count/#shards.

Скрипт

Используйте поле runtime, если данные в ваших документах неточно соответствуют тому, что вам нужно агрегировать. Например, если «антологии» должны быть в специальной категории, вы можете выполнить это:

GET /_search
{
  "size": 0,
  "runtime_mappings": {
    "normalized_genre": {
      "type": "keyword",
      "script": """
        String genre = doc['genre'].value;
        if (doc['product'].value.startsWith('Anthology')) {
          emit(genre + ' anthology');
        } else {
          emit(genre);
        }
      """
    }
  },
  "aggs": {
    "genres": {
      "terms": {
        "field": "normalized_genre"
      }
    }
  }
}

Что будет выглядеть так:

{
  "aggregations": {
    "genres": {
      "doc_count_error_upper_bound": 0,
      "sum_other_doc_count": 0,
      "buckets": [
        {
          "key": "electronic",
          "doc_count": 4
        },
        {
          "key": "rock",
          "doc_count": 3
        },
        {
          "key": "electronic anthology",
          "doc_count": 2
        },
        {
          "key": "jazz",
          "doc_count": 2
        }
      ]
    }
  },
  ...
}

Это немного медленнее, потому что поле runtime должно обращаться к двум полям вместо одного и потому что некоторые оптимизации, работающие с полями keyword без runtime, не работают с полями keyword runtime. Если вам нужна скорость, вы можете индексировать поле normalized_genre.

Фильтрация значений

Возможна фильтрация значений, для которых будут созданы корзины. Это можно сделать с помощью параметров include и exclude, которые основаны на строках регулярных выражений или массивах точных значений. Кроме того, клаузы include могут фильтровать с использованием выражений partition.

Фильтрация значений с помощью регулярных выражений

GET /_search
{
  "aggs": {
    "tags": {
      "terms": {
        "field": "tags",
        "include": ".*sport.*",
        "exclude": "water_.*"
      }
    }
  }
}

В приведённом выше примере корзины будут созданы для всех тегов, содержащих слово sport, за исключением тегов, начинающихся со слова water_ (следовательно, тег water_sports не будет агрегирован). Регулярное выражение include определит, какие значения можно агрегировать, а exclude — какие значения не следует агрегировать. При определении обоих параметров, параметр exclude имеет приоритет, то есть, параметр exclude оценивается первым, а затем exclude.

Синтаксис аналогичен запросам регулярных выражений.

Фильтрация значений с точными значениями

Для соответствия по точным значениям параметры include и exclude могут просто принимать массив строк, представляющих термины так, как они найдены в индексе:

GET /_search
{
  "aggs": {
    "JapaneseCars": {
      "terms": {
        "field": "make",
        "include": [ "mazda", "honda" ]
      }
    },
    "ActiveCarManufacturers": {
      "terms": {
        "field": "make",
        "exclude": [ "rover", "jensen" ]
      }
    }
  }
}

Фильтрация значений с помощью разделов

Иногда для обработки слишком большого количества уникальных терминов в одной паре запрос/ответ необходимо разбить анализ на несколько запросов. Это можно сделать, разделив значения поля на несколько разделов во время запроса и обработав только один раздел в каждом запросе. Рассмотрим этот запрос, который ищет учётные записи, которые недавно не регистрировали доступ:

GET /_search
{
   "size": 0,
   "aggs": {
      "expired_sessions": {
         "terms": {
            "field": "account_id",
            "include": {
               "partition": 0,
               "num_partitions": 20
            },
            "size": 10000,
            "order": {
               "last_access": "asc"
            }
         },
         "aggs": {
            "last_access": {
               "max": {
                  "field": "access_date"
               }
            }
         }
      }
   }
}

Этот запрос находит последнюю дату входа в систему для подмножества учётных записей клиентов, потому что мы можем захотеть аннулировать некоторые учётные записи клиентов, которых давно не видели. Параметр num_partitions запросил, чтобы уникальные идентификаторы учётных записей были равномерно распределены по двадцати разделам (с 0 до 19). Параметр partition в этом запросе фильтрует, чтобы учитывать только идентификаторы учётных записей, попадающие в раздел 0. Последующие запросы должны запрашивать разделы 1, затем 2 и т. д., чтобы завершить анализ аннулирования учётных записей.

Обратите внимание, что параметр size для количества возвращаемых результатов необходимо настроить вместе с параметром num_partitions. Для данного примера аннулирования учётных записей процесс балансировки значений для size и num_partitions будет следующим:

  1. Используйте агрегацию cardinality для оценки общего количества уникальных значений идентификаторов учётных записей.
  2. Выберите значение для num_partitions, чтобы разбить число из 1) на более управляемые части.
  3. Выберите значение size для количества ответов, которые мы хотим получить от каждого раздела.
  4. Выполните тестовый запрос.

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

  • увеличить параметр size, чтобы вернуть больше результатов на каждый раздел (это может сильно нагрузить память), или
  • увеличить параметр num_partitions, чтобы учитывать меньше учётных записей в каждом запросе (это может увеличить общее время обработки, так как нам нужно сделать больше запросов).

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

Разделы не могут использоваться совместно с параметром exclude.

Агрегирование терминов по нескольким полям

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

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

Скрипт
Используйте скрипт для извлечения терминов из нескольких полей. Это отключает оптимизацию глобальных порядковых номеров и будет медленнее, чем сбор терминов из одного поля, но предоставляет гибкость для реализации этого варианта во время поиска.
Поле copy_to
Если вам заранее известно, что вы хотите собрать термины из двух или более полей, используйте copy_to в вашей схеме, чтобы создать новое специализированное поле во время индексирования, содержащее значения из обоих полей. Вы можете агрегировать по этому единственному полю, что позволит воспользоваться оптимизацией глобальных порядковых номеров.
Агрегирование multi_terms
Используйте агрегирование multi_terms для объединения терминов из нескольких полей в составной ключ. Это также отключает глобальные порядковые номера и будет медленнее, чем сбор терминов из одного поля. Оно быстрее, но менее гибко, чем использование скрипта.

Режим сбора

Отложенный расчет дочерних агрегаций

Для полей с большим количеством уникальных терминов и небольшим количеством требуемых результатов может быть эффективнее отложить расчет дочерних агрегаций до тех пор, пока верхние агрегации на родительском уровне не будут пропущены. Обычно все ветви дерева агрегаций расширяются одним проходом поиска в глубину, и только потом происходит пропуск. В некоторых сценариях это может быть очень затратно и может привести к ограничениям памяти. Примером проблемной ситуации является запрос в базе данных фильмов для 10 самых популярных актёров и их 5 самых частых партнёров по съёмкам:

GET /_search
{
  "aggs": {
    "actors": {
      "terms": {
        "field": "actors",
        "size": 10
      },
      "aggs": {
        "costars": {
          "terms": {
            "field": "actors",
            "size": 5
          }
        }
      }
    }
  }
}

Несмотря на то, что количество актёров может быть относительно небольшим, и мы хотим только 50 результатов, происходит комбинаторный взрыв корзин во время вычисления — один актёр может создать n² корзин, где n — количество актёров. Более разумный вариант — сначала определить 10 самых популярных актёров, а затем рассмотреть 5 самых частых партнёров по съёмкам для этих 10 актёров. Этот альтернативный подход мы называем режимом сбора breadth_first по сравнению с режимом depth_first.

breadth_first — это режим по умолчанию для полей с кардинальностью, большей, чем запрашиваемый размер, или когда кардинальность неизвестна (например, числовые поля или скрипты). Можно переопределить стандартную эвристику и напрямую указать режим сбора в запросе:

GET /_search
{
  "aggs": {
    "actors": {
      "terms": {
        "field": "actors",
        "size": 10,
        "collect_mode": "breadth_first" 
      },
      "aggs": {
        "costars": {
          "terms": {
            "field": "actors",
            "size": 5
          }
        }
      }
    }
  }
}

возможные значения — breadth_first и depth_first

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

Вложенные агрегации, такие как top_hits, которые требуют доступа к информации о баллах в рамках агрегации, использующей режим сбора breadth_first, должны повторять запрос при втором проходе, но только для документов, принадлежащих верхним корзинам.

Подсказка выполнения

Существует несколько механизмов выполнения агрегаций по терминам:

  • путём использования значений поля непосредственно для агрегации данных по каждому ведру (map)
  • путём использования глобальных порядковых номеров поля и выделения одного ведра на каждый глобальный порядковый номер (global_ordinals)

Elasticsearch пытается использовать разумные значения по умолчанию, поэтому обычно это не требует настройки.

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

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

GET /_search
{
  "aggs": {
    "tags": {
      "terms": {
        "field": "tags",
        "execution_hint": "map" 
      }
    }
  }
}

Возможные значения — map, global_ordinals

Обратите внимание, что Elasticsearch проигнорирует эту подсказку выполнения, если она не применима, и нет гарантии обратной совместимости для этих подсказок.

Отсутствующее значение

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

GET /_search
{
  "aggs": {
    "tags": {
      "terms": {
        "field": "tags",
        "missing": "N/A" 
      }
    }
  }
}

Документы без значения в поле tags попадают в то же ведро, что и документы со значением N/A.

Смешение типов полей

При агрегации по нескольким индексам тип агрегированного поля может быть разным в разных индексах. Некоторые типы совместимы друг с другом (integer и long или float и double), но когда типы представляют собой смесь десятичных и недесятичных чисел, агрегация по терминам преобразует не десятичные числа в десятичные. Это может привести к потере точности значений ведер.

Устранение неполадок

Ошибка при попытке форматирования байтов

При выполнении агрегации по терминам (или другой агрегации, но на практике обычно по терминам) по нескольким индексам может возникнуть ошибка, начинающаяся с «Ошибка при попытке форматирования байтов…». Обычно это происходит из-за того, что два индекса не имеют одного и того же типа отображения для агрегируемого поля.

Используйте явное value_type Хотя лучше исправить отображения, вы можете обойти эту проблему, если поле не отображается в одном из индексов. Установка параметра value_type может решить эту проблему, принудительно преобразуя неотображаемое поле в правильный тип.

GET /_search
{
  "aggs": {
    "ip_addresses": {
      "terms": {
        "field": "destination_ip",
        "missing": "0.0.0.0",
        "value_type": "ip"
      }
    }
  }
}

© 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/7.17/search-aggregations-bucket-terms-aggregation.html

Spec-Zone.ru

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