ArangoDB - Примеры запросов AQL

В этой главе мы рассмотрим несколько примеров запросов AQL на Actors and MoviesБаза данных. Эти запросы основаны на графиках.

Проблема

Учитывая коллекцию актеров и коллекцию фильмов, а также коллекцию ребер actIn (со свойством года) для соединения вершины, как показано ниже -

[Actor] <- act in -> [Movie]

Как мы получаем -

  • Все актеры, которые играли в "movie1" ИЛИ "movie2"?
  • Все актеры, которые играли как в "movie1", так и "movie2"?
  • Все общие фильмы между «актер1» и «актер2»?
  • Все актеры, сыгравшие в 3-х и более фильмах?
  • Все фильмы, в которых снялось ровно 6 актеров?
  • Количество актеров по фильму?
  • Количество фильмов по актеру?
  • Сколько фильмов снял актер с 2005 по 2010 год?

Решение

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

Давайте начнем с создания тестового набора данных в Арангоше. Сначала скачайте этот файл -

# wget -O dataset.js
https://drive.google.com/file/d/0B4WLtBDZu_QWMWZYZ3pYMEdqajA/view?usp=sharing

Выход

...
HTTP request sent, awaiting response... 200 OK
Length: unspecified [text/html]
Saving to: ‘dataset.js’
dataset.js [ <=> ] 115.14K --.-KB/s in 0.01s
2017-09-17 14:19:12 (11.1 MB/s) - ‘dataset.js’ saved [117907]

Вы можете видеть в выводе выше, что мы загрузили файл JavaScript. dataset.js.Этот файл содержит команды Арангоша для создания набора данных в базе данных. Вместо того, чтобы копировать и вставлять команды одну за другой, мы будем использовать--javascript.executeопция Арангоша для выполнения нескольких команд в неинтерактивном режиме. Считайте это командой спасателя!

Теперь выполните следующую команду в оболочке -

$ arangosh --javascript.execute dataset.js

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

Первый вопрос

Давайте возьмем первый вопрос: All actors who acted in "movie1" OR "movie2". Предположим, мы хотим найти имена всех актеров, которые действовали в "TheMatrix" ИЛИ "TheDevilsAdvocate" -

Мы начнем с одного фильма за раз, чтобы узнать имена актеров -

127.0.0.1:8529@_system> db._query("FOR x IN ANY 'movies/TheMatrix' actsIn
OPTIONS {bfs: true, uniqueVertices: 'global'} RETURN x._id").toArray();

Выход

Мы получим следующий вывод -

[
   "actors/Hugo",
   "actors/Emil",
   "actors/Carrie",
   "actors/Keanu",
   "actors/Laurence"
]

Теперь мы продолжаем формировать UNION_DISTINCT из двух запросов NEIGHBORS, которые будут решением -

127.0.0.1:8529@_system> db._query("FOR x IN UNION_DISTINCT ((FOR y IN ANY
'movies/TheMatrix' actsIn OPTIONS {bfs: true, uniqueVertices: 'global'} RETURN
y._id), (FOR y IN ANY 'movies/TheDevilsAdvocate' actsIn OPTIONS {bfs: true,
uniqueVertices: 'global'} RETURN y._id)) RETURN x").toArray();

Выход

[
   "actors/Charlize",
   "actors/Al",
   "actors/Laurence",
   "actors/Keanu",
   "actors/Carrie",
   "actors/Emil",
   "actors/Hugo"
]

Второй вопрос

Давайте теперь рассмотрим второй вопрос: All actors who acted in both "movie1" AND "movie2". Это почти идентично поставленному выше вопросу. Но на этот раз нас интересует не СОЮЗ, а ПЕРЕСЕЧЕНИЕ -

127.0.0.1:8529@_system> db._query("FOR x IN INTERSECTION ((FOR y IN ANY
'movies/TheMatrix' actsIn OPTIONS {bfs: true, uniqueVertices: 'global'} RETURN
y._id), (FOR y IN ANY 'movies/TheDevilsAdvocate' actsIn OPTIONS {bfs: true,
uniqueVertices: 'global'} RETURN y._id)) RETURN x").toArray();

Выход

Мы получим следующий вывод -

[
   "actors/Keanu"
]

Третий вопрос

Давайте теперь рассмотрим третий вопрос: All common movies between "actor1" and "actor2". На самом деле это идентично вопросу об общих актерах в movie1 и movie2. Нам просто нужно изменить стартовые вершины. В качестве примера давайте найдем все фильмы, в которых снимаются Хьюго Уивинг («Хьюго») и Киану Ривз:

127.0.0.1:8529@_system> db._query(
   "FOR x IN INTERSECTION (
      (
         FOR y IN ANY 'actors/Hugo' actsIn OPTIONS 
         {bfs: true, uniqueVertices: 'global'}
          RETURN y._id
      ),
      
      (
         FOR y IN ANY 'actors/Keanu' actsIn OPTIONS 
         {bfs: true, uniqueVertices:'global'} RETURN y._id
      )
   ) 
   RETURN x").toArray();

Выход

Мы получим следующий вывод -

[
   "movies/TheMatrixReloaded",
   "movies/TheMatrixRevolutions",
   "movies/TheMatrix"
]

Четвертый вопрос

Давайте теперь рассмотрим четвертый вопрос. All actors who acted in 3 or more movies. Это другой вопрос; мы не можем использовать функцию соседей здесь. Вместо этого мы будем использовать edge-index и оператор COLLECT AQL для группировки. Основная идея - сгруппировать все ребра по ихstartVertex(который в этом наборе данных всегда является актером). Затем мы удаляем из результата всех актеров с менее чем 3 фильмами, так как здесь мы включили количество фильмов, в которых снимался актер -

127.0.0.1:8529@_system> db._query("FOR x IN actsIn COLLECT actor = x._from WITH
COUNT INTO counter FILTER counter >= 3 RETURN {actor: actor, movies:
counter}"). toArray()

Выход

[
   {
      "actor" : "actors/Carrie",
      "movies" : 3
   },
   
   {
      "actor" : "actors/CubaG",
      "movies" : 4
   },

   {
      "actor" : "actors/Hugo",
      "movies" : 3
   },

   {
      "actor" : "actors/Keanu",
      "movies" : 4
   },

   {
      "actor" : "actors/Laurence",
      "movies" : 3
   },

   {
      "actor" : "actors/MegR",
      "movies" : 5
   },

   {
      "actor" : "actors/TomC",
      "movies" : 3
   },
   
   {
      "actor" : "actors/TomH",
      "movies" : 3
   }
]

По оставшимся вопросам мы обсудим формирование запроса и предоставим только запросы. Читателю следует самостоятельно запустить запрос на терминале Арангоша.

Пятый вопрос

Давайте теперь рассмотрим пятый вопрос: All movies where exactly 6 actors acted in. Та же идея, что и в предыдущем запросе, но с фильтром равенства. Однако теперь нам нужен фильм вместо актера, поэтому мы возвращаем_to attribute -

db._query("FOR x IN actsIn COLLECT movie = x._to WITH COUNT INTO counter FILTER
counter == 6 RETURN movie").toArray()

Количество актеров по фильму?

Мы помним в нашем наборе данных _to по краю соответствует фильму, поэтому считаем, как часто _toпоявляется. Это количество актеров. Запрос почти идентичен предыдущим, ноwithout the FILTER after COLLECT -

db._query("FOR x IN actsIn COLLECT movie = x._to WITH COUNT INTO counter RETURN
{movie: movie, actors: counter}").toArray()

Шестой вопрос

Давайте теперь рассмотрим шестой вопрос: The number of movies by an actor.

То, как мы нашли решения для наших вышеуказанных запросов, поможет вам найти решение и для этого запроса.

db._query("FOR x IN actsIn COLLECT actor = x._from WITH COUNT INTO counter
RETURN {actor: actor, movies: counter}").toArray()