Neo4J - 在非常大的图上找到最宽的路径

时间:2015-05-04 14:54:10

标签: algorithm graph neo4j apache-spark bigdata

我创建了一个非常大的方向加权图,我试图找到两点之间最宽的路径。

每条边都有计数属性

以下是图表的一小部分: enter image description here

我找到了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,不支持"最广泛的路径"算法开箱即用。什么是正确的方法来运行最广泛的路径"查询一个非常大的图表?

2 个答案:

答案 0 :(得分:4)

你的算法需要花费很长时间才能运行的原因是因为(a)你有一个大图,(b)你的内存参数可能需要调整(见注释)和(c)你列举了每个可能的路径ENTRYEXIT。根据图表的结构,这可能是大量的路径。

请注意,如果您正在寻找最广泛的路径,那么最宽的是边缘上的最大/最小权重。这意味着您可能正在计算和重新计算许多您可以忽略的路径。

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)

一些想法。

  1. 您的查询(以及原始示例查询)可以简化。这可能会或可能不足以防止您的记忆问题。

    对于每个匹配的路径,不需要:(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值。)

  2. 您应该在name标签的Vertex属性上创建索引(或者更合适的是,唯一性约束)。例如:

    CREATE INDEX ON :Vertex(name)
    
  3. 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不会累积我们不关心的路径。正是这个条款可能在帮助避免记忆问题方面是最重要的。

    我没试过这个。