说我有以下Ecto型号:
ForumCategory
,has_many :forums
Forum
,belongs_to :forum_category
,has_many :forum_topics
ForumTopic
,belongs_to :forum
然后我要加载ForumCategory
的所有条目及其预先加载的所有关联Forum
条目和每条条目的最新ForumTopic
:
ForumCategory
- > Forum
- > ForumTopic
论坛类别的预加载论坛很简单:
ForumCategory
|> preload(:forums)
现在我们还想预加载最新的论坛主题,但我们只想为每个论坛预加载最新的主题,因此我们不会从数据库加载整个论坛主题列表(将会有很多人)
forum_topics_query = ForumTopic
|> order_by([desc: :inserted_at])
|> limit(1)
ForumCategory
|> preload([forums: [forum_topics: ^forum_topics_query]])
|> Repo.all
这似乎乍一看似乎很有效,但很快就会发现这个查询只返回一个论坛主题,而不是每个论坛一个,而是一个论坛主题,从实际的SQL查询中也可以看出执行:
SELECT f0."id", f0."title", f0."body", f0."pinned", f0."forum_replies_count", f0."deleted_at", f0."inserted_at", f0."updated_at", f0."forum_id", f0."user_id", f0."forum_id"
FROM "forum_topics" AS f0 WHERE (f0."forum_id" = ANY($1))
ORDER BY f0."forum_id", f0."inserted_at" DESC
LIMIT 1
所以我的问题是,如何使Ecto预加载不仅仅是一个记录,而是每个论坛类别预先加载一个论坛?
答案 0 :(得分:0)
我最终得到了这个使用连接和窗口函数组合的解决方案:
defmodule MyApp.ForumTopic do
def latest_over_forum(per \\ 1) do
from outer in __MODULE__,
join: inner in fragment("""
SELECT *, row_number() OVER (
PARTITION BY forum_id
ORDER BY inserted_at DESC
) FROM forum_topics
"""),
where: inner.row_number <= ^per and inner.id == outer.id
end
end
forum_topics_query = ForumTopic |> ForumTopic.latest_over_forum
ForumCategory
|> preload([forums: [forum_topics: ^forum_topics_query]])
|> Repo.all