ArangoDB AQL中的图形绘制遍历

来源:互联网 发布:java 值传递 引用传递 编辑:程序博客网 时间:2024/06/13 17:24

原文:Graphs in AQL


AQL中的图形

在ArangoDB中可以使用多种方式处理图形,以及使用AQL查询图形的不同方法。

管理图形的两个选项是使用

  • ArangoDB管理一个图形中涉及的集合的命名图,或
  • 图形功能在文档和边缘集合的组合上。

可以通过图形模块 或通过Web界面定义命名图该定义包含图形的名称,以及所涉及的顶点和边缘集合。由于管理功能分层在简单的文档和边集合之上,您还可以使用常规的AQL函数来处理它们。

图形查询的AQL语言结构支持两种变体(命名图和松散耦合的集合集,也称为匿名图)。这些构造充分利用优化,因此可以预期最佳性能:

  • AQL遍历以跟随连接到起始顶点的边缘,直到可变深度。它可以与AQL过滤条件相结合。

  • AQL最短路径,找到两个给定顶点之间的顶点和边,尽可能少的跳数。

这些类型的查询仅在数据模型中使用边集合和/或图形时才有用。



在AQL中绘制遍历

一般查询想法

遍历从一个特定文档(startVertex开始,并且跟随连接到该文档的所有边。对于这些边缘所针对的所有文档(顶点),它将再次跟随所有连接到它们的边缘,依此类推。可以定义至少(最小深度)和最多(最大深度)应执行这些后续迭代中有多少次

对于在此过程中访问的所有顶点,在最小深度和最大深度迭代之间的范围内, 您将获得一组结果,其中包含三个项目:

  1. 被访问的顶点。
  2. 边缘指向它。
  3. 从startVertex到被访问顶点的完整路径,作为具有属性和属性顶点的对象,每个都是相应元素的列表。这些列表进行排序,这意味着在所述第一元件的顶点 是startVertex和上次被访问的顶点,并且在第n个元件的边缘的第n个元件与在第(n + 1)个元件连接的顶点

示例执行

让我们来看一个简单的例子来解释它是如何工作的。这是我们将要遍历的图:

遍历图

我们使用以下参数进行查询:

  1. 我们从顶点A开始
  2. 我们使用最小深度为1。
  3. 我们使用的最大深度为2。
  4. 我们只沿着边缘的OUTBOUND方向

遍历图步骤1

现在它走到A的直接邻居之一,说B(注:订购不能保证!):

遍历图步骤2

查询将记住状态(红色圆圈),并将发出第一个结果 A → B(黑盒)。这也将阻止运行器被循环捕获。现在再次访问B的直接邻居之一,说E

遍历图步骤3

我们限制了最大深度为2的查询,所以它不会选择E的任何邻居,因为从AE的路径已经需要2个步骤。相反,我们将返回一级到B,并继续与其他直接邻居:

遍历图步骤4

再之后,我们生产这样的结果,我们将回踩但是没有B的邻居,我们还没有访问过。因此,我们再走一步回到A,继续在那里的任何其他邻居。

遍历图步骤5

与我们访问H之前的迭代相同

遍历图步骤6

J

遍历图步骤7

在这些步骤之后,没有进一步的结果。所以这个查询一起返回了以下路径:

  1. A → B
  2. A → B → E
  3. A → B → C
  4. A → G
  5. A → G → H
  6. A → G → J

句法

现在我们来看看我们如何编写遵循这个架构的查询。您有两个选项,您可以使用命名图或一组边集(匿名图)。

使用命名图

FOR vertex[, edge[, path]]  IN [min[..max]]  OUTBOUND|INBOUND|ANY startVertex  GRAPH graphName  [OPTIONS options]
  • FOR:最多发射三个变量:
    • vertex(object):遍历中的当前顶点
    • edge(object,可选):遍历中的当前边
    • path(object,optional):表示当前路径与两个成员:
      • vertices:此路径上所有顶点的数组
      • edges:该路径上所有边的数组
  • IN min..max:遍历的最小和最大深度:
    • min(number,可选):此查询返回的边和顶点将以min的遍历深度开始(因此下面的边和顶点不会被返回)。如果未指定,则默认为1.最小可能值为0。
    • max(number,可选):遍历最大长度路径。如果省略,max默认为min因此,只返回min范围内的顶点和边最大不能没有指定分钟
  • OUTBOUND|INBOUND|ANY:沿着在遍历中指向任一方向的传出,传入或边缘; 请注意,这不能被绑定参数替换。
  • startVertex(string | object):遍历来源的顶点。这可以以ID字符串的形式或具有该属性的文档的形式指定_id所有其他值将导致警告和空的结果。如果指定的文档不存在,结果也是空的,没有警告。
  • GRAPH graphName(string):标识命名图形的名称。它的顶点和边缘集合将被查找。
  • OPTIONS options(object,optional):用于修改遍历的执行。只有以下属性有效果,所有其他属性将被忽略:
    • uniqueVertices(string):可选地确保顶点唯一性
      • path” - 保证没有路径返回一个重复的顶点
      • global” - 保证在遍历期间每个顶点最多被访问一次,无论从起始顶点到这个顶点有多少路径。如果您从最小深度min depth > 1之前发现的顶点开始,可能根本不会返回(它仍然可能是路径的一部分)。注意: 使用此配置,结果不再是确定性的。如果从startVertex顶点有多条路径,则选择其中一条路径
      • “none”(默认) - 不对顶点应用唯一性检查
    • uniqueEdges(string):可选地确保边缘唯一性
      • “path”(默认) - 保证没有路径返回一个重复的边
      • global” - 保证在遍历过程中,每个边缘最多被访问一次,无论从起始顶点到该边缘有多少条路径。如果从a开始,min depth > 1最小深度之前发现的边缘根本不会被返回(它仍然可能是路径的一部分)。注意: 使用此配置,结果不再是确定性的。如果有从多个路径startVertex超过边缘的那些中的一个被拾取。
      • “none” - 不对边缘应用唯一性检查。注意: 使用此配置,遍历将跟随边沿周期。
    • bfs(bool):可选地使用可选的宽度优先遍历算法
      • true - 遍历将被执行宽度优先。结果将首先包含深度1的所有顶点。比深度2处的所有顶点等等。
      • false(默认) - 遍历将以深度优先执行。它首先将深度1的一个顶点的最小深度的最小深度返回最大深度。对于深度1处的下一个顶点,依此类推。

使用集合集

FOR vertex[, edge[, path]]  IN [min[..max]]  OUTBOUND|INBOUND|ANY startVertex  edgeCollection1, ..., edgeCollectionN  [OPTIONS options]

而不是GRAPH graphName您可以指定边集合的列表。顶点集合由边集合中的边确定。其余的行为与命名版本相似。如果多次指定相同的边缘集合,它将表现为只被指定一次。仅当集合没有冲突的遍历方向时,才允许指定相同的边集。

横向混合

对于具有边集合列表的遍历,您可以选择指定一些边集合的方向。例如,你有三个边缘集合edge1edge2edge3,其中在edge2方向上没有相关性,但是在edge1edge3中应该考虑方向。在这种情况下,您可以使用OUTBOUND作为通用遍历方向,并且可以使用任何 专门用于edge2的方式,如下所示:

FOR vertex IN OUTBOUND  startVertex  edges1, ANY edges2, edges3

列表中没有指定自己的方向的所有集合将使用之后定义的方向IN这允许在遍历中为每个集合使用不同的方向。

使用过滤器和解释器推断成本

遍历中发出的所有三个变量也可以用在过滤器语句中。对于这些过滤语句中的一些,优化器可以检测到可以更早地修剪遍历的路径,因此滤波结果将不会首先发送到变量。这可能会显着提高查询的性能。每当过滤器不满足时,将跳过完整的顶点路径长度大于max的所有路径将永远不会被计算。

在当前状态下,AND可以优化OR 组合滤波器,但组合滤波器不能。

过滤路径

对路径进行过滤允许最强大的过滤,并可能对性能产生最大影响。使用路径变量可以对特定的迭代深度进行过滤。您可以通过指定一个负数,通过指定一个正数(然后限定优化)或相对于路径末尾的相对位置来过滤路径中的绝对位置。

过滤路径上的边

FOR v, e, p IN 1..5 OUTBOUND 'circles/A' GRAPH 'traversalGraph'  FILTER p.edges[0].theTruth == true  RETURN p

将过滤所有路径,其中起始边(索引0)的属性 theTruth等于true所产生的路径长达5个项目。

过滤路径上的顶点

类似于过滤路径上的边缘,您也可以过滤顶点:

FOR v, e, p IN 1..5 OUTBOUND 'circles/A' GRAPH 'traversalGraph'  FILTER p.vertices[1]._key == "G"  RETURN p

组合几个过滤器

当然,您可以以任何您喜欢的方式组合这些过滤器:

FOR v, e, p IN 1..5 OUTBOUND 'circles/A' GRAPH 'traversalGraph'  FILTER p.edges[0].theTruth == true     AND p.edges[1].theFalse == false  FILTER p.vertices[1]._key == "G"  RETURN p

该查询将过滤所有路径,其中第一个边缘的属性 theTruth等于true,第一个顶点为“G”,第二个边缘的属性theFalse等于false所产生的路径长达5个项目。

注意:虽然我们定义了一个最小的1,我们只会得到深度2的结果,这是因为在深度为1的所有结果第二边缘不存在,因此不能在这里满足的条件。

过滤整个路径

在数组比较运算符的帮助下,也可以在整个路径上定义过滤器,如同所有的边都应该具有的.Truth == true:

FOR v, e, p IN 1..5 OUTBOUND 'circles/A' GRAPH 'traversalGraph'  FILTER p.edges[*].theTruth ALL == true  RETURN p

或者NONE的边缘应该有theTruth == true:

FOR v, e, p IN 1..5 OUTBOUND 'circles/A' GRAPH 'traversalGraph'  FILTER p.edges[*].theTruth NONE == true  RETURN p

上述两个示例由优化器识别,并且可能使用除边缘索引之外的其他索引。

还可以定义路径上的至少一个边缘必须满足以下条件:

FOR v, e, p IN 1..5 OUTBOUND 'circles/A' GRAPH 'traversalGraph'  FILTER p.edges[*].theTruth ANY == true  RETURN p

确保至少有一个,但可能有更多的边缘满足条件。所有上述过滤器都可以以完全相同的方式在顶点上定义。

在路径上进行过滤,而不是对顶点或边缘进行过滤

路径上的过滤会影响图形上的迭代。如果不满足某些条件,则遍历可能会停止沿着该路径继续。

相反,顶点或边缘上的过滤器只表示您是否对这些文档的实际价值感兴趣。因此,它会影响返回文档的列表(如果您返回v或e)与指定非空min相似如果指定最小值为2,则必须执行这些路径的前两个节点上的遍历 - 您将不会在结果数组中看到它们。

类似的是顶点或边缘上的过滤器 - 移动器必​​须沿着这些节点走动,因为您可能对路径上的文档感兴趣。

例子

我们将创建一个简单的对称遍历演示图:

遍历图

a 例子> var examples = require“@ arangodb / graph-examples / example-graph.js”);aangosh> var graph = examples.loadGraph(“traversalGraph”);aangosh> db.circles.toArray();aangosh> db.edges.toArray();
[   {     “_key” “I” “_id” :圈/ I” “_rev” “_VRZRUTu ---” ,标签”“9”   },   {     “_key” “G” “_id” :圆圈/ G” “_rev” “_VRZRUTq - E” ,标记”“7”   },   {     “_key” “F” “_id” :圆圈/ F” “_rev” “_VRZRUTq - D” ,标记”“6”   },   {     “_key” “A” “_id” :圆圈/ A” “_rev” “_VRZRUTq ---” ,标记”“1”   },   {     “_key” “E” “_id” :圆圈/ E” “_rev” “_VRZRUTq - C” ,标记”“5”   },   {     “_key” “C” “_id” :圆圈/ C” “_rev” “_VRZRUTq - A” ,标记”“3”   },   {     “_key” “D” “_id” :圆圈/ D” “_rev” “_VRZRUTq - B” ,标记”“4”   },   {     “_key” “J” “_id” :圆圈/ J” “_rev” “_VRZRUTu --_” ,标记”“10”   },   {     “_key” “B” “_id” :圆圈/ B” “_rev” “_VRZRUTq --_” ,标记”“2”   },   {     “_key” “H” “_id” :圆圈/ H” “_rev” “_VRZRUTq - F” ,标记”“8”   },   {     “_key” “K” “_id” :圆圈/ K” “_rev” “_VRZRUTu - A” ,标记”“11”   } ]aangosh> db.edges.toArray();[   {     “_key” “7561” “_id” :边缘/ 7561” “_from” :圆圈/ A” “_to” :圆圈/ B” “_rev” “_VRZRUTu - B” ,      theFalse“false”theTruth“true”label“”left_bar“   },   {     “_key” “7586” “_id” :边缘/ 7586” “_from” :圆圈/ G” “_to” :圆圈/ J” “_rev” “_VRZRUTy - C” ,      theFalse“false”theTruth“true”label“”right_zip“   },   {     “_key” “7568” “_id” :边缘/ 7568” “_from” :圆圈/ C” “_to” :圆圈/ D” “_rev” “_VRZRUTu - D” ,      theFalse“false”theTruth“true”label“”left_blorg“   },   {     “_key” “7577” “_id” :边缘/ 7577” “_from” :圆圈/ A” “_to” :圆圈/ G” “_rev” “_VRZRUTy --_” ,      theFalse“false”theTruth“true”label“”right_foo“   },   {     “_key” “7589” “_id” :边缘/ 7589” “_from” :圆圈/ J” “_to” :圆圈/ K” “_rev” “_VRZRUTy - D” ,      theFalse“false”theTruth“true”label“”right_zup“   },   {     “_key” “7565” “_id” :边缘/ 7565” “_from” :圆圈/ B” “_to” :圆圈/ C” “_rev” “_VRZRUTu - C” ,      theFalse“false”theTruth“true”label“”left_blarg“   },   {     “_key” “7583” “_id” :边缘/ 7583” “_from” :圆圈/ H” “_to” :圆圈/ I” “_rev” “_VRZRUTy - B” ,      theFalse“false”theTruth“true”label“”right_blub“   },   {     “_key” “7571” “_id” :边缘/ 7571” “_from” :圆圈/ B” “_to” :圆圈/ E” “_rev” “_VRZRUTu - E” ,      theFalse“false”theTruth“true”label“”left_blub“   },   {     “_key” “7580” “_id” :边缘/ 7580” “_from” :圆圈/ G” “_to” :圆圈/ H” “_rev” “_VRZRUTy - A” ,      theFalse“false”theTruth“true”label“”right_blob“   },   {     “_key” “7574” “_id” :边缘/ 7574” “_from” :圆圈/ E” “_to” :圆圈/ F” “_rev” “_VRZRUTy ---” ,      theFalse“false”theTruth“true”label“”left_schubi“   } ]

要开始我们选择完整的图表。为了更好的概述,我们只返回顶点ID:

arangosh> db._query(“FOR v IN 1..3 OUTBOUND”circles / A'GRAPH'遍历图'RETURN v._key“);[   “B” “C” “D” “E” “F” “G” “H” “I” “J” “K” ][object ArangoQueryCursor,count10hasMorefalse ]arangosh> db._query(“FOR v IN 1..3 OUTBOUND”circles / A'edges RETURN v._key“);[   “B” “C” “D” “E” “F” “G” “H” “I” “J” “K” ][object ArangoQueryCursor,count10hasMorefalse ]

我们可以很好地看到它正在朝向第一个外部顶点,然后返回到分支下降到下一个树。之后,它返回到我们的开始节点,再次下降。我们可以看到两个查询返回相同的结果,第一个使用命名图,第二个使用边缘集合直接。

现在我们只想要一个特定深度(min = max = 2)的元素,即后面的元素:

aangosh> db._query(“FOR v IN 2..2 OUTBOUND”circles / A'GRAPH'traversalGraph'return v._key“);[   “C”“E”“H”“J” ][对象ArangoQueryCursor,count4hasMorefalse ]aangosh> db._query(“FOR v IN 2 OUTBOUND”circles / A'GRAPH'遍历图返回v._key“);[   “C”“E”“H”“J” ][对象ArangoQueryCursor,count4hasMorefalse ]

可以看到,我们可以用两种方式表达:表达式中有或没有max参数。

过滤示例

现在我们开始添加一些过滤器。我们想在图表的右侧切割分支,我们可以通过两种方式进行过滤:

  • 我们知道深度1的顶点为_key==G
  • 我们知道label连接AG的边属性right_foo
arangosh> db._query(“FOR v,e,p IN 1..3 OUTBOUND”circles / A'GRAPH'遍历图'FILTER p.vertices [1] ._ key!='G'RETURN v._key“);[   “B”“C”“D”“E”“F” ][object ArangoQueryCursor,count5hasMorefalse ]arangosh> db._query(“FOR v,e,p IN 1..3 OUTBOUND”circles / A'GRAPH'遍历图'FILTER p.edges [0] .label!='right_foo'RETURN v._key“);[   “B”“C”“D”“E”“F” ][object ArangoQueryCursor,count5hasMorefalse ]

我们可以看到G中的所有顶点在两个查询中被跳过。顶点上的第一个过滤器_key,第二个在边缘标签上。再次注意,只要过滤器不符合三个要素中的任何一个 ve或者p完整的一组将被从结果中排除。

我们也可以组合几个过滤器,例如过滤右分支(G)和E分支:

arangosh> db._query(“FOR v,e,p IN 1..3 OUTBOUND”circles / A'GRAPH'遍历图'FILTER p.vertices [1] ._ key!='G'FILTER p.edges [1]。 label!='left_blub'return v._key“);[   “B”“C”“D” ][对象ArangoQueryCursor,count3hasMorefalse ]aangosh> db._query(“FOR v,e,p IN 1..3 OUTBOUND”circles / A'GRAPH'traversalGraph'FILTER p.vertices [1] ._ key!='G'AND p.edges [1]。 label!='left_blub'return v._key“);[   “B”“C”“D” ][对象ArangoQueryCursor,count3hasMorefalse ]

您可以看到,将两个FILTER语句与一个AND结果相结合

比较OUTBOUND / INBOUND / ANY

所有我们以前的例子在OUTBOUND边缘方向上遍历图形但是,您也可以反向(INBOUND)或两者(ANY)。因为circles/A只有出站的边缘,我们开始从查询circles/E

aangosh> db._query(“FOR v IN 1..3 OUTBOUND”circles / E'GRAPH'遍历图返回v._key“);aangosh> db._query(“FOR v IN 1..3 INBOUND”circles / E'GRAPH'遍历图返回v._key“);arangosh> db._query(“FOR v IN 1..3 ANY”circles / E'GRAPH'遍历图返回v._key“);
显示执行结果

第一个遍历只能沿前进(OUTBOUND)方向行进。因此,从Ë我们只能看到˚F走向相反方向(INBOUND),我们看到AB → A的路径

走向正反方向(ANY),我们可以看到更多样化的结果。首先,我们看到了FA的简单路径然而,这些顶点在其他方向上具有边,它们将被遍历。

注意:运行器可以多次使用相同的边。举例来说,如果它行走Ë˚F,它将继续从走路˚FË 再次使用相同的边缘。因此,我们将在结果中看到重复的节点。

请注意,方向不能通过绑定参数传入。

使用AQL解释器进行优化

现在让我们看看优化器在幕后做什么,并使用explainer来检查遍历查询

aangosh> db._explain(“FOR v,e,p IN 1..3 OUTBOUND”circles / A'GRAPH'遍历图'LET localScopeVar = RAND()> 0.5 FILTER p.edges [0] .theTruth!= localScopeVar RETURN v ._key“,{},{colors:false});arangosh> db._explain(“FOR v,e,p IN 1..3 OUTBOUND”circles / A'GRAPH'遍历图'FILTER p.edges [0] .label =='right_foo'RETURN v._key“,{} ,{colors:false});
显示执行结果

我们现在看到两个查询:在一个中,我们添加一个变量localScopeVar,它不在遍历本身的范围内 - 在遍历器内部是不知道的。因此,该过滤器只能在遍历之后执行,这在大图中可能是不期望的。另一方面,第二个查询仅在路径上运行,因此在执行遍历期间可以使用此条件。根据此条件筛选出的路径将不会被处理。

最后再次清理一下:

a 例子> var examples = require“@ arangodb / graph-examples / example-graph.js”);arangosh> examples.dropGraph(“traversalGraph”);真正

如果这种遍历对您的需求不够强大,就像您无法将条件描述为AQL过滤器语句,那么您可能需要查看 手动设计的运行程序

另请参阅如何组合图遍历


AQL最短路径

一般查询想法

这种类型的查询应该在图中找到两个给定文档(startVertextargetVertex之间的最短路径对于这个最短路径上的所有顶点,您将得到一个具有两个项目的集合的结果:

  1. 这个路径上的顶点。
  2. 边缘指向它。

示例执行

让我们来看一个简单的例子来解释它是如何工作的。这是我们要找到最短路径的图表:

遍历图

现在我们使用以下参数进行查询:

  1. 我们从顶点A开始
  2. 我们完成与顶点ð

所以很明显,我们将按照这个顺序在最短路径上有顶点ABCD。比最短的路径语句将返回以下对:

顶点边缘一个空值A→BCB→CðC→D

注意:第一个边缘将始终是null因为没有边缘指向startVertex

句法

现在我们来看看如何编写最短路径查询。您有两个选项,您可以使用命名图或一组边集(匿名图)。

使用命名图

FOR vertex[, edge]  IN OUTBOUND|INBOUND|ANY SHORTEST_PATH  startVertex TO targetVertex  GRAPH graphName  [OPTIONS options]
  • FOR:最多发射两个变量:
    • vertex(object):当前顶点在最短路径上
    • (对象,可选):指向顶点的边
  • IN OUTBOUND|INBOUND|ANY:定义沿着哪个方向边缘(传出,传入或两者)
  • startVertex TO targetVertex(两个字符串|对象):两个顶点之间的最短路径将被计算。这可以以ID字符串的形式或具有该属性的文档的形式指定_id所有其他值将导致警告和空的结果。如果其中一个指定的文档不存在,结果也是空的,没有警告。
  • GRAPH graphName(string):标识命名图形的名称。它的顶点和边缘集合将被查找。
  • OPTIONS options(object,optional):用于修改遍历的执行。只有以下属性有效果,所有其他属性将被忽略:
    • weightAttribute(string):应用于读取边缘权重的顶级边缘属性。如果属性不存在或不是数字,则将使用defaultWeight
    • defaultWeight(number):如果边缘文档中没有weightAttribute,或者不是数字,则此值将用作回退默认值为1。

使用集合集

FOR vertex[, edge]  IN OUTBOUND|INBOUND|ANY SHORTEST_PATH  startVertex TO targetVertex  edgeCollection1, ..., edgeCollectionN  [OPTIONS options]

而不是GRAPH graphName您可以指定边缘集合列表(匿名图)。所涉及的顶点集合由给定边集合的边确定。其余的行为与命名版本相似。

横向混合

对于具有边集合列表的最短路径,您可以选择指定某些边集合的方向。例如,你有三个边缘集合edge1edge2edge3,其中在edge2方向上没有相关性,但是在edge1edge3中应该考虑方向。在这种情况下,您可以使用OUTBOUND作为常规搜索方向,并且任何 专门用于edge2的方式如下:

FOR vertex IN OUTBOUND SHORTEST_PATH  startVertex TO targetVertex  edges1, ANY edges2, edges3

列表中没有指定自己的方向的所有集合将使用IN(此处:OUTBOUND之后定义的方向这允许在您的路径搜索中为每个集合使用不同的方向。

条件最短路径

SHORTEST_PATH计算将只找到一个无条件的最短路径。使用这个结构,不可能定义一个条件,如:“找到所有边都是X类型的最短路径”。如果你想这样做,使用正常的横越代替与选项{bfs: true}组合LIMIT 1

例子

我们将创建一个简单的对称遍历演示图:

遍历图

a 例子> var examples = require“@ arangodb / graph-examples / example-graph.js”);aangosh> var graph = examples.loadGraph(“traversalGraph”);aangosh> db.circles.toArray();[   {     “_key” “I” “_id” :圈/ I” “_rev” “_VRZRUPK - E” ,标签”“9”   },   {     “_key” “G” “_id” :圆圈/ G” “_rev” “_VRZRUPK - C” ,标记”“7”   },   {     “_key” “F” “_id” :圆圈/ F” “_rev” “_VRZRUPK - B” ,标记”“6”   },   {     “_key” “A” “_id” :圆圈/ A” “_rev” “_VRZRUPG ---” ,标记”“1”   },   {     “_key” “E” “_id” :圆圈/ E” “_rev” “_VRZRUPK - A” ,标记”“5”   },   {     “_key” “C” “_id” :圆圈/ C” “_rev” “_VRZRUPK ---” ,标记”“3”   },   {     “_key” “D” “_id” :圆圈/ D” “_rev” “_VRZRUPK --_” ,标记”“4”   },   {     “_key” “J” “_id” :圆圈/ J” “_rev” “_VRZRUPK - F” ,标记”“10”   },   {     “_key” “B” “_id” :圆圈/ B” “_rev” “_VRZRUPG --_” ,标记”“2”   },   {     “_key” “H” “_id” :圆圈/ H” “_rev” “_VRZRUPK - D” ,标记”“8”   },   {     “_key” “K” “_id” :圆圈/ K” “_rev” “_VRZRUPO ---” ,标记”“11”   } ]aangosh> db.edges.toArray();[   {     “_key” “7505” “_id” :边缘/ 7505” “_from” :圆圈/ J” “_to” :圆圈/ K” “_rev” “_VRZRUPS - C” ,      theFalse“false”theTruth“true”label“”right_zup“   },   {     “_key” “7499” “_id” :边缘/ 7499” “_from” :圆圈/ H” “_to” :圆圈/ I” “_rev” “_VRZRUPS - A” ,      theFalse“false”theTruth“true”label“”right_blub“   },   {     “_key” “7487” “_id” :边缘/ 7487” “_from” :圆圈/ B” “_to” :圆圈/ E” “_rev” “_VRZRUPO - C” ,      theFalse“false”theTruth“true”label“”left_blub“   },   {     “_key” “7493” “_id” :边缘/ 7493” “_from” :圆圈/ A” “_to” :圆圈/ G” “_rev” “_VRZRUPS ---” ,      theFalse“false”theTruth“true”label“”right_foo“   },   {     “_key” “7496” “_id” :边缘/ 7496” “_from” :圆圈/ G” “_to” :圆圈/ H” “_rev” “_VRZRUPS --_” ,      theFalse“false”theTruth“true”label“”right_blob“   },   {     “_key” “7490” “_id” :边缘/ 7490” “_from” :圆圈/ E” “_to” :圆圈/ F” “_rev” “_VRZRUPO - D” ,      theFalse“false”theTruth“true”label“”left_schubi“   },   {     “_key” “7502” “_id” :边缘/ 7502” “_from” :圆圈/ G” “_to” :圆圈/ J” “_rev” “_VRZRUPS - B” ,      theFalse“false”theTruth“true”label“”right_zip“   },   {     “_key” “7481” “_id” :边缘/ 7481” “_from” :圆圈/ B” “_to” :圆圈/ C” “_rev” “_VRZRUPO - A” ,      theFalse“false”theTruth“true”label“”left_blarg“   },   {     “_key” “7484” “_id” :边缘/ 7484” “_from” :圆圈/ C” “_to” :圆圈/ D” “_rev” “_VRZRUPO - B” ,      theFalse“false”theTruth“true”label“”left_blorg“   },   {     “_key” “7477” “_id” :边缘/ 7477” “_from” :圆圈/ A” “_to” :圆圈/ B” “_rev” “_VRZRUPO --_” ,      theFalse“false”theTruth“true”label“”left_bar“   } ]

我们从AD的最短路径开始如上:

aangosh> db._query(“FOR v,e IN OUTBOUND SHORTEST_PATH”circles / A'TO'circles / D'GRAPH'遍历图'返回[v._key,e._key]“);[   [     “A”null   ]   [     “B”“7477”   ]   [     “C”“7481”   ]   [     “D”“7484”   ] ][对象ArangoQueryCursor,count4hasMorefalse ]aangosh> db._query(“FOR v,e IN OUTBOUND SHORTEST_PATH”circles / A'TO'circles / D'edges RETURN [v._key,e._key]“);[   [     “A”null   ]   [     “B”“7477”   ]   [     “C”“7481”   ]   [     “D”“7484”   ] ][对象ArangoQueryCursor,count4hasMorefalse ]

我们可以看到我们的期望得到实现。我们发现顶点的顺序是正确的,第一个边是空的,因为没有边缘指向他的路径上的起始顶点。

我们还可以根据集合中的文档计算最短路径:

arangosh> db._query(“FOR a IN circles FILTER a._key =='A'FOR d IN圈FILTER d._key =='D'FOR v,e IN OUTBOUND SHORTEST_PATH a TO d GRAPH'遍历图'返回[v ._key,e._key]“);[   [     “A”null   ]   [     “B”“7477”   ]   [     “C”“7481”   ]   [     “D”“7484”   ] ][对象ArangoQueryCursor,count4hasMorefalse ]aangosh> db._query(“FOR a IN circles FILTER a._key =='A'FOR d IN圈FILTER d._key =='D'FOR v,e IN OUTBOUND SHORTEST_PATH a TO d edges RETURN [v._key, e._key]“);[   [     “A”null   ]   [     “B”“7477”   ]   [     “C”“7481”   ]   [     “D”“7484”   ] ][对象ArangoQueryCursor,count4hasMorefalse ]

最后再次清理一下:

a 例子> var examples = require“@ arangodb / graph-examples / example-graph.js”);arangosh> examples.dropGraph(“traversalGraph”);