原文:Graphs in AQL
AQL中的图形
在ArangoDB中可以使用多种方式处理图形,以及使用AQL查询图形的不同方法。
管理图形的两个选项是使用
- ArangoDB管理一个图形中涉及的集合的命名图,或
- 图形功能在文档和边缘集合的组合上。
可以通过图形模块 或通过Web界面定义命名图。该定义包含图形的名称,以及所涉及的顶点和边缘集合。由于管理功能分层在简单的文档和边集合之上,您还可以使用常规的AQL函数来处理它们。
图形查询的AQL语言结构支持两种变体(命名图和松散耦合的集合集,也称为匿名图)。这些构造充分利用优化,因此可以预期最佳性能:
这些类型的查询仅在数据模型中使用边集合和/或图形时才有用。
在AQL中绘制遍历
一般查询想法
遍历从一个特定文档(startVertex)开始,并且跟随连接到该文档的所有边。对于这些边缘所针对的所有文档(顶点),它将再次跟随所有连接到它们的边缘,依此类推。可以定义至少(最小深度)和最多(最大深度)应执行这些后续迭代中有多少次。
对于在此过程中访问的所有顶点,在最小深度和最大深度迭代之间的范围内, 您将获得一组结果,其中包含三个项目:
- 被访问的顶点。
- 边缘指向它。
- 从startVertex到被访问顶点的完整路径,作为具有属性边和属性顶点的对象,每个都是相应元素的列表。这些列表进行排序,这意味着在所述第一元件的顶点 是startVertex和上次被访问的顶点,并且在第n个元件的边缘的第n个元件与在第(n + 1)个元件连接的顶点。
示例执行
让我们来看一个简单的例子来解释它是如何工作的。这是我们将要遍历的图:
我们使用以下参数进行查询:
- 我们从顶点A开始。
- 我们使用最小深度为1。
- 我们使用的最大深度为2。
- 我们只沿着边缘的OUTBOUND方向
现在它走到A的直接邻居之一,说B(注:订购不能保证!):
查询将记住状态(红色圆圈),并将发出第一个结果 A → B(黑盒)。这也将阻止运行器被循环捕获。现在再次访问B的直接邻居之一,说E:
我们限制了最大深度为2的查询,所以它不会选择E的任何邻居,因为从A到E的路径已经需要2个步骤。相反,我们将返回一级到B,并继续与其他直接邻居:
再之后,我们生产这样的结果,我们将回踩乙。但是没有B的邻居,我们还没有访问过。因此,我们再走一步回到A,继续在那里的任何其他邻居。
与我们访问H之前的迭代相同:
和J:
在这些步骤之后,没有进一步的结果。所以这个查询一起返回了以下路径:
- A → B
- A → B → E
- A → B → C
- A → G
- A → G → H
- 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处的下一个顶点,依此类推。
- uniqueVertices(string):可选地确保顶点唯一性
使用集合集
FOR vertex[, edge[, path]]
IN [min[..max]]
OUTBOUND|INBOUND|ANY startVertex
edgeCollection1, ..., edgeCollectionN
[OPTIONS options]
而不是GRAPH graphName
您可以指定边集合的列表。顶点集合由边集合中的边确定。其余的行为与命名版本相似。如果多次指定相同的边缘集合,它将表现为只被指定一次。仅当集合没有冲突的遍历方向时,才允许指定相同的边集。
横向混合
对于具有边集合列表的遍历,您可以选择指定一些边集合的方向。例如,你有三个边缘集合edge1,edge2和edge3,其中在edge2方向上没有相关性,但是在edge1和edge3中应该考虑方向。在这种情况下,您可以使用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,count:10,hasMore:false ]
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,count:10,hasMore:false ]
我们可以很好地看到它正在朝向第一个外部顶点,然后返回到分支下降到下一个树。之后,它返回到我们的开始节点,再次下降。我们可以看到两个查询返回相同的结果,第一个使用命名图,第二个使用边缘集合直接。
现在我们只想要一个特定深度(min = max = 2)的元素,即后面的元素:
aangosh> db._query(“FOR v IN 2..2 OUTBOUND”circles / A'GRAPH'traversalGraph'return v._key“);
[
“C”,
“E”,
“H”,
“J”
]
[对象ArangoQueryCursor,count:4,hasMore:false ]
aangosh> db._query(“FOR v IN 2 OUTBOUND”circles / A'GRAPH'遍历图返回v._key“);
[
“C”,
“E”,
“H”,
“J”
]
[对象ArangoQueryCursor,count:4,hasMore:false ]
可以看到,我们可以用两种方式表达:表达式中有或没有max参数。
过滤示例
现在我们开始添加一些过滤器。我们想在图表的右侧切割分支,我们可以通过两种方式进行过滤:
- 我们知道深度1的顶点为
_key
==G
- 我们知道
label
连接A到G的边的属性是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,count:5,hasMore:false ]
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,count:5,hasMore:false ]
我们可以看到G中的所有顶点在两个查询中被跳过。顶点上的第一个过滤器_key
,第二个在边缘标签上。再次注意,只要过滤器不符合三个要素中的任何一个 v
,e
或者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,count:3,hasMore:false ]
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,count:3,hasMore:false ]
您可以看到,将两个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),我们看到A:B → A的路径。
走向正反方向(ANY),我们可以看到更多样化的结果。首先,我们看到了F和A的简单路径。然而,这些顶点在其他方向上具有边,它们将被遍历。
注意:运行器可以多次使用相同的边。举例来说,如果它行走Ë到˚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最短路径
一般查询想法
这种类型的查询应该在图中找到两个给定文档(startVertex和targetVertex)之间的最短路径。对于这个最短路径上的所有顶点,您将得到一个具有两个项目的集合的结果:
- 这个路径上的顶点。
- 边缘指向它。
示例执行
让我们来看一个简单的例子来解释它是如何工作的。这是我们要找到最短路径的图表:
现在我们使用以下参数进行查询:
- 我们从顶点A开始。
- 我们完成与顶点ð。
所以很明显,我们将按照这个顺序在最短路径上有顶点A,B,C和D。比最短的路径语句将返回以下对:
顶点 | 边缘 |
---|---|
一个 | 空值 |
乙 | A→B |
C | B→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
您可以指定边缘集合列表(匿名图)。所涉及的顶点集合由给定边集合的边确定。其余的行为与命名版本相似。
横向混合
对于具有边集合列表的最短路径,您可以选择指定某些边集合的方向。例如,你有三个边缘集合edge1,edge2和edge3,其中在edge2方向上没有相关性,但是在edge1和edge3中应该考虑方向。在这种情况下,您可以使用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“
}
]
我们从A到D的最短路径开始如上:
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,count:4,hasMore:false ]
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,count:4,hasMore:false ]
我们可以看到我们的期望得到实现。我们发现顶点的顺序是正确的,第一个边是空的,因为没有边缘指向他的路径上的起始顶点。
我们还可以根据集合中的文档计算最短路径:
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,count:4,hasMore:false ]
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,count:4,hasMore:false ]
最后再次清理一下:
a 例子> var examples = require(“@ arangodb / graph-examples / example-graph.js”);
arangosh> examples.dropGraph(“traversalGraph”);