Hi, experienced programmer but beginner elixir user here. I’ve been working on a Phoenix app on and off for a few weeks now and have reached the point where I really need to learn the proper way to query Ecto. The problem I’m facing now is that say I have a User, and Users have A’s, and each A has multiple B’s. If I have the User.id, A.id, and B.id, how do I get that exact model from Ecto? Is it no enough to just do something like
def show(conn, %{"id" => Bid}) do
bModel = B |> Repo.one!(Bid)
render(conn, "show.json", bModel: bModel)
end
?
I’d love help with this exact problem, but also any general tips or tutorials for learning how I’d properly query Ecto.
Are these different models that are associated with the user? Just trying to get a grip on how these relate. Or are these fields defined on the User model?
Well first of all, you should complete the association in the associated model.
So:
schema "user" do
:has_many :As, Project.A
end
schema "a" do
:belongs_to: :user, Project.User
:has_many :Bs, Project.B
end
Then you should be able to preload these associations like this(note I just learned this as well so maybe some experienced people can join in here as well)
I want the B model associated with a particular A that is associated with a particular User. If I have User.id = 1, A.id = 2, and B.id = 3, how do I get the B model with id 3 from Ecto? From what I’ve followed in tutorials, this is how I would get the A
def show(conn, %{"id" => a_id}) do
current_user = Guardian.Plug.current_resource(conn)
a = current_user
|> assoc(:As)
|> Repo.get(a_id)
...
end
And this would get me the A model with whatever ID I gave it. But what if I need to go one level deeper and get a certain B associated with a certain A associated with a certain User? I may just have a flawed understanding of how some of this works.
You could just join B to User through A via a query and preload B’s that way, efficient if there are few B’s per User, otherwise a secondary query may be faster.
Important question: Are you already familiar with SQL or no? Further answers to your question depend upon this because we need to know if we’re merely explaining how you do a particular SQL pattern in ecto, or if we need to explain the relevant SQL patterns as well.
Similar to how you would do it in Sql, something like (I’m on phone so please excuse oddities ^.^):
from u in User,
join: a in A, on: a.user_id == u.id,
join: b in B, on: b.a_id == a.id,
select: {u, b}
Though if you have B’s in A’s multiple times you may get duplicate B’s but you can fix that be adding something like group_by [u.id, b.id] in there too or something like that if I have the syntax right.
Ahh, I figured there would be some fancy shmancy Ecto way of doing it with more abstraction. I guess I’ll go read the Query docs all the way through to make more sense of all of this. These magical macros are the one part about Elixir that is frustrating to learn. I’m never sure what’s part of the language and what’s magic added from some macro.
It is not actually magical, if you look at the source things the query syntax just builds and mutates a simple record of data, of which is passed to, say, the PostgreSQL adaptor, which then takes the data in that record to build a query. It is actually remarkably simple.