Docs Menu

スコープ設定

このガイドでは、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

Mongoid モデルをカスタマイズする方法の詳細については、「 データのモデル化 」ガイドを参照してください。