Overview
このガイドでは、Mongoid モデルにスコープを実装する方法を学習できます。スコープは、共通のフィルター条件を再利用する便利な方法を提供します。フィルタ条件の作成の詳細については、「 ドキュメントクエリの指定 」ガイドを参照してください。
ほとんどのクエリに同じ基準を適用する場合は、繰り返しのコードを減らすためにアプリケーションにスコープを実装できます。
名前付きスコープ
名前付きスコープは、指定された名前によって参照される、クラスのロード時に定義される基準です。 フィルター条件と同様に、これらは遅延読み込みされ、連鎖可能です。
この例では、次の名前付きスコープを含む Band モデルを定義します。
japanese:countryフィールドの値が"Japan"であるドキュメントに一致しますrock:genreフィールドの値に"rock"が含まれるドキュメントと一致します
class Band include Mongoid::Document field :country, type: String field :genres, type: Array scope :japanese, ->{ where(country: "Japan") } scope :rock, ->{ where(:genres.in => [ "rock" ]) } end
次に、名前付きスコープを使用してクエリを実行できます。 次のクエリでは、名前付きスコープを使用して、countryフィールドの値が "Japan" で、かつ genreフィールドの値に "rock" が含まれるドキュメントを一致させます。
Band.japanese.rock
高度なスコープ設定
名前付きスコープで Proc オブジェクトとブロックを定義すると、パラメータを受け入れ、機能を拡張できます。
この例では、 based_in スコープを含む Band モデルを定義します。これは、countryフィールド値がパラメータとして渡される指定された値であるドキュメントに一致します。
class Band include Mongoid::Document field :name, type: String field :country, type: String scope :based_in, ->(country){ where(country: country) } end
次に、次のコードに示すように、based_in スコープを使用してクエリを実行できます。
Band.based_in("Spain")
Mongoid では、次の例に示すように、既存のクラスメソッドをシャドウするスコープを定義できます。
class Band include Mongoid::Document def self.on_tour true end scope :on_tour, ->{ where(on_tour: true) } end
scope_overwrite_exception 構成オプションを true に設定することで、スコープが既存のクラスメソッドを上書きするときに Mongoid にエラーを発生させるように指示できます。
この設定の詳細については、 アプリケーション構成ガイド を参照してください。
デフォルトのスコープ
デフォルトのスコープは、ほとんどのクエリに同じ基準を適用する場合に役立ちます。 デフォルトのスコープを定義することで、モデルを使用するすべてのクエリのデフォルトとしてこれらの基準を指定します。 デフォルトのスコープでは Criteria オブジェクトが返されます。
デフォルトのスコープを作成するには、 モデルクラスで default_scope メソッドを定義する必要があります。
次のコードでは、Band モデルの default_scope メソッドを定義し、activeフィールド値が true であるドキュメントのみを検索します。
class Band include Mongoid::Document field :name, type: String field :active, type: Boolean default_scope -> { where(active: true) } end
次に、Band 上のすべてのクエリは、active 値が true であるドキュメントを事前にフィルタリングします。
フィールドの初期化
デフォルトスコープを指定すると、新しいモデルのフィールドは、それらの値がブール値値や整数などのリテラルである場合、デフォルトスコープで指定された値に初期化されます。
注意
フィールドとスコープの競合
フィールド定義とデフォルトスコープにデフォルト値を指定すると、次の例に示すように、デフォルトスコープの値が優先されます。
class Band include Mongoid::Document field :name, type: String field :on_tour, type: Boolean, default: true default_scope ->{ where(on_tour: false) } end # Creates a new Band instance in which "on_tour" is "false" Band.new
デフォルトスコープ内のネストされたフィールドを参照ために、ドット表記を使用することは推奨されません。 これにより、Mongoid は新しいモデル内の予期しないフィールドを初期化するように指示する可能性があります。
例、tour.yearフィールド を参照するデフォルトのスコープを定義すると、新しいモデルは、yearフィールド を含むネストされたオブジェクトを持つ tourフィールドではなく、フィールドtour.year で初期化されます。
をクエリする場合、Mongoid はドット表記を正しく解釈し、ネストされたフィールドが指定された値を持つドキュメントを一致させます。
関連付け
関連付けの一部であるモデルにデフォルトのスコープを定義する場合、スコープを再適用するには関連付けを再ロードする必要があります。 これは、スコープが適用されたときに可視性に影響する関連付け内のドキュメントの値を変更する場合に必要です。
この例では、次のモデルを使用します。
class Label include Mongoid::Document field :name, type: String embeds_many :bands end class Band include Mongoid::Document field :name, type: String field :active, default: true embedded_in :label default_scope ->{ where(active: true) } end
たとえば、active の値が true である Band への関連付けを含む Label モデルを作成するとします。 activeフィールドを false に更新しても、Mongoid はデフォルトのスコープに関係なく、それを引き続きロードします。 スコープが適用された関連付けられているドキュメントを表示するには、reload 演算子を呼び出す必要があります。
次のコードは、このシーケンスを示しています。
label = Label.new(name: "Hello World Records") band = Band.new(name: "Ghost Mountain") label.bands.push(band) label.bands # Displays the Band because "active" is "true" band.update_attribute(:active, false) # Updates "active" to "false" # Displays the "Ghost Mountain" band label.bands # => {"_id":"...","name":"Ghost Mountain",...} # Won't display "Ghost Mountain" band after reloading label.reload.bands # => nil
または ではないクエリ動作
Mongoid は、デフォルトスコープ内の条件を他のクエリ条件と同じ方法で扱います。 これにより、or メソッドと nor メソッドを使用すると、予期しない動作を引き起こす可能性があります。
次の例は、Mongoid がデフォルトのスコープを持つモデルに対するクエリを解釈する方法を示しています。
class Band include Mongoid::Document field :name field :touring field :member_count default_scope ->{ where(touring: true) } end # Combines the condition to the default scope with "and" Band.where(name: 'Infected Mushroom') # Interpreted query: # {"touring"=>true, "name"=>"Infected Mushroom"} # Combines the first condition to the default scope with "and" Band.where(name: 'Infected Mushroom').or(member_count: 3) # Interpreted query: # {"$or"=>[{"touring"=>true, "name"=>"Infected Mushroom"}, {"member_count"=>3}]} # Combines the condition to the default scope with "or" Band.or(member_count: 3) # Interpreted query: # {"$or"=>[{"touring"=>true}, {"member_count"=>3}]}
クエリ時にスコープを無効化
次の例えに示すように、unscoped 演算子を使用して、Mongoid にデフォルトのスコープを適用しないように指示することができます。
# Inline example Band.unscoped.where(name: "Depeche Mode") # Block example Band.unscoped do Band.where(name: "Depeche Mode") end
実行時にデフォルトのスコープを上書き
実行時にブロック内のデフォルトのスコープを変更するには、 with_scope メソッドを使用します。
次のモデルは、名前付きスコープmexican を定義します。
class Band include Mongoid::Document field :country, type: String field :genres, type: Array scope :mexican, ->{ where(country: "Mexico") } end
次のコードに示すように、with_scope メソッドを使用して、mexican という名前のスコープをデフォルトのスコープとして実行時に設定できます。
Band.with_scope(Band.mexican) do Band.all end
クラスメソッド
Mongoid は、Criteria オブジェクトを返すクラスメソッドをスコープとして扱います。 次の例に示すように、これらのクラスメソッドを使用してクエリを実行できます。
class Band include Mongoid::Document field :name, type: String field :touring, type: Boolean, default: true def self.touring where(touring: true) end end Band.touring