【问题标题】:Neo4j (Cypher) - Is it possible to use non-implicit aggregation?Neo4j (Cypher) - 是否可以使用非隐式聚合?
【发布时间】:2018-07-03 15:02:53
【问题描述】:

我的问题很简单。我一直在尝试编写一个使用聚合函数的 Cypher 查询 - min()

我正在尝试使用 Neo4j 3.4 中提供的新空间函数来获取与特定节点最近的节点。我的查询目前如下所示:

MATCH (a { agency: "Bus", stop_id: "1234" }), (b { agency: "Train" }) 
WITH distance(a.location, b.location) AS dist, a.stop_id as orig_stop_id, b.stop_id AS dest_stop_id 
RETURN orig_stop_id,min(dist) 

location 属性是一个point 属性,这个查询实际上做了我想做的事情,除了一件事:我还想在结果中包含dest_stop_id 字段,以便我实际上可以知道哪个其他节点对应于这个最小距离,但是 Neo4j 似乎隐式聚合了 RETURN 子句中不在聚合函数内的所有字段,结果是我只得到所有对的列表(orig_stop_iddest_stop_id) 以及它们的距离与仅获得最小值和对应的 dest_stop_id。有没有办法指定结果集中哪些字段应该分组?

在 SQL 中,GROUP BY 允许您指定此项,但我在 Cypher 中找不到类似的函数。

提前致谢,如果您需要任何额外信息,请告诉我。

【问题讨论】:

    标签: neo4j cypher


    【解决方案1】:

    这应该可行:

    MATCH (a { agency: "Bus", stop_id: "1234" }), (b { agency: "Train" }) 
    RETURN
      a.stop_id AS orig_stop_id,
      REDUCE(
        s = NULL,
        d IN COLLECT({dist: distance(a.location, b.location), sid: b.stop_id}) |
        CASE WHEN s.dist < d.dist THEN s ELSE {dist: d.dist, dest_stop_id: d.sid} END
      ) AS min_data
    

    此查询使用REDUCE 来获取最小距离,同时还使用对应的dest_stop_id

    棘手的部分是CASE 子句第一次执行时,s 将是NULL。之后,s 将成为一张地图。 CASE 子句通过专门执行s.dist &lt; d.dist 测试来处理特殊的NULL 情况,如果sNULL,它将始终评估为false - 导致ELSE 子句在该测试中执行例,将s 初始化为地图。

    注意:理想情况下,您应该在查询中使用节点的标签,这样查询就不必扫描数据库中的每个节点来查找每个节点。此外,您可能需要添加适当的索引以进一步加快查询速度。

    【讨论】:

    • 这行得通,是的!谢谢。虽然有点尴尬的解决方案,因为本质上这告诉我 Neo4j 中的聚合函数非常有限,因为这样的事情是一个常见的用例。关于你笔记的主题,是的,目前所有节点都具有相同的标签,并且查询仍处于“实验”阶段,所以我并没有太担心(实际查询比我提出的要大一些),我们做到了有索引!再次感谢。
    【解决方案2】:

    似乎您可以跳过聚合函数,只订购距离并取顶部:

    MATCH (a { agency: "Bus", stop_id: "1234" }), (b { agency: "Train" }) 
    WITH distance(a.location, b.location) AS dist, a, b
    ORDER BY dist DESC
    LIMIT 1
    RETURN a.stop_id as orig_stop_id, b.stop_id AS dest_stop_id, dist
    

    正如其他人在这里提到的那样,您确实应该在此处使用标签(否则这会执行所有节点扫描以找到您的起点,这可能是您查询的主要性能瓶颈),并且有适当的索引以便您使用a 和 b 的索引查找。

    编辑

    如果你有多个起始节点时需要最近的,你可以像这样取收集元素的头部:

    MATCH (a { agency: "Bus", stop_id: "1234" }), (b { agency: "Train" }) 
    WITH distance(a.location, b.location) AS dist, a, b
    ORDER BY dist DESC
    WITH a, head(collect(b {.stop_id, dist})) as b
    RETURN a.stop_id as orig_stop_id, b.stop_id AS dest_stop_id, b.dist as dist
    

    我们确实需要将dist 包含到b 的地图投影中,否则它将与a 一起用作分组键。

    或者,您可以只收集 b 而不是地图投影,然后使用 distance() 函数重新计算每个剩余行。

    【讨论】:

    • 这不是一个解决方案,因为我希望扩展此查询以将多个最近节点返回到不同的起始节点。正如我上面提到的,这个查询仍处于“实验”阶段,所以我不太担心标签,但我知道它们的重要性,但是这篇文章的重点是 Neo4j 中聚合功能的问题和限制,不是性能。不过,感谢您的意见。
    • 我添加了一个不使用 LIMIT 的替代查询。
    • 酷!感谢您提供替代解决方案!
    【解决方案3】:

    您可以使用 COLLECT 进行聚合(注意此查询未选中):

    MATCH (a { agency: "Bus", stop_id: "1234" }), (b { agency: "Train" }) 
    WITH COLLECT (distance(a.location, b.location)) as distances, a.stop_id as stopId
    UNWIND distances as distance
    WITH min(distance) as min, stopId 
    MATCH (bus { agency: "Bus", stop_id: stopId}), (train{ agency: "Train" }) 
    WHERE distance(bus.location, train.location) = distance 
    RETURN bus,train, distance
    

    希望这会对你有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-06-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-29
      相关资源
      最近更新 更多