Reputation: 304
Are there any ways to preload records by selecting another joined columns?
# table structure
# User 1---* Post 1---* PostTag *---1 Tag
# extract definition of scheme
scheme "posts" do
...
has_many :post_tags, PostTag
has_many :tags, [:post_tags, :tag]
end
Following pseudo-code expresses my goal(but not work).
query = from post in Post,
join: user in User, on post.user_id == user.id,
select: %{
id: post.id,
title: post.title,
user_name: user.name, # <= column at joined table
},
preload: [:tags]
Repo.all(query)
#=> ** (Ecto.QueryError) the binding used in `from` must be selected in `select` when using `preload` in query:`
I expect the result like this.
[
%{id: 1, title: "AAA", user_name: "John", tags: [%{name: "elixir"},...]},
%{id: 2, title: "BBB", user_name: "Mike", tags: [%{name: "erlang"},...]},
...
]
Upvotes: 17
Views: 6981
Reputation: 51429
As the error message says, you need to select the binding you gave in from
when you are preloading, otherwise Ecto has no place to put the preloaded tags. Here is a simple answer:
query = from post in Post,
join: user in User, on: post.user_id == user.id,
select: {post, user.name},
preload: [:tags]
By returning a tuple, you can have the full post and the user.name on the side. Another approach is to return both post and users as full structs:
query = from post in Post,
join: user in User, on: post.user_id == user.id,
preload: [:tags, user: user]
or if you don't want all fields:
query = from post in Post,
join: user in User, on: post.user_id == user.id,
preload: [:tags, user: user],
select: [:id, :title, :user_id, user: [:name]]
Upvotes: 23