そのタイトルは少し鈍いので、ここに例を示します。Ship
モデル、Pirate
、およびを含む Rails 3 アプリがあるとしParrot
ます。船にはたくさんの海賊がいて、海賊にはたくさんのオウムがいます。
Ship.includes(pirates: :parrots).where('parrots.name LIKE ?', '%polly%')
これは、「ポリー」のような名前のオウムが少なくとも 1 匹いる海賊が少なくとも 1 匹いる船を返します。また、それらの船のすべての海賊とオウムを熱心にロードしたいのですが...実際には、一致するオウムを持つ海賊だけが熱心にロードされ、その中で一致するオウムだけが熱心にロードされます。生成された SQL は次のようなものです。
SELECT ships.id AS t0_r0, ships.name AS t0_r1, pirates.id AS t1_r0, pirates.name AS t1_r1, parrots.id AS t2_r0, parrots.name AS t2_r1 FROM ships LEFT OUTER JOIN pirates ON pirates.ship_id = ships.id LEFT OUTER JOIN parrots ON parrots.pirate_id = pirates.id WHERE (parrots.name LIKE '%polly%')
条件なしで実行Ship.includes(pirates: :parrots)
すると、ActiveRecord は、私が望むものにいくらか近いクエリのバンドルを生成します。
SELECT ships.* FROM ships
SELECT pirates.* FROM pirates WHERE pirates.ship_id IN (ship IDs from previous query)
SELECT parrots.* FROM parrots WHERE parrots.pirate_id IN (pirate IDs from previous query)
最初の例の SQL を使用するように最初のクエリを何らかの方法で変更できれば、まさに私が望んでいることを実行できます。
SELECT ships.* FROM ships LEFT OUTER JOIN pirates ON pirates.ship_id = ships.id LEFT OUTER JOIN parrots ON parrots.pirate_id = pirates.id WHERE (parrots.name LIKE '%polly%')
SELECT pirates.* FROM pirates WHERE pirates.ship_id IN (ship IDs from previous query)
SELECT parrots.* FROM parrots WHERE parrots.pirate_id IN (pirate IDs from previous query)
しかし、ActiveRecordにこれをさせる方法、または自分でそれを行い、熱心な読み込みを「手動で」接続する方法を知りません(私の状況では、N + 1クエリの爆発を避けるために必要です)。アイデアやアドバイスをいただければ幸いです。