8

モデルオブジェクトをその関連付けとともにロードしたいと思っています:

user= User.includes(:posts).find(1)

しかし、コードの特定のポイントで、次のようなことをしたいと思います。

user.posts.where(:topic => "x")

しかし、それはクエリを再実行するだけです。だから代わりに私はこれをやろうと思った:

user.posts.select{|post| post.topic == "x" }

それはクエリを再実行しません。しかし、いくつか質問があります。

まず、これは正しい方法ですか?

次に、この場合の select の動作について少し混乱しています。includes関数を使用していない場合でも最後の行を実行すると、最初にクエリが実行され、その後再度実行すると実行されないため、何らかのキャッシュが含まれていますか? where句を使用すると、毎回クエリが実行されるためです。

ありがとうございました。

4

1 に答える 1

17

selectEnumerable の Ruby メソッドです。初めて走る時

user.posts.select{|post| post.topic == "x" }

の関連付けのすべてのPostインスタンスがデータベースからメモリにロードされます。具体的には ActiveRecord コレクション オブジェクトに。次に、このコレクションに対して が呼び出され、以外の属性を持つコレクション内のすべてのインスタンスが除外されます。user:postsselectPost:topic"x"

上記の行を 2 回実行しても、データベースは再度クエリされません。これは、既にpostsメモリに読み込まれているためです。


以下のincludesようにすると

user= User.includes(:posts).find(1)

:posts返された各Userインスタンス (この場合、単一のUserwhere :idis )のリレーションを熱心にロードします1Post上記の前のセクションで説明したように、すべてのインスタンスがメモリにロードされました。

その後、次のようなことをすると

user.posts.where(:topic => "x")

に対して新しいクエリを実行するように Rails に指示していますPost。今回は、 の where と where が のすべてのインスタンスを検索しPostます。インメモリ ARel コレクションの「フィルター」のようには機能しません。:topic"x":user_id:iduserwhere


  • 悪い別のクエリを避けたい場合selectは、メモリ内の結果セットをフィルタリングする良い方法です。
  • ベンチマークを実行して、どちらが速いかを見つけることができます。
    1. データベースにクエリを実行し、別の ARel コレクションをメモリに再設定する
    2. メモリ内の enumberable を反復処理し、Ruby でフィルターを実行する
  • に関連するすべてが必要ない場合は、これを元のクエリに簡単に含めることができます:postsuser

    user.includes(:posts).where("posts.topic = ?", 'x')
    
于 2013-03-20T12:51:50.143 に答える