我的问题很简单。我一直在尝试编写一个使用聚合函数-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_id
,dest_stop_id
)中的距离以及它们之间的距离与获得最小值和相应的dest_stop_id
的关系。有什么方法可以指定应在结果集中将哪些字段分组?
在SQL中,GROUP BY
允许您指定它,但我无法在Cypher中找到类似的功能。
在此先感谢您是否需要其他信息。
答案 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
情况,如果false
为s
,它将始终评估为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
希望这会对您有所帮助。