Aには多くのBがあり、Bには多くのCがあります。C には と呼ばれるプロパティがありますthing
。
class A < ActiveRecord::Base
has_many :bs
end
class B < ActiveRecord::Base
belongs_to :a
has_many :cs
end
class C < ActiveRecord::Base
belongs_to :b
attr_accessible :thing
end
A に属するすべての B を照会し、その B に属する C を熱心にロードしたいと思います。
> a = A.first
A Load (0.2ms) SELECT "as".* FROM "as" LIMIT 1
=> #<A id: 1, created_at: "2012-08-21 09:25:18", updated_at: "2012-08-21 09:25:18">
> bs = a.bs.includes(:cs)
B Load (0.2ms) SELECT "bs".* FROM "bs" WHERE "bs"."a_id" = 1
C Load (0.1ms) SELECT "cs".* FROM "cs" WHERE "cs"."b_id" IN (1)
=> [#<B id: 1, a_id: 1, created_at: "2012-08-21 09:25:22", updated_at: "2012-08-21 09:25:22", thing: nil>]
>
これはうまくいきます:
> bs[0]
=> #<B id: 1, a_id: 1, created_at: "2012-08-21 09:25:22", updated_at: "2012-08-21 09:25:22", thing: nil>
> bs[0].cs
=> [#<C id: 1, b_id: 1, thing: 2, created_at: "2012-08-21 09:29:31", updated_at: "2012-08-21 09:29:31">]
>
—ただし、後でwhere()
B インスタンスに属する C で検索を実行する場合は除きます。
> bs[0].cs.where(:thing => 1)
C Load (0.2ms) SELECT "cs".* FROM "cs" WHERE "cs"."b_id" = 1 AND "cs"."thing" = 1
=> []
> bs[0].cs.where(:thing => 2)
C Load (0.2ms) SELECT "cs".* FROM "cs" WHERE "cs"."b_id" = 1 AND "cs"."thing" = 2
=> [#<C id: 1, b_id: 1, thing: 2, created_at: "2012-08-21 09:29:31", updated_at: "2012-08-21 09:29:31">]
>
利用可能な情報があるにもかかわらず、クエリが再発行されることに注意してください。
もちろん、私はただ使うことができますEnumerable#select
:
> bs[0].cs.select {|c| c.thing == 2}
=> [#<C id: 1, b_id: 1, thing: 2, created_at: "2012-08-21 09:29:31", updated_at: "2012-08-21 09:29:31">]
>
これにより再クエリを回避できますが、Rails 自体にも同様のことができることを期待していました。
本当の欠点は、アソシエーションが熱心にロードされているかどうかがわからない場所でこのコードを使用したいということです。そうでない場合、select
メソッドはフィルタを実行する前に B に対してすべての C をロードしますが、where
メソッドはより小さなデータ セットを取得するために SQL を生成します。
これが問題であるとはまったく確信していませんが、熱心な読み込みについて何か見逃していることがあれば、ぜひ聞いてみたいです.