Is it possible to paginate an association?
joshchernoff opened this issue · comments
Tried an idea I'd hoped would work but knew would not. Still I think it conveys what I'm trying to do.
def get_tag!(tag, params \\ %{}) do
{pics_query, k} = from(p in Pic, order_by: p.inserted_at) |> Repo.paginate(params, lazy: true)
tag =
from(t in Tag, where: t.name == ^tag, preload: [pics: ^pics_query])
|> Repo.one!()
{tag, k}
end
The ideas being just as the title asks.
Thoughts?
After reading some of the source code I realized that if I forgo the repo.all in
Lines 46 to 52 in ca90a54
I can return the query which I could then just pass in my preload to achieve the effect I'm looking for.
Whats the thought about making a new function that mimics paginate
but does not fetch the records or adding an option to paginate
like lazy
where is true it will return a query vs records?
There is one thing that should be considered as noted in ectos docs.
Note: keep in mind operations like limit and offset in the preload query will affect the whole result set and not each association. For example, the query below:
comments_query = from c in Comment, order_by: c.popularity, limit: 5
Repo.all from p in Post, preload: [comments: ^comments_query]
won’t bring the top of comments per post. Rather, it will only bring the 5 top comments across all posts.
Heres my first pass on the ideas.
https://github.com/PolymorphicProductions/kerosene/commit/7d19ae0970a340e8e38c9b9ae426abce2b9a8079
The only obvious issue I see with this approach is that it requires some work to get_total_count
to address the fact that the pics in question are limited by their association to tag and thus get_total_count
will return the wrong total.
Here is my working example for using kerosene on a preload. Note this is a wip and is using my fork.
PolymorphicProductions/kerosene
def get_tag!(tag, params \\ %{}) do
down_tag = String.downcase(tag)
total_count =
from(t in "tags",
join: pt in "pic_tags",
on: pt.tag_id == t.id,
where: t.name == ^down_tag,
select: count()
)
|> Repo.one()
{pics_query, k} =
from(p in Pic, order_by: p.inserted_at)
|> Repo.paginate(params, total_count: total_count, lazy: true)
tag =
from(t in Tag, where: t.name == ^down_tag, preload: [pics: ^pics_query])
|> Repo.one!()
{tag, k}
end
I ended up having to manage my own total count since I didn't want the preload query to be ran to figure that out. Also note the use of the lazy option.