ecto:在虚拟字段上具有计数查询的动态order_by

时间:2018-12-31 14:37:01

标签: elixir ecto

我想通过处理来建立具有动态顺序的查询。 基本的排序示例id +名称(见下文)可以正常工作。排序变量已插入查询中。

favorites_count_query = from(f in Favorite,
  select: %{wallpaper_id: f.wallpaper_id, count: count(f.user_id)},
  group_by: f.wallpaper_id)

...

sort = [asc: :id, asc: :name] # comes from request

from(w in Wallpaper,
  left_join: f in subquery(favorites_count_query), on: f.wallpaper_id == w.id,
  left_join: d in subquery(downloads_count_query), on: d.wallpaper_id == w.id,
  select: w,
  select_merge: %{favorites_count: coalesce(f.count, 0)},
  select_merge: %{downloads_count: coalesce(d.count, 0)},
  order_by: ^sort,
  group_by: w.id)

此外,我想对计数字段(favorites_countdownloads_count)进行排序。将这部分放在查询中将是静态的。

...
order_by: coalesce(d.count, 0),

我现在的问题是如何像第一个使用动态变量的示例那样处理它。我没有找到解决这个问题的方法。

sort = [asc: :favorites_count, asc: :name] # comes from request
...
order_by: ^sort,

最后以Unknown column 'w0.favorites_count' in 'order clause'

order_by: ^foo(sort),

def foo(sort), do: coalesce(:count, 0)

最后以undefined function coalesce/2 (fragment/3)

使用dynamic/2expected a field as an atom, a list or keyword list in 'order_by', got: 'dynamic(...)'结尾

我了解主要问题,但没有找到解决方案。有什么想法或最好的解决方案吗?感谢您的帮助!

2 个答案:

答案 0 :(得分:1)

解决方案是多次调用order_by/3作为函数,而不是生成列表并将其直接放在from/2中。

sort = [asc: :favorites_count, asc: :name] # comes from request

from(w in Wallpaper, ...)
|> order_wallpapers(sort)

...

defp order_wallpapers(query, []), do: query

defp order_wallpapers(query, [{order, :favorites_count} = sort|others]) do
  query
  |> order_by([w, f, d], [{^order, f.count}])
  |> order_wallpapers(others)
end

defp order_wallpapers(query, [sort|others]) do
  query
  |> order_by([w, f, d], ^[sort])
  |> order_wallpapers(others)
end

答案 1 :(得分:-1)

我设法简化了很多,所以这是我的解决方案:

from(p in Post,
  order_by: [desc_nulls_last: fragment("array_length(?, 1)", p.votes)]
)
|> Repo.all()

您可以选择desc_nulls_last,但该片段效果很好。 array_length是PostgreSQL特有的,所以要当心!