知识库

使用 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 是针对每个食物的,而不是在所有食物中汇总的。

如果我们像这样收集 foodRETURN person, collect(food) as foods, max(timesBought) as mostBoughtCount,虽然 mostBoughtCount 是正确的,但我们收集了所有食物,而且不知道哪个与该最大值相关联。

我们被迫放弃这种方法,而是执行排序、收集,然后保留最上面的结果。

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 开始,添加了新的函数来帮助解决这些情况。

apoc.agg.maxItems(item, value, groupLimit: -1)

返回一个映射 {items:[], value:n},其中 value 是存在的最大值,而 items 是所有具有相同值的项目。项目数量可以选择限制。

还有一个类似工作的 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

这让我们可以保留所有并列为最喜欢的食物,如果我们想限制并列,我们可以将其作为函数调用中的一个额外参数添加。