使用 max() 和 min() 同时保留项目
聚合函数 max() 和 min() 非常有用,但有时你会发现自己在与 Cypher 的聚合行为作斗争,而这些情况本应很简单。
当你想计算 max() 或 min()(或类似的)时,却想保留与该最大或最小值关联的项目或多个项目,这种情况经常出现。
让我们用一个非常简单的例子:一个关于人们在商店购买食品的图。
(:Person {name})-[:BOUGHT]->(:FoodItem {name})
我们想要找出每个人购买次数最多的食品项目或多个项目。
本该简单却变得复杂
我们可以轻松使用 max() 来找出他们购买次数最多的食品的计数。
MATCH (person:Person)-[:BOUGHT]->(food:FoodItem)
WITH person, food, count(food) as timesBought
RETURN person, max(timesBought) as mostBoughtCount
但我们失去了生成该结果的食品相关数据!究竟是哪种食品被购买了这么多次?是只有一种食品,还是有几种食品并列?
如果我们像下面这样保留 food 的作用域:RETURN person, food, max(timesBought) as mostBoughtCount,会得到错误的结果,因为每种食品各占一行,而 mostBoughtCount 只对应各自的食品,而不是对所有食品的聚合。
如果我们这样使用 collect():RETURN person, collect(food) as foods, max(timesBought) as mostBoughtCount,虽然 mostBoughtCount 是正确的,但我们收集了所有食品,却不知道哪一个对应这个最大值。
我们被迫放弃这种做法,改为进行排序、collect(),然后保留首个结果
MATCH (person:Person)-[:BOUGHT]->(food:FoodItem)
WITH person, food, count(food) as timesBought
ORDER BY timesBought DESC
RETURN person, collect(food)[0] as favoriteFood, max(timesBought) as mostBoughtCount
但同样的问题是并列怎么办?一个人可能有多个最受欢迎的食品并列在同一 mostBoughtCount。我们可能会花很多时间重构查询,进行多次 collect()、UNWIND、计数和比较,导致查询更加复杂。
APOC 过程帮助保持简洁
最初,我们获得了自定义过程;随后获得了自定义函数;最后我们可以编写自定义聚合函数。自 APOC 3.5.0.5 起,新增了在此类场景下非常有用的函数。
|
返回一个映射 {items:[], value:n},其中 |
同样还有 apoc.agg.minItems(),工作方式类似。
简而言之,这个函数让我们能够使用等同于 min() 或 max() 的功能,同时保留与该值关联的项目或多个项目。
如果我们把它加入查询中,就会得到以下结果
MATCH (person:Person)-[:BOUGHT]->(food:FoodItem)
WITH person, food, count(food) as timesBought
WITH person, apoc.agg.maxItems(food, timesBought) as maxData
RETURN person, maxData.items as favoriteFoods, maxData.value as mostBoughtCount
这使我们能够保留所有并列为最爱食品的项目;如果想限制并列数量,只需在函数调用时添加额外的参数即可。
此页面有帮助吗?