Neo4j(Cypher)-可以使用非隐式聚合吗?

时间:2018-07-03 15:02:54

标签: neo4j cypher

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

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

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中找到类似的功能。

在此先感谢您是否需要其他信息。

3 个答案:

答案 0 :(得分: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子句通过专门执行NULL测试来处理特殊的s.dist < d.dist情况,如果falses,它将始终评估为NULL- -在这种情况下,执行ELSE子句,将s初始化为地图。

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

答案 1 :(得分:1)

好像您可以跳过聚合函数,而只是排序距离并获得顶部:

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()函数对剩余的每一行进行重新计算。

答案 2 :(得分:0)

您可以使用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

希望这会对您有所帮助。