Gremlin中coalesce配合group().by()求和结果不符合预期问题
问题根源
查询结果和预期不符的核心原因是coalesce的使用方式破坏了原有聚合步骤的作用域逻辑:
- 最初不带
coalesce的查询能够正确返回[person:59],是因为sum()作为屏障型归约步骤,会在分组级别收集所有顶点经out("knows").values("age")得到的年龄值,计算全局总和。software类顶点没有关联的knows边,sum()在无输入时不会产出任何结果,group步骤会自动丢弃value为空的分组,因此最终结果只保留person组。 - 引入
coalesce后出现全0的问题,来自两处逻辑错位:coalesce是逐元素处理的非屏障步骤,会把原有的组级全局聚合拆分为逐顶点的独立处理,彻底打乱了sum()原本的聚合范围。sum()被包裹在coalesce的第一个分支内部,会导致coalesce对每个顶点单独执行一次求和逻辑,而非对整个组的所有年龄值做一次全局求和。加上sum()本身在无输入时不会产出任何值,coalesce会直接走到返回0的分支;原本能产出59的person组,也因为逐元素拆分的处理逻辑打乱了全局聚合流程,无法正确收集年龄总和,最终结果也变成了0。
正确的调整查询
要实现“有匹配年龄值时返回组内总和,无匹配时返回缺省值0”的效果,需要先固定收集所有匹配的年龄值(保证无论有没有值都能触发后续计算逻辑),再基于收集结果计算总和,空值场景返回0。
通用版本的写法(兼容所有TinkerPop 3.x版本):
g.V().group().by(__.label()).by( __.out("knows").values("age") .fold() // 收集所有匹配的年龄值为列表,无匹配时返回空列表 .coalesce( __.unfold().sum(), // 列表非空时展开计算总和 __.constant(0) // 列表为空时返回缺省值0 ) )
该查询的执行逻辑:
- person组收集到所有关联年龄值27、32,经fold形成列表后展开求和,得到59
- software组无匹配年龄值,fold后得到空列表,展开后无元素触发求和,coalesce返回缺省值0
最终返回结果为[person:59, software:0],符合预期。
如果使用TinkerPop 3.6.0及以上版本,可借助sum()支持的空输入默认值参数,用更简洁的写法实现同样效果:
g.V().group().by(__.label()).by(__.out("knows").values("age").sum(0))
内容的提问来源于stack exchange,提问作者To-om
相关产品推荐
相关产品推荐

