Preload associations being missed, nested self-reference preload, works with single id

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… :frowning: ). 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).