如何将预加载User的user_id传入Ecto预加载子查询?
Ecto预加载时传递User ID到子查询解决方案
问题场景与代码
我有三个数据表:Tasks、Users、Reviews。Users负责处理Tasks,并会因工作表现获得Reviews,Reviews针对Users而非Tasks(评价的是用户的工艺水平)。编写的查询代码如下:
rq = from r in Review, where: r.module_type == "users", where: r.module_id == ^user_id, group_by: r.module_id, select: avg(r.stars) t = Repo.get_by(Task, id: task_id) |> Repo.preload(user: [reviews: rq])
运行时报错:undefined function user_id/0,需要实现将预加载得到的User的user_id传入子查询rq,且知道可以用元组配置预加载细节但不知具体操作。
错误原因
定义子查询rq时直接引用^user_id,但此时user_id并非提前存在的变量——它是从Task关联的User中动态获取的,硬编码外部变量会导致变量未定义错误。
解决方案
使用Ecto预加载的元组配置形式,通过父级绑定动态获取User的ID,替代硬编码的外部变量:
方式1:单独定义子查询并指定结果键名
# 子查询中用^parent.id引用预加载的父级User的ID rq = from r in Review, where: r.module_type == "users", where: r.module_id == ^parent.id, group_by: r.module_id, select: avg(r.stars) t = Repo.get_by(Task, id: task_id) # 用元组{子查询, 结果键名}给预加载结果命名,方便后续访问 |> Repo.preload(user: [reviews: {rq, :avg_stars}])
后续可通过task.user.avg_stars直接获取该用户的平均评分。
方式2:单独定义子查询不重命名
如果不需要给结果重命名,直接传递子查询即可:
rq = from r in Review, where: r.module_type == "users", where: r.module_id == ^parent.id, group_by: r.module_id, select: avg(r.stars) t = Repo.get_by(Task, id: task_id) |> Repo.preload(user: [reviews: rq])
方式3:内联子查询
直接在预加载中编写子查询,简化代码结构:
t = Repo.get_by(Task, id: task_id) |> Repo.preload( user: [ reviews: from r in Review, where: r.module_type == "users", where: r.module_id == ^parent.id, group_by: r.module_id, select: avg(r.stars) ] )
核心逻辑:^parent.id是Ecto预加载的特殊绑定,代表当前被预加载的父级记录(即User)的id字段,能动态将每个User的ID传入子查询,计算对应用户的平均评分。
内容的提问来源于stack exchange,提问作者sooon
相关产品推荐
相关产品推荐

