ArangoDB - AQL 示例查询


在本章中,我们将考虑一些针对演员和电影数据库的 AQL 示例查询。这些查询基于图表。

问题

给定演员集合和电影集合,以及用于连接顶点的 actIn 边集合(具有年份属性),如下所示 -

[演员] <- 出演 -> [电影]

我们如何得到 -

  • 所有出演“电影1”或“电影2”的演员?
  • 所有出演过“电影1”和“电影2”的演员?
  • “演员1”和“演员2”之间所有常见的电影?
  • 所有出演过 3 部或以上电影的演员?
  • 所有电影都有 6 名演员出演?
  • 电影的演员人数是多少?
  • 演员出演的电影有多少部?
  • 演员在 2005 年至 2010 年间出演了多少部电影?

解决方案

在解决并获得上述查询的答案的过程中,我们将使用 Arangosh 创建数据集并对其运行查询。所有 AQL 查询都是字符串,可以简单地复制到您最喜欢的驱动程序而不是 Arangosh。

让我们首先在 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。该文件包含用于在数据库中创建数据集的 Arangosh 命令。我们将使用Arangosh 上的--javascript.execute选项以非交互方式执行多个命令,而不是逐一复制和粘贴命令。将其视为救星命令!

现在在 shell 上执行以下命令 -

$ arangosh --javascript.execute dataset.js

外壳命令

出现提示时提供密码,如上面的屏幕截图所示。现在我们已经保存了数据,因此我们将构建 AQL 查询来回答本章开头提出的具体问题。

第一个问题

让我们回答第一个问题:所有出演过“movie1”或“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"
]

第一个问题

现在我们继续形成两个 NEIGHBORS 查询的 UNION_DISTINCT,这将是解决方案 -

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"
]

第一个问题2

第二个问题

现在让我们考虑第二个问题:同时出演“movie1”和“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"
]

第二个问题

第三个问题

现在让我们考虑第三个问题:“actor1”和“actor2”之间所有共同的电影。这实际上与 movie1 和 movie2 中关于共同演员的问题相同。我们只需要更改起始顶点即可。举个例子,让我们找到雨果·维文(“Hugo”)和基努·里维斯共同主演的所有电影 -

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"
]

第三个问题

第四个问题

现在我们来考虑第四个问题。所有出演过 3 部或以上电影的演员。这个问题是不同的;我们不能在这里使用邻居功能。相反,我们将利用 AQL 的边缘索引和 COLLECT 语句进行分组。基本思想是按起始顶点(在该数据集中始终是参与者)对所有边进行分组。然后我们从结果中删除所有电影少于 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
   }
]

第四个问题

对于剩下的问题,我们将讨论查询的形成,并仅提供查询。读者应该在 Arangosh 终端上自行运行查询。

第五个问题

现在让我们考虑第五个问题:所有由 6 名演员出演的电影。与之前的查询中的想法相同,但使用了相等过滤器。然而,现在我们需要电影而不是演员,所以我们返回_to 属性-

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

电影的演员人数是多少?

我们记得在数据集中,边缘上的_to对应于电影,因此我们计算相同_to出现的频率。这是演员的数量。该查询与之前的查询几乎相同,但在 COLLECT 之后没有 FILTER -

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

第六个问题

现在让我们考虑第六个问题:演员的电影数量

我们找到上述查询的解决方案的方式也将帮助您找到此查询的解决方案。

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