COLLECT¶
Операция COLLECT
может использоваться для группировки данных по одному или нескольким критериям группировки. Она также может использоваться для получения всех отдельных значений, подсчета частоты встречаемости значений и эффективного вычисления статистических свойств.
Оператор COLLECT
удаляет все локальные переменные в текущей области видимости. После COLLECT
доступны только переменные, введенные самим COLLECT
.
Синтаксис¶
Существует несколько вариантов синтаксиса для операций COLLECT
:
COLLECT variableName = expression
COLLECT variableName = expression INTO groupsVariable
COLLECT variableName = expression INTO groupsVariable = projectionExpression
COLLECT variableName = expression INTO groupsVariable KEEP keepVariable
COLLECT variableName = expression WITH COUNT INTO countVariable
COLLECT variableName = expression AGGREGATE variableName = aggregateExpression
COLLECT variableName = expression AGGREGATE variableName = aggregateExpression INTO groupsVariable
COLLECT AGGREGATE variableName = aggregateExpression
COLLECT AGGREGATE variableName = aggregateExpression INTO groupsVariable
COLLECT WITH COUNT INTO countVariable
Все варианты могут опционально заканчиваться предложением OPTIONS { ... }
.
Синтаксис группировки¶
Первая синтаксическая форма COLLECT
группирует результат только по определенным групповым критериям, указанным в выражении. Для дальнейшей обработки результатов, полученных с помощью COLLECT
, вводится новая переменная (заданная variableName). Эта переменная содержит значение группы.
Вот пример запроса, который находит отдельные значения в u.city
и делает их доступными в переменной city
:
1 2 3 4 5 |
|
Вторая форма делает то же самое, что и первая, но дополнительно вводит переменную (заданную groupsVariable), которая содержит все элементы, попавшие в группу. Это работает следующим образом: Переменная groupsVariable - это массив, содержащий столько элементов, сколько их в группе. Каждый член этого массива представляет собой объект JSON, в котором значение каждой переменной, определенной в запросе AQL, привязано к соответствующему атрибуту. Обратите внимание, что при этом учитываются все переменные, определенные до оператора COLLECT
, но не те, которые находятся на верхнем уровне (вне любого FOR
), если только оператор COLLECT
сам не находится на верхнем уровне, в этом случае учитываются все переменные. Кроме того, обратите внимание, что оптимизатор может перемещать операторы LET
из операторов FOR
для повышения производительности.
1 2 3 4 5 6 |
|
В приведенном выше примере массив users
будет сгруппирован по атрибуту city
. В результате будет получен новый массив документов, с одним элементом для каждого отдельного значения u.city
. Элементы исходного массива (здесь: users
) для каждого города доступны в переменной groups
. Это происходит благодаря предложению INTO
.
COLLECT
также позволяет указать несколько групповых критериев. Отдельные групповые критерии могут быть разделены запятыми:
1 2 3 4 5 6 7 |
|
В приведенном выше примере массив users
сгруппирован сначала по стране, а затем по городу, и для каждой отдельной комбинации страны и города будут возвращены пользователи.
Отказ от устаревших переменных¶
Третья форма COLLECT
позволяет переписать содержимое groupsVariable с помощью произвольной projectionExpression:
1 2 3 4 5 6 7 |
|
В приведенном выше примере только projectionExpression является u.name
. Поэтому только этот атрибут копируется в groupsVariable для каждого документа. Это, вероятно, гораздо эффективнее, чем копирование всех переменных из области видимости в groupsVariable, как это произошло бы без projectionExpression.
Выражение, следующее за INTO
, также может быть использовано для произвольных вычислений:
1 2 3 4 5 6 7 8 9 10 |
|
COLLECT
также предоставляет необязательное условие KEEP
, которое можно использовать для управления тем, какие переменные будут скопированы в переменную, созданную INTO
. Если условие KEEP
не указано, все переменные из области видимости будут скопированы в качестве податрибутов в groupsVariable. Это безопасно, но может отрицательно сказаться на производительности, если в области видимости много переменных или переменные содержат большие объемы данных.
В следующем примере переменные, которые копируются в groupsVariable, ограничиваются только name
. Переменные u
и someCalculation
, также присутствующие в области видимости, не будут скопированы в groupsVariable, поскольку они не указаны в предложении KEEP
:
1 2 3 4 5 6 7 8 |
|
KEEP
действует только в сочетании с INTO
. В предложении KEEP
можно использовать только правильные имена переменных. KEEP
поддерживает указание нескольких имен переменных.
Вычисление длины группы¶
COLLECT
также предоставляет специальное предложение WITH COUNT
, которое может быть использовано для эффективного определения количества членов группы.
В простейшей форме возвращается только количество элементов, попавших в COLLECT
:
1 2 3 |
|
Вышеприведенный вариант эквивалентен, но менее эффективен, чем:
1 |
|
Предложение WITH COUNT
также можно использовать для эффективного подсчета количества элементов в каждой группе:
1 2 3 4 5 6 |
|
Предложение WITH COUNT
можно использовать только вместе с предложением INTO
.
Агрегация¶
Оператор COLLECT
можно использовать для агрегирования данных по группам. Чтобы определить только длину группы, можно использовать вариант WITH COUNT INTO
оператора COLLECT
, как описано ранее.
Для других агрегаций можно запустить агрегатные функции на результатах COLLECT
:
1 2 3 4 5 6 7 |
|
Однако вышеописанный способ требует хранения всех значений группы во время операции сбора для всех групп, что может быть неэффективным.
Специальный вариант AGGREGATE
функции COLLECT
позволяет создавать агрегатные значения постепенно во время операции сбора и поэтому часто является более эффективным.
При использовании варианта AGGREGATE
приведенный выше запрос становится:
1 2 3 4 5 6 7 8 |
|
Ключевое слово AGGREGATE
может использоваться только после ключевого слова COLLECT
. Если оно используется, то должно следовать непосредственно за объявлением ключей группировки. Если ключи группировки не используются, оно должно следовать непосредственно за ключевым словом COLLECT
:
1 2 3 4 5 6 |
|
В правой части каждого присваивания AGGREGATE
допускаются только определенные выражения:
-
на верхнем уровне агрегированное выражение должно быть вызовом одной из поддерживаемых функций агрегирования:
-
LENGTH()
/COUNT()
MIN()
MAX()
SUM()
AVERAGE()
/AVG()
STDDEV_POPULATION()
/STDDEV()
STDDEV_SAMPLE()
VARIANCE_POPULATION()
/VARIANCE()
VARIANCE_SAMPLE()
UNIQUE()
SORTED_UNIQUE()
COUNT_DISTINCT()
/COUNT_UNIQUE()
BIT_AND()
BIT_OR()
-
BIT_XOR()
-
агрегированное выражение не должно ссылаться на переменные, введенные самим
COLLECT
.
COLLECT
против RETURN DISTINCT
¶
Для того чтобы сделать набор результатов уникальным, можно использовать либо COLLECT
, либо RETURN DISTINCT
.
1 2 |
|
1 2 3 |
|
За кулисами оба варианта создают CollectNode. Однако они используют разные реализации COLLECT
, которые имеют разные свойства:
-
RETURN DISTINCT
сохраняет порядок результатов, но он ограничен одним значением. -
COLLECT
изменяет порядок результатов (отсортированный или неопределенный), но поддерживает несколько значений и является более гибким, чемRETURN DISTINCT
.
Помимо сложных возможностей группировки и агрегации, COLLECT
позволяет поместить операцию LIMIT
перед RETURN
, чтобы потенциально остановить операцию COLLECT
раньше времени.
Параметры COLLECT
¶
method
¶
Существует два варианта COLLECT
, которые оптимизатор может выбрать: вариант sorted и вариант hash. Опция method
может быть использована в операторе COLLECT
, чтобы сообщить оптимизатору о предпочтительном методе, "sorted"
или "hash"
.
1 |
|
Если метод не указан пользователем, то оптимизатор создаст план, использующий метод sorted, и дополнительный план, использующий метод hash, если оператор COLLECT
удовлетворяет его требованиям.
Если метод явно установлен в sorted, то оптимизатор всегда будет использовать sorted вариант COLLECT
и даже не создаст план с использованием hash варианта. Если он явно установлен в hash, то оптимизатор будет создавать план с использованием метода hash только если оператор COLLECT
соответствует требованиям. Не все операторы COLLECT
могут использовать метод hash, в частности, операторы с клаузулой INTO
не подходят. Если оператор COLLECT
соответствует требованиям, то будет только один план, использующий метод hash. В противном случае оптимизатор по умолчанию будет использовать метод sorted.
Метод sorted требует, чтобы его входные данные были отсортированы по групповым критериям, указанным в предложении COLLECT
. Для обеспечения корректности результата оптимизатор автоматически вставит в запрос операцию SORT
перед оператором COLLECT
. В дальнейшем оптимизатор может отказаться от этой операции SORT
, если для групповых критериев имеется отсортированный индекс.
Если оператор COLLECT
претендует на использование варианта hash, оптимизатор создаст для него дополнительный план в начале фазы планирования. В этом плане перед оператором COLLECT
не будет добавляться дополнительный оператор SORT
. Это связано с тем, что hash вариант COLLECT
не требует отсортированного ввода. Вместо этого после COLLECT
будет добавлен оператор SORT
для сортировки его вывода. Этот оператор SORT
может быть снова оптимизирован на последующих этапах.
Если порядок сортировки в COLLECT
не имеет значения для пользователя, добавление дополнительной инструкции SORT null
после COLLECT
позволит оптимизатору полностью удалить сортировку:
1 2 3 4 |
|
Какой вариант COLLECT
используется оптимизатором, если явно не задан предпочтительный метод, зависит от оценок затрат оптимизатора. Созданные планы с различными вариантами COLLECT
будут проходить через обычный конвейер оптимизации. В итоге оптимизатор, как обычно, выберет план с наименьшей оценкой общей стоимости.
В целом, сортированный вариант COLLECT
следует предпочесть в тех случаях, когда в групповых критериях присутствует сортированный индекс. В этом случае оптимизатор может исключить операцию SORT
перед COLLECT
, так что никакого SORT
не останется.
Если в критериях группы нет отсортированного индекса, то предварительная сортировка, требуемая вариантом sorted, может оказаться дорогостоящей. В этом случае, скорее всего, оптимизатор предпочтет hash вариант COLLECT
, который не требует сортировки входных данных.
Какой вариант COLLECT
будет использоваться на самом деле, можно выяснить, посмотрев на план выполнения запроса, в частности, на комментарий CollectNode:
1 2 3 4 5 6 7 8 |
|