我创建了一个非常大的方向加权图,我试图找到两点之间最宽的路径。
每条边都有计数属性
以下是图表的一小部分:
我找到了docs并修改了查询,因此路径收集方式如此:
MATCH p = (v1:Vertex {name:'ENTRY'})-[:TRAVELED*]->(v2:Vertex {name:'EXIT'})
WITH p, EXTRACT(c IN RELATIONSHIPS(p) | c.count) AS counts
UNWIND(counts) AS b
WITH p, MIN(b) AS count
ORDER BY count DESC
RETURN NODES(p) AS `Widest Path`, count
LIMIT 1
此查询似乎需要大量内存,甚至在部分数据上也会失败。
更新:对于分类,查询一直在运行,直到内存不足为止。
我发现this example,结合了spark和neo4j的使用。不幸的是Mazerunner为Neo4j,不支持"最广泛的路径"算法开箱即用。什么是正确的方法来运行最广泛的路径"查询一个非常大的图表?
答案 0 :(得分:4)
你的算法需要花费很长时间才能运行的原因是因为(a)你有一个大图,(b)你的内存参数可能需要调整(见注释)和(c)你列举了每个可能的路径ENTRY
和EXIT
。根据图表的结构,这可能是大量的路径。
请注意,如果您正在寻找最广泛的路径,那么最宽的是边缘上的最大/最小权重。这意味着您可能正在计算和重新计算许多您可以忽略的路径。
Wikipedia has good information on this algorithm you should consider。特别是:
可以使用a找到最大容量路径和minimax路径 单一来源和单一目的地即使在模型中也非常有效 计算只允许比较输入图的边缘 权重而非算术。[12] [18]算法保持一个 设置已知包含的瓶颈边缘的边缘S. 最佳路径;最初,S只是所有m个边缘的集合 图形。在算法的每次迭代中,它将S分成有序的 大小相等的子集S1,S2 ......的序列;该 以这样的方式选择该分区中的子集数量 通过重复可以找到子集之间的分裂点 中位数发现时间O(m)。然后算法重新计算每个边缘 图形由包含边缘的子集的索引组成,并使用 在重加权图上修改Dijkstra算法;基于 这个计算的结果,它可以在线性时间中确定哪个 子集包含瓶颈边缘权重。然后它取代了S by 它确定包含瓶颈重量的子集Si, 并使用这个新集合S开始下一次迭代 S可以分裂成的子集随着每个子集呈指数增长 步骤,因此迭代次数与迭代次数成正比 对数函数,O(log n),总时间为O(m log n)。[18]在 计算模型,其中每个边权重是机器整数, 在这个算法中使用重复的二分法可以用a代替 Han& amp; list的分裂技术Thorup(2002),允许S成为 在一步中分成O(√m)个较小的Si组并导致a 线性总时间限制。
您应该考虑使用cypher而不是当前的“枚举所有路径”方法来实现此方法,因为“枚举所有路径”方法您重新检查相同的边数是否与涉及该特定路径的路径相同边缘。
没有现成的软件会为你做这件事,我建议你拿这个段落(并检查它的引用以获取更多信息)然后实现它。我认为表现明智,你可以比你当前的查询做得更好。
答案 1 :(得分:2)
一些想法。
您的查询(以及原始示例查询)可以简化。这可能会或可能不足以防止您的记忆问题。
对于每个匹配的路径,不需要:(a)创建计数集合,(b)将其UNWIND到行中,然后(c)执行MIN聚合。使用REDUCE函数可以获得相同的结果:
MATCH p = (v1:Vertex {name:'ENTRY'})-[:TRAVELED*]->(v2:Vertex {name:'EXIT'})
WITH p, REDUCE(m = 2147483647, c IN RELATIONSHIPS(p) | CASE WHEN c.count < m THEN c.count ELSE m END) AS count
ORDER BY count DESC
RETURN NODES(p) AS `Widest Path`, count
LIMIT 1;
(我假设count
属性值是int。2147483647
是max int值。)
您应该在name
标签的Vertex
属性上创建索引(或者更合适的是,唯一性约束)。例如:
CREATE INDEX ON :Vertex(name)
EDITED
上述查询的增强版可能会解决您的内存问题:
MERGE (t:Temp) SET t.count = 0, t.widest_path = NULL
WITH t
OPTIONAL MATCH p = (v1:Vertex {name:'ENTRY'})-[:TRAVELED*]->(v2:Vertex {name:'EXIT'})
WITH t, p, REDUCE(m = 2147483647, c IN RELATIONSHIPS(p) | CASE WHEN c.count < m THEN c.count ELSE m END) AS count
WHERE count > t.count
SET t.count = count, t.widest_path = NODES(p)
WITH COLLECT(DISTINCT t)[0] AS t
WITH t, t.count AS count, t.widest_path AS `Widest Path`
DELETE t
RETURN `Widest Path`, count;
它创建(并最终删除)临时:Temp
节点以跟踪当前“获胜”计数和(相应的路径节点)。 (您必须确保未使用标签Temp
。)
以WITH
开头的COLLECT(DISTINCT t)
子句使用不同:Temp
个节点的聚合(其中只有1个),以确保Cypher仅保留对{{1}的单个引用} node,无论有多少路径满足:Temp
子句。此外,WHERE
子句不包括WITH
,因此Cypher不会累积我们不关心的路径。正是这个条款可能在帮助避免记忆问题方面是最重要的。
我没试过这个。