You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Enum.reduce中Select Merge未按预期工作的技术求助

解决Ecto聚合查询无法同时保留avg和count的问题

嘿,我一眼就看出问题所在了——你在内层循环里每次都重新初始化了查询对象,导致每次处理新的聚合函数时,之前已经构建好的查询都会被覆盖,最后只剩下最后一次的操作结果。

问题根源

你原来的代码里,每次进入内层Enum.reduce时都执行了query = from p in model, select: %{},这相当于每次处理$avg或$count时都从头开始创建查询,而不是在之前的基础上扩展。

修复后的代码

我们只需要把初始查询移到循环外面,让所有聚合操作都基于同一个查询对象累加构建就行:

# 先创建一次基础查询,后续所有操作都基于它扩展
initial_query = from p in model, select: %{}

Enum.reduce(select, initial_query, fn {_top_key, agg_funcs}, current_query ->
  Enum.reduce(agg_funcs, current_query, fn {agg_type, _fields}, query ->
    case agg_type do
      "$avg" ->
        from p in query, select_merge: %{avg: avg(p.id)}
      "$count" ->
        from p in query, select_merge: %{count: count(p.pass)}
      # 要是以后需要加$sum之类的聚合,直接在这里加分支就行
      _ -> query
    end
  end)
end)

为什么这样改就对了?

  1. 初始查询仅创建一次:把initial_query放在所有循环外面,确保后续的select_merge都是在同一个查询基础上添加字段。
  2. 累加式构建查询:两层Enum.reduce都把当前的查询对象作为累加器,每次处理聚合函数时,都会把新的聚合字段合并到已有的查询结果里,而不是重置。
  3. 代码更易维护:用case代替多个if判断,逻辑更清晰,以后要加其他聚合类型也很方便。

验证效果

修改后执行流程会变成:

  1. 处理$avg后,得到查询:#Ecto.Query<from m in Model, select: %{avg: avg(m.id)}>
  2. 处理$count时,基于上面的查询继续select_merge,最终得到:#Ecto.Query<from h in Model, select: %{avg: avg(h.id), count: count(h.pass)}>

完全符合你想要的结果!

内容的提问来源于stack exchange,提问作者script

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:46:42