我有一个树状结构,实际上是一个有向无环图。一个小版本如下所示。
在任何父级,我想总结子树的一些属性。今天我在AQL中使用TRAVERSAL和COLLECT基于起始节点执行此操作:
var str = "test %s, test %%, test %%s too";
var output = str.replace(/%%|%s|./g, function(match, capture) {
return match.replace("%%", "%").replace("%s", "foobar");
});
console.log("output:", output);
然后我可以在组上使用聚合。 (使用ArangoDB 2.8,我相信现在可以直接在collect语句中完成。)唯一性选项解决了重复问题。
缩放
当树(图)增长到相当大的尺寸(10-20k节点)时,这将如何扩展?我需要快速,因为用户将等待响应(不是长时间运行的作业)。
我想在节点中缓存值并且有一个脏标志。然后在节点1中可以只加2和3,如果它们都是 clean 。 问题是5和2和3的总和。
我该如何解决这个问题?或者这是一个非问题 - 遍历真的那么快吗?
到目前为止,我已经提出让每个节点都包含它的子树副本列表的想法,在1的情况下,这意味着信息" 5是包括两次"。这可以用来从1的总数中减去这个。但我怎么能找到这些信息呢?我已经考虑过使用> 1父节点查找所有节点,然后向上遍历(快速),然后以某种方式计算此信息。
答案 0 :(得分:2)
遍历的运行时间受到进程中实际触及的顶点和边缘数量的限制。因此,遍历的运行时间取决于路径的深度和分支因子(预期有多个具有多个父项的顶点)。
您描述的构造问题是遍历将选择从1
到5
的一条路径(比如左边的路径)并对所有值求和并最终返回1
选择正确的道路。现在它再次达到5
,但这次搜索深度低于上次5
的搜索深度,因此它必须再次实际遍历5上的子树,因为它现在可能会获得更大的距离在这条路径中(它不知道这个子树上的所有顶点都可以在更短的距离内到达)。此路径上的顶点不会再次调用访问者,但仍会遍历并跟踪,这会花费时间。
我尝试优化遍历来验证扩展。 首先,我注册了一个新的优化访问者:
require("@arangodb/aql/functions").register("test::counter", "function (config, result, vertex) {result[0] = result[0] || {value: 0}; result[0].value += vertex.value}");
此访问者对顶点的值进行求和并直接返回它们,因此我可以删除COLLECT
语句。我可以使用它我的AQL:
FOR x IN TRAVERSAL(TestVertices, TestEdges, 'TestVertices/0', 'outbound', {uniqueness:{vertices:'global'}, visitor: 'test::counter', maxDepth: 5012})
RETURN x.value
请注意:我在选项中提供了maxDepth
以进行高深度搜索,默认为256
。
我的测试树基本上是一个20.000
个顶点的链,其中每个第三个顶点都有一个额外的边到链后面的随机顶点(模拟你描述的多个父节点问题)
通过此遍历,我设法从5012
中的根搜索~5 secs
的深度。使用更高的深度,它呈指数增长。
我假设您的图表中有多个父项,所以我希望图表上的运行时间更少。
如果您期望更多读取然后写入,您还可以考虑计算每次写入的总和。 这将减慢写入速度,但会立即进行所有读取。
作为示例,您可以在更新值时使用以下AQL:
LET i = (FOR x IN 1..5012 INBOUND @start TestEdges
RETURN DISTINCT x)
FOR x IN i UPDATE x WITH {sum: x.sum + @add} IN TestVertices
使用绑定参数@add
表示要添加的值,使用@start
表示更新的顶点。使用这种技术,您的阅读查询是微不足道的:
FOR x IN TestVertices FILTER x._id == @start
RETURN x.sum
希望这有帮助。