Перейти к содержанию

Операторы массива

Расширение массива

Чтобы легко получить доступ к именованному атрибуту из всех элементов массива, AQL предлагает оператор быстрого доступа [*] для раскрытия переменных массива.

Использование оператора [*] с переменной массива будет перебирать все элементы в массиве, что позволит получить доступ к определенному атрибуту каждого элемента. Требуется, чтобы раскрываемая переменная была массивом. Результатом оператора [*] снова является массив.

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
[
  {
    "name": "john",
    "age": 35,
    "friends": [
      { "name": "tina", "age": 43 },
      { "name": "helga", "age": 52 },
      { "name": "alfred", "age": 34 }
    ]
  },
  {
    "name": "yves",
    "age": 24,
    "friends": [
      { "name": "sergei", "age": 27 },
      { "name": "tiffany", "age": 25 }
    ]
  },
  {
    "name": "sandra",
    "age": 40,
    "friends": [
      { "name": "bob", "age": 32 },
      { "name": "elena", "age": 48 }
    ]
  }
]

С оператором [*] становится легко запросить только имена друзей для каждого пользователя:

1
2
FOR u IN users
  RETURN { name: u.name, friends: u.friends[*].name }

Результат:

1
2
3
4
5
6
7
8
[
  {
    "name": "john",
    "friends": ["tina", "helga", "alfred"]
  },
  { "name": "yves", "friends": ["sergei", "tiffany"] },
  { "name": "sandra", "friends": ["bob", "elena"] }
]

Это сокращение для более длинного, семантически эквивалентного запроса:

1
2
FOR u IN users
  RETURN { name: u.name, friends: (FOR f IN u.friends RETURN f.name) }

Сжатие массива

Чтобы свернуть (или сгладить) результаты во вложенных массивах, AQL предоставляет оператор [**]. Он работает аналогично оператору [*], но дополнительно сворачивает вложенные массивы.

Количество свернутых уровней определяется количеством используемых символов звездочки. [**] сворачивает один уровень вложенности - точно так же, как это сделали бы FLATTEN(массив) или FLATTEN(массив, 1) -, [***] сворачивает два уровня - эквивалентно FLATTEN(массив, 2) - и так далее.

Сравним оператор расширения массива с оператором сжатия массива. Например, следующий запрос создает массив имен друзей для каждого пользователя:

1
2
FOR u IN users
  RETURN u.friends[*].name

Поскольку у нас несколько пользователей, общий результат представляет собой вложенный массив:

1
2
3
4
5
[
  ["tina", "helga", "alfred"],
  ["sergei", "tiffany"],
  ["bob", "elena"]
]

Если цель состоит в том, чтобы избавиться от вложенного массива, мы можем применить оператор [**] к результату. Но просто добавление [**] к запросу не поможет, потому что u.friends — это не вложенный (многомерный) массив, а простой (одномерный) массив. Тем не менее, [**] можно использовать, если у него есть доступ к многомерному вложенному результату.

Мы можем расширить приведенный выше запрос следующим образом и по-прежнему создавать тот же вложенный результат:

1
2
3
RETURN (
  FOR u IN users RETURN u.friends[*].name
)

К настоящему времени добавление оператора [**] в конце запроса...

1
2
3
RETURN (
  FOR u IN users RETURN u.friends[*].name
)[**]

... результат запроса становится:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
[
  [
    "tina",
    "helga",
    "alfred",
    "sergei",
    "tiffany",
    "bob",
    "elena"
  ]
]

Обратите внимание, что элементы не дублируются. Для плоского массива, состоящего только из уникальных элементов, рекомендуется комбинация UNIQUE() и FLATTEN().

Встроенные выражения

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

Эти встроенные выражения могут следовать за операторами расширения и сжатия массива [* ...], [** ...] и т. д. Ключевые слова FILTER, LIMIT и RETURN должны встречаться в этом порядке, если они используются в комбинации, и могут встречаться только один раз:

anyArray[* FILTER conditions LIMIT skip,limit RETURN projection]

Пример с вложенными числами и сжатием массива:

1
2
LET arr = [ [ 1, 2 ], 3, [ 4, 5 ], 6 ]
RETURN arr[** FILTER CURRENT % 2 == 0]

Все четные числа возвращаются в плоском массиве:

1
[[2, 4, 6]]

Сложный пример с несколькими условиями, лимитом и проекцией:

1
2
3
4
5
6
7
8
FOR u IN users
    RETURN {
        name: u.name,
        friends: u.friends[* FILTER CONTAINS(CURRENT.name, "a") AND CURRENT.age > 40
            LIMIT 2
            RETURN CONCAT(CURRENT.name, " is ", CURRENT.age)
        ]
    }

Для каждого пользователя возвращается не более двух вычисленных строк, основанных на друзьях с буквой a в имени и старше 40 лет:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
[
  {
    "name": "john",
    "friends": ["tina is 43", "helga is 52"]
  },
  {
    "name": "sandra",
    "friends": ["elena is 48"]
  },
  {
    "name": "yves",
    "friends": []
  }
]

Встроенный фильтр

Чтобы вернуть только имена друзей, у которых значение возраста выше, чем у самого пользователя, можно использовать встроенный FILTER:

1
2
FOR u IN users
  RETURN { name: u.name, friends: u.friends[* FILTER CURRENT.age > u.age].name }

Псевдопеременная CURRENT может использоваться для доступа к текущему элементу массива. Условие FILTER может ссылаться на CURRENT или любые переменные, допустимые во внешней области видимости.

Встроенный лимит

Количество возвращаемых элементов может быть ограничено с помощью LIMIT. Работает так же, как операция ограничения. LIMIT должен идти после FILTER и перед RETURN, если они присутствуют.

1
2
FOR u IN users
  RETURN { name: u.name, friends: u.friends[* LIMIT 1].name }

Пример выше возвращает по одному другу:

1
2
3
4
5
[
  { "name": "john", "friends": ["tina"] },
  { "name": "sandra", "friends": ["bob"] },
  { "name": "yves", "friends": ["sergei"] }
]

Ряд элементов также можно пропустить и вернуть до n:

1
2
FOR u IN users
  RETURN { name: u.name, friends: u.friends[* LIMIT 1,2].name }

Пример запроса пропускает первого друга и возвращает не более двух друзей на пользователя:

1
2
3
4
5
[
  { "name": "john", "friends": ["helga", "alfred"] },
  { "name": "sandra", "friends": ["elena"] },
  { "name": "yves", "friends": ["tiffany"] }
]

Встроенная проекция

Чтобы вернуть проекцию текущего элемента, используйте RETURN. Если также присутствует FILTER, RETURN должен появиться позже.

1
2
FOR u IN users
  RETURN u.friends[* RETURN CONCAT(CURRENT.name, " is a friend of ", u.name)]

Вышеупомянутое вернется:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
[
  [
    "tina is a friend of john",
    "helga is a friend of john",
    "alfred is a friend of john"
  ],
  [
    "sergei is a friend of yves",
    "tiffany is a friend of yves"
  ],
  [
    "bob is a friend of sandra",
    "elena is a friend of sandra"
  ]
]

Оператор вопросительного знака

Вы можете использовать [? ... ] для массивов, чтобы проверить, соответствуют ли элементы определенным критериям, и вы можете указать, как часто они должны удовлетворяться. Оператор подобен встроенному фильтру, но с дополнительной проверкой длины и оценивается как true или false.

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

1
2
LET arr = [ 1, 2, 3, 4 ]
RETURN arr[? 2 FILTER CURRENT % 2 == 0] // true

Число 2 после ? является квантором. Это необязательно и по умолчанию ANY. Поддерживаются следующие квантификаторы:

  • Целые числа для точных количеств (например, 2)
  • Диапазоны чисел для количества между двумя значениями (например, 2..3)
  • NONE (эквивалент 0)
  • ANY
  • ALL
  • AT LEAST

За квантификатором должна следовать операция FILTER, если вы хотите указать условия. Вы можете обратиться к текущему элементу массива через псевдопеременную CURRENT в выражении фильтра. Если опустить квантификатор и операцию FILTER (только arr[?]), то arr проверяется, является ли он массивом и содержит ли он хотя бы один элемент.

Оператор вопросительного знака — это сокращение для встроенного фильтра с проверкой длины окружающего пространства. В следующей таблице сравниваются оба варианта:

Оператор вопросительного знака Встроенный фильтр с проверкой длины
arr[? <number> FILTER <conditions>] LENGTH(arr[* FILTER <conditions>]) == <number>
arr[? <min>..<max> FILTER <conditions>] IN_RANGE(LENGTH(arr[* FILTER <conditions>]), <min>, <max>, true, true)
arr[? NONE FILTER <conditions>] LENGTH(arr[* FILTER <conditions>]) == 0
arr[? ANY FILTER <conditions>] LENGTH(arr[* FILTER <conditions>]) > 0
arr[? ALL FILTER <conditions>] LENGTH(arr[* FILTER <conditions>]) == LENGTH(arr)
arr[? AT LEAST (<number>) FILTER <conditions>] LENGTH(arr[* FILTER <conditions>]) >= <number>
arr[?] LENGTH(arr[*]) > 0

Комментарии