This is a sort of convoluted model that I didn’t have much control over. I’ll try to explain in words what’s happening and then provide a distilled-down example of the code.
I have a model, A, with a many-to-many relationship to another model, B. Model B has a many-to-many self-referencing relationship with itself (I didn’t design the schema… ). I also have a “bulk loading” query that, given several ids for A, should load those As and preload their Bs and the child Bs. This works fine if I give the query a single A id, but if I give it multiple ids, the query results only have one of the As associations loaded.
The models look like this:
defmodule A do
use MyApp.Web, :model
schema "a" do
many_to_many(:bs, B, join_through: "a_bs")
end
end
defmodule B do
use MyApp.Web, :model
schema "b" do
field :parent_id, :integer
has_many(:children, B, foreign_key: :parent_id)
end
end
defmodule A.Query do
import Ecto.Query
alias MyApp.Repo
# there's some subtlety here in how my actual (much more complex) query is written that I'm
# trying to preserve
def bulk_load(ids) do
as_query()
|> where([a], a.id in ^ids)
|> Repo.all
|> Repo.preload([
bs: bs_query()
])
end
defp as_query do
from a in A, select struct(a, [:id])
end
def bs_query do
from b in B,
left_join: children in assoc(b, :children),
preload: [children: children],
select: struct([:id, {:children, [:id]}])
end
end
Then, supposing I have two A
with ids 1 and 2, both associated with a B
having id 1 and no children, The results look like:
# individual queries work as expected
A.Query.bulk_load([1])
=> [%A{id: 1, bs: [%B{id: 1, children: []}]}]
A.Query.bulk_load([2])
=> [%A{id: 2, bs: [%B{id: 1, children: []}]}]
# query with multiple ids only populates bs in one of the results
A.Query.bulk_load([1, 2])
=> [%A{id: 1, bs: [%B{id: 1, children: []}]}, %A{id: 2, bs: []}] # note the second bs is empty
# expected result
A.Query.bulk_load([1, 2])
=> [%A{id: 1, bs: [%B{id: 1, children: []}]}, %A{id: 2, bs: [%B{id: 1, children: []]}]
I have some other models that I am preloading with the same framework that work perfectly, including preloading of nested associations. It seems to be the self-reference that jams things up. If I remove the preloading of :children
, then the queries work as expected (except without the child data).