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()