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"},...]},
...
]
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]]
来源:https://stackoverflow.com/questions/48557128/ecto-how-to-preload-records-with-selecting-another-joined-columns