Reputation: 3
I have a Post schema, which has_many Comment. I want to list all posts with comments on one page (I'll use pagination separately), using single db query, with following limits:
First: I want to limit preloading to 3 latest comments per post (not all of them, as there might be hundreds potentially).
Second: I want to preload only title column from the comment, and avoid 'text' column, as the text might potentially contain too much content.
My final result would be:
Post 1 ──┬── Comment 1 Title
│── Comment 2 Title
└── Comment 3 Title
Post 2 ──┬── Comment 1 Title
│── Comment 2 Title
└── Comment 3 Title
Post 3 ──┬── Comment 1 Title
│── Comment 2 Title
└── Comment 3 Title
...(etc)
Whatever combination I have tried, I fail to limit number of nested comments per post (my limits always limit total number of comments, rather than on per-post basis). Also my selects fail to load title only from the comments. If anyone with experience has any inputs, it would be more than appreciated.
PS: it is already suggested above, but for more clarification, here's my model:
schema "posts" do
field :title, :string
field :slug, :string
field :active, :boolean, default: true
has_many :comments, App.Comment
timestamps()
end
schema "comments" do
field :title, :string
field :body, :string
field :email, :string
field :meta, :map
field :active, :boolean, default: false
belongs_to :post, App.Post
timestamps()
end
PPS: to be even more specific, I was wondering if it is possible to have nested limits, in same manner as nested preloads:
query = from Post, preload: [:file, :image, {:comments, [:user, :icon]}], limit: [10, {:comments: 3}]
That preload will preload nested user and icon column in comments, but limit obviously does not work for nested records.
Upvotes: 0
Views: 602
Reputation: 15736
There is a "pseudo" version of what you could do since I don't have time to actually check the code but it will get you started.
There are alternatives so it's up to you how to go in this situation.
First you create a view somehow like this:
create view posts_with_last_comments as
select
p.*,
(select array_agg(title) from comments where post_id = p.id order by inserted_at limit 3) as last_titles
from
posts p
and then in your app you do the following:
query = from p in "posts_with_comments"
posts = MyApp.Repo.all(query)
Please also mind that I tried to use postgresql syntax, which may change for other DBs.
Upvotes: 1
Reputation: 5804
Didn't test this, but i think it is possible to use a query when preloading. However i'm a bit uncertain on the syntax.
query = from Post, preload: [:file, :image, {:comments, from(c in Comment, limit: 10), [:user, :icon]}], limit: 10
Have a look at the third example here. https://hexdocs.pm/ecto/Ecto.Repo.html#c:preload/3
Upvotes: 0