Docs Menu
Docs Home
/ / /
Mongoid
/

クエリ

項目一覧

  • 条件構文
  • フィールド構文
  • MQL 構文
  • シンボル演算子構文
  • フィールド
  • 定義済みフィールドのクエリ
  • Raw Values のクエリ
  • フィールド エイリアス
  • 埋め込みドキュメント
  • 論理演算
  • 演算子の組み合わせ
  • and 動作
  • any_of 動作
  • none_of 動作
  • not 動作
  • 増分クエリ構築
  • 合併戦略
  • サポートされている演算子メソッド
  • 演算子値の拡張
  • クエリメソッド
  • elem_match
  • プロジェクション
  • only
  • without
  • 順序
  • ページ分割
  • limit
  • skip
  • batch_size
  • 検索方法 _id
  • 追加のクエリメソッド
  • 積載量
  • 正規表現
  • フィールドの条件
  • スコープ設定
  • 名前付きスコープ
  • デフォルトのスコープ
  • ランタイムのデフォルトスコープの上書き
  • クラスメソッド
  • クエリ + 永続性
  • クエリ キャッシュ
  • クエリキャッシュの有効化
  • クエリキャッシュの自動有効化
  • クエリキャッシュの手動有効化
  • 結果をキャッシュする #first
  • 非同期クエリ
  • 非同期クエリ実行の構成

Mongoid は、ActiveRecord に触発された豊富なクエリ DSL を提供します。簡単なクエリは次のようになります。

Band.where(name: "Depeche Mode")

さまざまな Mongoid 機能を活用したより複雑なクエリは次のようになります。

Band.
where(:founded.gte => "1980-01-01").
in(name: [ "Tool", "Deftones" ]).
union.
in(name: [ "Melvins" ])

クエリ メソッドは、MongoDB クエリ言語 (MQL) の連鎖可能で遅延評価されるラッパーである Mongoid::Criteria オブジェクトを返します。クエリは、結果セットが反復されるときに実行されます。例:

# Construct a Criteria object:
Band.where(name: 'Deftones')
# => #<Mongoid::Criteria
# selector: {"name"=>"Deftones"}
# options: {}
# class: Band
# embedded: false>
# Evaluate the query and get matching documents:
Band.where(name: 'Deftones').to_a
# => [#<Band _id: 5ebdeddfe1b83265a376a760, name: "Deftones", description: nil>]

firstlast などのメソッドは、個々のドキュメントをすぐに返します。それ以外の場合は、eachmap などのメソッドを使用して Criteria オブジェクトを反復処理すると、サーバーからドキュメントが検索されます。to_a は、ドキュメントの配列を返すクエリを強制的に実行するために使用でき、文字通り Criteria オブジェクトを配列に変換します。

クエリ メソッドが Criteria インスタンスで呼び出されると、メソッドは既存の条件に新しい条件が追加された新しい Criteria インスタンスを返します。

scope = Band.where(:founded.gte => "1980-01-01")
# => #<Mongoid::Criteria
# selector: {"founded"=>{"$gte"=>"1980-01-01"}}
# options: {}
# class: Band
# embedded: false>
scope.where(:founded.lte => "2020-01-01")
# => #<Mongoid::Criteria
# selector: {"founded"=>{"$gte"=>"1980-01-01", "$lte"=>"2020-01-01"}}
# options: {}
# class: Band
# embedded: false>
scope
# => #<Mongoid::Criteria
# selector: {"founded"=>{"$gte"=>"1980-01-01"}}
# options: {}
# class: Band
# embedded: false>

Mongoid は、個々の条件を指定する 3 つの方法をサポートしています。

  1. フィールド構文。

  2. MQL構文。

  3. シンボル演算子の構文。

すべての構文は、ドット表記を使用した埋め込みドキュメントのクエリをサポートしています。すべての構文は、クエリ対象のフィールドがモデル クラスで定義されている場合、フィールド型とフィールド エイリアスを尊重します。

このセクションの例では、次のモデル定義を使用します。

class Band
include Mongoid::Document
field :name, type: String
field :founded, type: Integer
field :m, as: :member_count, type: Integer
embeds_one :manager
end
class Manager
include Mongoid::Document
embedded_in :band
field :name, type: String
end

最も単純なクエリ構文では、基本的な Ruby ハッシュが使用されます。キーはシンボルまたは文字列にすることができ、MongoDB ドキュメント内のフィールド名に対応します。

Band.where(name: "Depeche Mode")
# => #<Mongoid::Criteria
# selector: {"name"=>"Depeche Mode"}
# options: {}
# class: Band
# embedded: false>
# Equivalent to:
Band.where("name" => "Depeche Mode")

MQL 演算子は、ハッシュ構文を使用して任意のフィールドに指定できます。

Band.where(founded: {'$gt' => 1980})
# => #<Mongoid::Criteria
# selector: {"founded"=>{"$gt"=>1980}}
# options: {}
# class: Band
# embedded: false>
# Equivalent to:
Band.where('founded' => {'$gt' => 1980})

MQL 演算子は、次のように、それぞれのフィールド名のシンボルのメソッドとして指定できます。

Band.where(:founded.gt => 1980)
# => #<Mongoid::Criteria
# selector: {"founded"=>{"$gt"=>1980}}
# options: {}
# class: Band
# embedded: false>

フィールドをクエリするために、 モデル クラス定義にフィールドを追加する必要はありません。ただし、モデル クラスでフィールドが定義されている場合、Mongoid はクエリを構築するときに、定義されたフィールド型と一致するようにクエリ値を強制します。

Band.where(name: 2020, founded: "2020")
# => #<Mongoid::Criteria
# selector: {"name"=>"2020", "founded"=>2020}
# options: {}
# class: Band
# embedded: false>

Mongoid のクエリ型強制動作をバイパスし、データベース内の未加工の型値を直接クエリする場合は、クエリ値をMongoid::RawValueクラスでラップします。 これは、レガシー データを操作する際に役立ちます。

Band.where(founded: Mongoid::RawValue("2020"))
# => #<Mongoid::Criteria
# selector: {"founded"=>"2020"}
# options: {}
# class: Band
# embedded: false>

クエリでは、ストレージ フィールド名フィールド エイリアスが考慮されます。

Band.where(name: 'Astral Projection')
# => #<Mongoid::Criteria
# selector: {"n"=>"Astral Projection"}
# options: {}
# class: Band
# embedded: false>

id フィールドと _id フィールドはエイリアスなので、どちらもクエリに使用できます。

Band.where(id: '5ebdeddfe1b83265a376a760')
# => #<Mongoid::Criteria
# selector: {"_id"=>BSON::ObjectId('5ebdeddfe1b83265a376a760')}
# options: {}
# class: Band
# embedded: false>

埋め込まれたドキュメントのフィールドの値を一致させるには、ドット表記を使用します。

Band.where('manager.name' => 'Smith')
# => #<Mongoid::Criteria
# selector: {"manager.name"=>"Smith"}
# options: {}
# class: Band
# embedded: false>
Band.where(:'manager.name'.ne => 'Smith')
# => #<Mongoid::Criteria
# selector: {"manager.name"=>{"$ne"=>"Smith"}}
# options: {}
# class: Band
# embedded: false>

注意

すべての条件が埋め込みドキュメントを参照している場合でも、クエリは常に最上位のモデル インスタンスを返します。

Mongoid は、Criteria オブジェクトに対する andornor、および not の論理演算をサポートします。これらのメソッドは、1 つ以上の条件のハッシュまたは別の Criteria オブジェクトを引数として受け取ります。not には引数なしのバージョンも追加されます。

# and with conditions
Band.where(label: 'Trust in Trance').and(name: 'Astral Projection')
# or with scope
Band.where(label: 'Trust in Trance').or(Band.where(name: 'Astral Projection'))
# not with conditions
Band.not(label: 'Trust in Trance', name: 'Astral Projection')
# argument-less not
Band.not.where(label: 'Trust in Trance', name: 'Astral Projection')

以前の Mongoid バージョンとの下位互換性のため、すべての論理演算メソッドはパラメーターの配列も受け入れ、基準を取得するためにフラット化されます。論理演算に配列を渡すことは非推奨であり、Mongoid の将来のバージョンでは削除される可能性があります。

次の呼び出しはすべて同じクエリ条件を生成します。

# Condition hashes passed to separate and invocations
Band.and(name: 'SUN Project').and(member_count: 2)
# Multiple condition hashes in the same and invocation
Band.and({name: 'SUN Project'}, {member_count: 2})
# Multiple condition hashes in an array - deprecated
Band.and([{name: 'SUN Project'}, {member_count: 2}])
# Condition hash in where and a scope
Band.where(name: 'SUN Project').and(Band.where(member_count: 2))
# Condition hash in and and a scope
Band.and({name: 'SUN Project'}, Band.where(member_count: 2))
# Scope as an array element, nested arrays - deprecated
Band.and([Band.where(name: 'SUN Project'), [{member_count: 2}]])
# All produce:
# => #<Mongoid::Criteria
# selector: {"name"=>"SUN Project", "member_count"=>2}
# options: {}
# class: Band
# embedded: false>

Mongoid7.1 以降、論理演算子(andornornot )は 、ActiveRecord 論理演算子 と同じセマンティクスを持つように変更されました。 。Mongoid 7でのorの動作のセマンティクスを取得します。 0以前の場合は、以下で説明するany_ofを使用します。

同じフィールドに条件が複数回指定されている場合、すべての条件が基準に追加されます。

Band.where(name: 1).where(name: 2).selector
# => {"name"=>"1", "$and"=>[{"name"=>"2"}]}
Band.where(name: 1).or(name: 2).selector
# => {"$or"=>[{"name"=>"1"}, {"name"=>"2"}]}

any_ofnone_ofnornot は同様に動作しますが、not は以下に示すように異なるクエリシェイプを生成します。

andornor 論理演算子が使用される場合、それらはその時点までに構築された基準とその引数に基づいて動作します。whereand と同じ意味を持ちます:

# or joins the two conditions
Band.where(name: 'Sun').or(label: 'Trust').selector
# => {"$or"=>[{"name"=>"Sun"}, {"label"=>"Trust"}]}
# or applies only to the first condition, the second condition is added
# to the top level as $and
Band.or(name: 'Sun').where(label: 'Trust').selector
# => {"$or"=>[{"name"=>"Sun"}], "label"=>"Trust"}
# Same as previous example - where and and are aliases
Band.or(name: 'Sun').and(label: 'Trust').selector
# => {"$or"=>[{"name"=>"Sun"}], "label"=>"Trust"}
# Same operator can be stacked any number of times
Band.or(name: 'Sun').or(label: 'Trust').selector
# => {"$or"=>[{"name"=>"Sun"}, {"label"=>"Trust"}]}
# The label: Foo condition is added to the top level as $and
Band.where(name: 'Sun').or(label: 'Trust').where(label: 'Foo').selector
# => {"$or"=>[{"name"=>"Sun"}, {"label"=>"Trust"}], "label"=>"Foo"}

and メソッドは、受信基準にそれぞれのフィールドの条件がすでに設定されていない限り、基準の最上位レベルに新しい単純な条件を追加します。設定されている場合、条件は $and と結合されます。

Band.where(label: 'Trust in Trance').and(name: 'Astral Projection').selector
# => {"label"=>"Trust in Trance Records", "name"=>"Astral Projection"}
Band.where(name: /Best/).and(name: 'Astral Projection').selector
# => {"name"=>/Best/, "$and"=>[{"name"=>"Astral Projection"}]}

Mongoid 7.1 以降では、and を使用して同じフィールドに複数の条件を指定すると、指定されたすべての条件が結合されますが、以前のバージョンの Mongoid では、使用された and の形式に応じて、フィールドの条件が同じフィールドで以前に指定された条件を置き換えることがありました。

or/nor Behavior _----------------------

or および nor は、レシーバーとすべての引数をオペランドとして使用して、それぞれ $or および $nor MongoDB 演算子を生成します。例:

Band.where(name: /Best/).or(name: 'Astral Projection')
# => {"$or"=>[{"name"=>/Best/}, {"name"=>"Astral Projection"}]}
Band.where(name: /Best/).and(name: 'Astral Projection').
or(Band.where(label: /Records/)).and(label: 'Trust').selector
# => {"$or"=>[{"name"=>/Best/, "$and"=>[{"name"=>"Astral Projection"}]}, {"label"=>/Records/}], "label"=>"Trust"}

レシーバーの唯一の条件が別の or/nor である場合、新しい条件が既存のリストに追加されます。

Band.where(name: /Best/).or(name: 'Astral Projection').
or(Band.where(label: /Records/)).selector
# => {"$or"=>[{"name"=>/Best/}, {"name"=>"Astral Projection"}, {"label"=>/Records/}]}

これまでに構築されたすべての条件をそのまま維持しながら、Criteria オブジェクトに論理和を追加するには、any_of を使用します。

any_of 引数から構築された論理和を基準内の既存の条件に追加します。例:

Band.where(label: /Trust/).any_of({name: 'Astral Projection'}, {name: /Best/})
# => {"label"=>/Trust/, "$or"=>[{"name"=>"Astral Projection"}, {"name"=>/Best/}]}

可能な場合は、条件が最上位レベルに引き上げられます。

Band.where(label: /Trust/).any_of({name: 'Astral Projection'})
# => {"label"=>/Trust/, "name"=>"Astral Projection"}

none_of は、その引数から構築された否定論理和(「nor」)を基準内の既存の条件に追加します。例:

Band.where(label: /Trust/).none_of({name: 'Astral Projection'}, {name: /Best/})
# => {"label"=>/Trust/, "$nor"=>[{"name"=>"Astral Projection"}, {"name"=>/Best/}]}

not メソッドは引数なしで呼び出すことができます。その場合、指定された次の条件は否定されます。not は、1 つ以上のハッシュ条件または Criteria オブジェクトを使用して呼び出すこともできます。これらはすべて否定されて条件に追加されます。

# not negates subsequent where
Band.not.where(name: 'Best').selector
# => {"name"=>{"$ne"=>"Best"}}
# The second where is added as $and
Band.not.where(name: 'Best').where(label: /Records/).selector
# => {"name"=>{"$ne"=>"Best"}, "label"=>/Records/}
# not negates its argument
Band.not(name: 'Best').selector
# => {"name"=>{"$ne"=>"Best"}}

注意

$not (MongoDBサーバー内)は文字列引数では使用できません。Mongoid は、このような否定を実現するために $ne 演算子を使用します。

# String negation - uses $ne
Band.not.where(name: 'Best').selector
# => {"name"=>{"$ne"=>"Best"}}
# Regexp negation - uses $not
Band.not.where(name: /Best/).selector
# => {"name"=>{"$not"=>/Best/}}

and と同様に、not は単純なフィールド基準の個々の条件を否定します。複雑な条件の場合、およびフィールドにすでに条件が定義されている場合、MongoDB サーバーはグローバルではなくフィールドごとにのみ $not 演算子をサポートするため、Mongoid は {'$and' => [{'$nor' => ...}]} 構造を使用して $not をエミュレートします。

# Simple condition
Band.not(name: /Best/).selector
# => {"name"=>{"$not"=>/Best/}}
# Complex conditions
Band.where(name: /Best/).not(name: 'Astral Projection').selector
# => {"name"=>/Best/, "$and"=>[{"$nor"=>[{"name"=>"Astral Projection"}]}]}
# Symbol operator syntax
Band.not(:name.ne => 'Astral Projection')
# => #<Mongoid::Criteria
# selector: {"$and"=>[{"$nor"=>[{"name"=>{"$ne"=>"Astral Projection"}}]}]}
# options: {}
# class: Band
# embedded: false>

配列または正規表現とともにnotを使用する場合は、 MongoDB サーバーのドキュメント に記載されている$notの警告と制限に注意してください。

デフォルトでは、条件がクエリに追加されると、Mongoid は各条件が完了し、クエリ内に存在する可能性のある他の条件から独立していると見なします。たとえば、in を 2 回呼び出すと、2 つの個別の $in 条件が追加されます。

Band.in(name: ['a']).in(name: ['b'])
=> #<Mongoid::Criteria
selector: {"name"=>{"$in"=>["a"]}, "$and"=>[{"name"=>{"$in"=>["b"]}}]}
options: {}
class: Band
embedded: false>

一部の演算子メソッドは、条件を段階的にビルドすることをサポートしています。この場合、サポートされている演算子の 1 つを使用するフィールドの条件が追加されるときに、同じフィールドに同じ演算子を使用する条件がすでに存在する場合は、指定された マージ戦略 に従って演算子式が結合されます。

Mongoid は 3 つのマージ戦略を提供します。

  • オーバーライド: 新しい演算子インスタンスは、同じ演算子を使用する同じフィールドの既存の条件を置き換えます。

  • 交差: 同じフィールドで同じ演算子を使用する条件が既に存在する場合、既存の条件の値と新しい条件の値が交差され、その結果が演算子値として保存されます。

  • 結合: 同じフィールドで同じ演算子を使用する条件が既に存在する場合、新しい条件の値が既存の条件の値に追加され、結果が演算子値として保存されます。

次のスニペットは、in を例の演算子として使用して、すべての戦略を示しています。

Band.in(name: ['a']).override.in(name: ['b'])
=> #<Mongoid::Criteria
selector: {"name"=>{"$in"=>["b"]}}
options: {}
class: Band
embedded: false>
Band.in(name: ['a', 'b']).intersect.in(name: ['b', 'c'])
=> #<Mongoid::Criteria
selector: {"name"=>{"$in"=>["b"]}}
options: {}
class: Band
embedded: false>
Band.in(name: ['a']).union.in(name: ['b'])
=> #<Mongoid::Criteria
selector: {"name"=>{"$in"=>["a", "b"]}}
options: {}
class: Band
embedded: false>

この戦略は、Criteria インスタンスで overrideintersect、または union を呼び出すことによってリクエストされます。リクエストされた戦略は、クエリで呼び出される次の条件メソッドに適用されます。次に呼び出される条件メソッドがマージ戦略をサポートしていない場合、次の例に示すように、戦略はリセットされます。

Band.in(name: ['a']).union.ne(name: 'c').in(name: ['b'])
=> #<Mongoid::Criteria
selector: {"name"=>{"$in"=>["a"], "$ne"=>"c"}, "$and"=>[{"name"=>{"$in"=>["b"]}}]}
options: {}
class: Band
embedded: false>

ne はマージ戦略をサポートしていないため、union 戦略は無視されてリセットされ、in が 2 回目に呼び出されたときにアクティブな戦略がありませんでした。

警告

現在のところ、マージ戦略では前の条件がクエリの最上位に追加されていることを前提としていますが、常にそうであるとは限りません(条件は $and 句の下にネストされている場合があります)。複雑な条件でマージ戦略を使用すると、誤ったクエリが構築される可能性があります。この不具合は将来修正される予定です。

次の演算子メソッドはマージ戦略をサポートします。

  • all

  • in

  • nin

このメソッドのセットは、Mongoid の将来のリリースで拡張される可能性があります。将来の互換性のために、次のメソッド呼び出しがマージ戦略をサポートする演算子である場合にのみ、戦略メソッドを呼び出します。

マージ戦略は現在、指定されたメソッドを通じて条件が追加された場合にのみ適用されることに注意してください。次の例では、2 つ目の条件が in ではなく where 経由で追加されているため、マージ戦略は適用されません。

Band.in(foo: ['a']).union.where(foo: {'$in' => 'b'})
=> #<Mongoid::Criteria
selector: {"foo"=>{"$in"=>["a"]}, "$and"=>[{"foo"=>{"$in"=>"b"}}]}
options: {}
class: Band
embedded: false>

この動作は Mongoid の将来のリリースで変更される可能性があるため、依存しないでください。

対照的に、マージ戦略をサポートする演算子メソッドが呼び出される場合、既存のクエリがどのように構築されたかは問題ではありません。次の例では、最初の条件は where を通じて追加されましたが、戦略メカニズムは引き続き適用されます。

Band.where(foo: {'$in' => ['a']}).union.in(foo: ['b'])
=> #<Mongoid::Criteria
selector: {"foo"=>{"$in"=>["a", "b"]}}
options: {}
class: Band
embedded: false>

マージ戦略をサポートする演算子メソッドはすべて、値の型として Array を受け取ります。Mongoid は、次の演算子メソッドで使用される場合、Range などの Array 互換型を拡張します。

Band.in(year: 1950..1960)
=> #<Mongoid::Criteria
selector: {"year"=>{"$in"=>[1950, 1951, 1952, 1953, 1954, 1955, 1956, 1957, 1958, 1959, 1960]}}
options: {}
class: Band
embedded: false>

さらに、次の例に示すように、Mongoid は歴史的に Array 以外の値を配列にラップしてきました。

Band.in(year: 1950)
=> #<Mongoid::Criteria
selector: {"year"=>{"$in"=>[1950]}}
options: {}
class: Band
embedded: false>

このマッチャーは、配列値の 1 つがすべての条件に一致する配列フィールドを持つドキュメントを検索します。例:

class Band
include Mongoid::Document
field :name, type: String
field :tours, type: Array
end
aerosmith = Band.create!(name: 'Aerosmith', tours: [
{city: 'London', year: 1995},
{city: 'New York', year: 1999},
])
Band.elem_match(tours: {city: 'London'}).to_a # => [aerosmith]

elem_match は、埋め込み関連付けでも機能します。

class Band
include Mongoid::Document
field :name, type: String
embeds_many :tours
end
class Tour
include Mongoid::Document
field :city, type: String
field :year, type: Integer
embedded_in :band
end
dm = Band.create!(name: 'Depeche Mode')
aerosmith = Band.create!(name: 'Aerosmith')
Tour.create!(band: aerosmith, city: 'London', year: 1995)
Tour.create!(band: aerosmith, city: 'New York', year: 1999)
Band.elem_match(tours: {city: 'London'}).to_a # => [aerosmith]

elem_match MongoDB には結合がないため、非埋め込み関連付けでは機能しません。条件は、関連付けのターゲットのコレクションではなく、非埋め込み関連付けのソースであるコレクションに追加されます。

elem_match 次の例に示すように、再帰的に埋め込まれた関連付けで使用することもできます。

class Tag
include Mongoid::Document
field :name, type: String
recursively_embeds_many
end
root = Tag.create!(name: 'root')
sub1 = Tag.new(name: 'sub1', child_tags: [Tag.new(name: 'subsub1')])
root.child_tags << sub1
root.child_tags << Tag.new(name: 'sub2')
root.save!
Tag.elem_match(child_tags: {name: 'sub1'}).to_a # => [root]
root.child_tags.elem_match(child_tags: {name: 'subsub1'}).to_a # => [sub1]

Mongoid は、onlywithout の 2 つのプロジェクション演算子を提供します。

only メソッドは、データベースから指定されたフィールドのみを検索します。この操作は「プロジェクション」と呼ばれることもあります。

class Band
include Mongoid::Document
field :name, type: String
field :label, type: String
embeds_many :tours
end
class Tour
include Mongoid::Document
field :city, type: String
field :year, type: Integer
embedded_in :band
end
band = Band.only(:name).first

ロードされていない属性を参照しようとすると、Mongoid::Errors::AttributeNotLoaded が発生します。

band.label
#=> raises Mongoid::Errors::AttributeNotLoaded

Mongoid は現在、ロードされていない属性への書き込みを許可していますが、このような書込みは保持されません( MONGOID-4701 )のため、 は避けてください。

only は埋め込み関連付けでも使用できます。

band = Band.only(:name, 'tours.year').last
# => #<Band _id: 5c59afb1026d7c034dba46ac, name: "Aerosmith">
band.tours.first
# => #<Tour _id: 5c59afdf026d7c034dba46af, city: nil, year: 1995>

注意

サーバー バージョン 4.2 以前では、次のように、同じクエリで関連付けと関連付けのフィールドの両方を投影されました。

band = Band.only(:tours, 'tours.year').last

最新のプロジェクション仕様が以前のものを上書きします。たとえば、上記のクエリは次のクエリと同等です。

band = Band.only('tours.year').last

サーバー バージョン 4.4 以降では、同じクエリで関連付けとそのプロジェクションのフィールドを指定することは禁止されています。

only 参照された関連付け(has_one、has_many、has_and_belongs_to_many)で指定できますが、現在は参照された関連付けでは無視され、参照されたすべての関連付けのフィールドがロードされます (MONGOID-4704)。

ドキュメントに has_one または has_and_belongs_to_many の関連付けがある場合、それらの関連付けをロードするには、外部キーを持つフィールドが only でロードされる属性のリストに含まれている必要があることに注意してください。例:

class Band
include Mongoid::Document
field :name, type: String
has_and_belongs_to_many :managers
end
class Manager
include Mongoid::Document
has_and_belongs_to_many :bands
end
band = Band.create!(name: 'Astral Projection')
band.managers << Manager.new
Band.where(name: 'Astral Projection').only(:name).first.managers
# => []
Band.where(name: 'Astral Projection').only(:name, :manager_ids).first.managers
# => [#<Manager _id: 5c5dc2f0026d7c1730969843, band_ids: [BSON::ObjectId('5c5dc2f0026d7c1730969842')]>]

only の反対で、without は指定されたフィールドを省略します。

Band.without(:name)
# =>
# #<Mongoid::Criteria
# selector: {}
# options: {:fields=>{"name"=>0}}
# class: Band
# embedded: false>

Mongoid はさまざまな操作に _id フィールドを必要とするため、このフィールド(およびその id エイリアス)を without で省略することはできません。

Band.without(:name, :id)
# =>
# #<Mongoid::Criteria
# selector: {}
# options: {:fields=>{"name"=>0}}
# class: Band
# embedded: false>
Band.without(:name, :_id)
# =>
# #<Mongoid::Criteria
# selector: {}
# options: {:fields=>{"name"=>0}}
# class: Band
# embedded: false>

Mongoid は、ドキュメントの順序を指定するために、Criteria オブジェクトとそのエイリアスである order_byorder メソッドを提供します。これらのメソッドは、ドキュメントをどのフィールドで並べ替えるか、および各フィールドに対して昇順または降順のどちらを使用するかを示すハッシュを受け取ります。

Band.order(name: 1)
# => #<Mongoid::Criteria
# selector: {}
# options: {:sort=>{"name"=>1}}
# class: Band
# embedded: false>
Band.order_by(name: -1, description: 1)
# => #<Mongoid::Criteria
# selector: {}
# options: {:sort=>{"name"=>-1, "description"=>1}}
# class: Band
# embedded: false>
Band.order_by(name: :desc, description: 'asc')
# => #<Mongoid::Criteria
# selector: {}
# options: {:sort=>{"name"=>-1, "description"=>1}}
# class: Band
# embedded: false>

方向は、昇順と降順を表す整数 1-1、またはシンボル :asc:desc、あるいは文字列 "asc""desc" として指定できます。

あるいは、order は順序を指定する 2 要素の配列の配列を受け入れます。フィールド名と方向は文字列または記号にすることができます。

Band.order([['name', 'desc'], ['description', 'asc']])
Band.order([[:name, :desc], [:description, :asc]])

順序を指定する別の方法は、次のようにシンボルに対して #asc メソッドと #desc メソッドを使用することです。

Band.order(:name.desc, :description.asc)

引数は、SQL 構文を使用して文字列として指定できます。

Band.order('name desc, description asc')

最後に、order/order_by の代わりに使用できる asc メソッドと desc メソッドがあります。

Band.asc('name').desc('description')
# => #<Mongoid::Criteria
selector: {}
options: {:sort=>{"name"=>1, "description"=>-1}}
class: Band
embedded: false>

order 呼び出しは連鎖させることができ、その場合、最も古い呼び出しが最も重要な基準を定義し、最も新しい呼び出しが最も重要な基準を定義します(Ruby ではハッシュがキーの順序を維持するため)。

Band.order('name desc').order('description asc')
# => #<Mongoid::Criteria
selector: {}
options: {:sort=>{"name"=>-1, "description"=>1}}
class: Band
embedded: false>

デフォルトのスコープを含め、order/order_by を使用するスコープがある場合、予期しない結果が生じることがあります。たとえば、次のスニペットでは、デフォルトのスコープが最初に評価されるため、デフォルトのスコープ内の順序がクエリで指定された順序よりも優先され、バンドは最初に名前で順序付けられます。

class Band
include Mongoid::Document
field :name, type: String
field :year, type: Integer
default_scope -> { order(name: :asc) }
end
Band.order(year: :desc)
# => #<Mongoid::Criteria
selector: {}
options: {:sort=>{"name"=>1, "year"=>-1}}
class: Band
embedded: false>

Mongoid は、Criteria でページネーション演算子 limitskip、および batch_size を提供します。

limit はクエリによって返されるドキュメントの合計数を設定します。

Band.limit(5)
# =>
# #<Mongoid::Criteria
# selector: {}
# options: {:limit=>5}
# class: Band
# embedded: false>

skip (エイリアス: offset )は、ドキュメントを返す前にスキップするクエリ結果の数を設定します。 limit値が指定されている場合は、ドキュメントがスキップされた後にこの値が適用されます。 ページネーションを行う際には、一貫した結果を得るために、 skipを使用する際にクエリの順序も指定することをお勧めします。

Band.skip(10)
# =>
# #<Mongoid::Criteria
# selector: {}
# options: {:skip=>10}
# class: Band
# embedded: false>

大規模なクエリを実行する場合、または Criteria#each などの列挙メソッドを使用してクエリ結果を反復処理する場合、Mongoid は自動的に MongoDB getMore コマンドを使用して結果をバッチで読み込みます。デフォルトの batch_size は 1000 ですが、明示的に設定することもできます。

Band.batch_size(500)
# =>
# #<Mongoid::Criteria
# selector: {}
# options: {:batch_size=>500}
# class: Band
# embedded: false>

Mongoid は、_id 値でドキュメントを検索するために、Criteria オブジェクトに find メソッドを提供します。

Band.find('5f0e41d92c97a64a26aabd10')
# => #<Band _id: 5f0e41d92c97a64a26aabd10, name: "Juno Reactor">

find メソッドは、必要に応じて、引数を _id フィールドに対して照会されているモデルで宣言された型に変換します。デフォルトでは、_id 型は BSON::ObjectId であるため、上記のクエリは次のクエリと同等になります。

Band.find(BSON::ObjectId.from_string('5f0e41d92c97a64a26aabd10'))
# => #<Band _id: 5f0e41d92c97a64a26aabd10, name: "Juno Reactor">

注意

ドライバーを使用してコレクションを直接クエリする場合、型変換は自動的に実行されません。

Band.collection.find(_id: BSON::ObjectId.from_string('5f0e41d92c97a64a26aabd10')).first
# => {"_id"=>BSON::ObjectId('5f0e41d92c97a64a26aabd10'), "name"=>"Juno Reactor"}
Band.collection.find(_id: '5f0e41d92c97a64a26aabd10').first
# => nil

find メソッドは複数の引数、または引数の配列を受け入れることができます。どちらの場合も、各引数または配列要素は _id 値として扱われ、指定されたすべての _id 値を持つドキュメントが配列で返されます。

Band.find('5f0e41d92c97a64a26aabd10', '5f0e41b02c97a64a26aabd0e')
# => [#<Band _id: 5f0e41b02c97a64a26aabd0e, name: "SUN Project", description: nil, likes: nil>,
#<Band _id: 5f0e41d92c97a64a26aabd10, name: "Juno Reactor", description: nil, likes: nil>]
Band.find(['5f0e41d92c97a64a26aabd10', '5f0e41b02c97a64a26aabd0e'])
# => [#<Band _id: 5f0e41b02c97a64a26aabd0e, name: "SUN Project", description: nil, likes: nil>,
#<Band _id: 5f0e41d92c97a64a26aabd10, name: "Juno Reactor", description: nil, likes: nil>]

同じ _id 値が複数回指定されている場合、対応するドキュメントは 1 回だけ返されます。

Band.find('5f0e41b02c97a64a26aabd0e', '5f0e41b02c97a64a26aabd0e')
# => [#<Band _id: 5f0e41b02c97a64a26aabd0e, name: "SUN Project", description: nil, likes: nil>]

返されるドキュメントは順序付けられておらず、上記の例に示すように、指定された _id 値の順序とは異なる順序で返される場合があります。

_id 値のいずれかがデータベースで見つからない場合、find の動作は raise_not_found_error 構成オプションの値によって異なります。オプションが true に設定されている場合に _id のいずれかが見つからない場合は、findMongoid::Errors::DocumentNotFound が発生します。オプションを false に設定し、 find に検索目的で単一の _id が与えられ、一致するドキュメントがない場合、findnil を返します。オプションが false に設定され、find に検索対象の ID の配列が指定され、一部が見つからなかった場合、戻り値は見つかったドキュメントの配列になります(ドキュメントがまったく見つからなかった場合は空になる可能性があります)。

Mongoid には、基準に関する役立つメソッドもいくつかあります。

操作

Criteria#count

フィルターに一致するドキュメントの合計数、またはコレクション内のドキュメントの合計数を取得します。カウントのために常にデータベースがヒットすることに注意してください。

Mongoid 7.2 以降、 count メソッドは、 count_documents ドライバー ヘルパーを使用して正確な数を入手します。かつては、 count )ドライバー ヘルパーが使用されていましたが、これはコレクション メタデータを使用するため、必ずしも正確ではありませんでした(ただし、結果がより速く返される可能性があります)。 estimated_count メソッドを使用して、コレクション内のドキュメントのおおよその数をすばやく取得します。

Band.count
Band.where(name: "Photek").count

Criteria#estimated_count

コレクションのメタデータを使用して、コレクション内のドキュメントのおおよその数を取得します。 estimated_count メソッドはクエリ条件を受け入れません。条件が指定された場合は、次が発生します: Mongoid::Errors::InvalidEstimatedCountCriteriaモデルがデフォルトのスコープを定義している場合estimated_count スコープが設定されていないモデルで呼び出す必要があります

Band.count
Band.where(name: "Photek").count
class Contract
include Mongoid::Document
field :active, type: Boolean
default_scope -> { where(active: true) }
end
Contract.estimated_count
# => raises Mongoid::Errors::InvalidEstimatedCountCriteria
Contract.unscoped.estimated_count
# => 0

Criteria#distinct

単一のフィールドの個別の値のリストを取得します。異なる値については、常にデータベースにアクセスすることに注意してください。

このメソッドはドット表記を受け入れるため、埋め込まれた関連付け内のフィールドを参照できます。

このメソッドは、埋め込みドキュメントで定義されているものも含め、:ref:`フィールド エイリアス <field-aliases>` を尊重します。

Band.distinct(:name)
Band.where(:fans.gt => 100000).
distinct(:name)
Band.distinct('cities.name')
# Assuming an aliased field:
class Manager
include Mongoid::Document
embedded_in :band
field :name, as: :n
end
# Expands out to "managers.name" in the query:
Band.distinct('managers.n')

Criteria#each

条件に一致するすべてのドキュメントを反復処理します。

Band.where(members: 1).each do |band|
p band.name
end

Criteria#exists?

一致するドキュメントが存在するかどうかを判断します。1 個以上の場合は true を返します。

#exists? は、次のいくつかの引数型を取ります。

  • Hash: 条件のハッシュ。

  • Object: 検索する_id。

  • false/nil: 常に false を返します。

Band.exists?
Band.where(name: "Photek").exists?
Band.exists?(name: "Photek")
Band.exists?(BSON::ObjectId('6320d96a3282a48cfce9e72c'))
Band.exists?('6320d96a3282a48cfce9e72c')
Band.exists?(false)
Band.exists?(nil)

Criteria#fifth

指定された条件に該当する 5 番目のドキュメントを取得します。

このメソッドは、ソートが指定されていない場合、_id に自動的にソートを追加します。

Band.fifth

Criteria#fifth!

指定された条件に該当する 5 番目のドキュメントを取得します。存在しない場合はエラーが発生します。

このメソッドは、ソートが指定されていない場合、_id に自動的にソートを追加します。

Band.fifth!

Criteria#find_by

指定された属性でドキュメントを検索します。見つからない場合は、 raise_not_found_error 構成オプションの値に応じてエラーを発生させるか、nil を返します。

Band.find_by(name: "Photek")
Band.find_by(name: "Tool") do |band|
band.impressions += 1
end

Criteria#find_or_create_by

指定された属性でドキュメントを検索し、見つからない場合は新しく作成して保存したものを返します。このメソッドの引数で指定された属性は、「create_with」で設定された属性を上書きすることに注意してください

Band.find_or_create_by(name: "Photek")
Band.where(:likes.gt => 10).find_or_create_by(name: "Photek")

find_or_create_by どのスコープでも使用できますが、この場合はスコープによって指定された基準と find_or_create_by によって指定された基準が組み合わされます。次の例では、3 つのバンドが作成されます。

Band.find_or_create_by(name: "Photek")
Band.where(name: "Photek").find_or_create_by(name: "Aerosmith")
# creates Aerosmith again because there is no band whose name
# is Photek and Aerosmith at the same time
Band.where(name: "Photek").find_or_create_by(name: "Aerosmith")

Criteria#find_or_initialize_by

指定された属性でドキュメントを検索し、見つからない場合は新しいドキュメントを返します。

Band.find_or_initialize_by(name: "Photek")
Band.where(:likes.gt => 10).find_or_initialize_by(name: "Photek")

Criteria#first|last

指定された条件に従って単一のドキュメントを検索します。制限引数を渡してドキュメントのリストを取得します。このメソッドは、_id に基づいて自動的にソートを追加します。これによりパフォーマンスの問題が発生する可能性があるため、ソートが望ましくない場合は、代わりに Criteria#take を使用できます。

Band.first
Band.where(:members.with_size => 3).first
Band.where(:members.with_size => 3).last
Band.first(2)

Criteria#first!|last!

指定された条件に従って単一のドキュメントを検索します。見つからない場合はエラーが発生します。このメソッドは、ソートが指定されていない場合、_id に自動的にソートを追加します。これによりパフォーマンスの問題が発生する可能性があるため、並べ替えが望ましくない場合は、代わりに Criteria#take! を使用できます。

Band.first!
Band.where(:members.with_size => 3).first!
Band.where(:members.with_size => 3).last!

Criteria#first_or_create

指定された属性で最初のドキュメントを検索し、見つからない場合は新しく保存されたドキュメントを作成して返します。

Band.where(name: "Photek").first_or_create

Criteria#first_or_create!

指定された属性で最初のドキュメントを検索し、見つからない場合は、新しく保存されたドキュメントを作成して返します。 create!.

Band.where(name: "Photek").first_or_create!

Criteria#first_or_initialize

指定された属性で最初のドキュメントを検索し、見つからない場合は新しいドキュメントを返します。

Band.where(name: "Photek").first_or_initialize

Criteria#for_js

指定された JavaScript 式のドキュメントを検索します。オプションで、指定された変数を評価スコープに追加します。スコープ引数は MongoDB 4.2 以前でサポートされています。 好み $expr over for_js.

# All MongoDB versions
Band.for_js("this.name = 'Tool'")
# MongoDB 4.2 and lower
Band.for_js("this.name = param", param: "Tool")

Criteria#fourth

指定された条件に該当する 4 番目のドキュメントを取得します。

このメソッドは、ソートが指定されていない場合、_id に自動的にソートを追加します。

Band.fourth

Criteria#fourth!

指定された条件に一致する 4 番目のドキュメントを取得します。存在しない場合はエラーが発生します。

このメソッドは、ソートが指定されていない場合、_id に自動的にソートを追加します。

Band.fourth!

Criteria#length|size

countと同じですが、データベースへの後続の呼び出しをキャッシュします。

Band.length
Band.where(name: "FKA Twigs").size

Criteria#pick

指定されたフィールドの値を 1 つのドキュメントから取得します。設定されていないフィールドおよび存在しないフィールドの場合は nil を返します。

このメソッドはドキュメントにソートを適用しないため、必ずしも最初のドキュメントの値が返されるわけではありません。

このメソッドはドット表記を受け入れるため、埋め込まれた関連付け内のフィールドを参照できます。

このメソッドは、埋め込みドキュメントで定義されているものも含め、:ref:`フィールド エイリアス <field-aliases>` を尊重します。

Band.all.pick(:name)
Band.all.pick('cities.name')
# Using the earlier definition of Manager,
# expands out to "managers.name" in the query:
Band.all.pick('managers.n')

Criteria#pluck

指定されたフィールドのすべての値を取得します。設定されていないフィールドおよび存在しないフィールドの場合は nil を返します。

このメソッドはドット表記を受け入れるため、埋め込まれた関連付け内のフィールドを参照できます。

このメソッドは、埋め込みドキュメントで定義されているものも含め、:ref:`フィールド エイリアス <field-aliases>` を尊重します。

Band.all.pluck(:name)
#=> ["Daft Punk", "Aphex Twin", "Ween"]
Band.all.pluck('address.city')
#=> ["Paris", "Limerick", "New Hope"]
# Using the earlier definition of Manager,
# expands out to "managers.name" in the query:
Band.all.pluck('managers.n')
#=> [ ["Berry Gordy", "Tommy Mottola"], [], ["Quincy Jones"] ]
# Accepts multiple field arguments, in which case
# the result will be returned as an Array of Arrays.
Band.all.pluck(:name, :likes)
#=> [ ["Daft Punk", 342], ["Aphex Twin", 98], ["Ween", 227] ]

Criteria#read

基準の読み込み設定 (read preference) を設定します。

Band.all.read(mode: :primary)

Criteria#second

指定された条件に該当する 2 番目のドキュメントを取得します。

このメソッドは、ソートが指定されていない場合、_id に自動的にソートを追加します。

Band.second

Criteria#second!

指定された条件に該当する 2 番目のドキュメントを取得します。

このメソッドは、ソートが指定されていない場合、_id に自動的にソートを追加します。

Band.second!

Criteria#second_to_last

指定された条件に該当する最後から 2 番目のドキュメントを取得します。

このメソッドは、ソートが指定されていない場合、_id に自動的にソートを追加します。

Band.second_to_last

Criteria#second_to_last!

指定された条件に一致する最後から 2 番目のドキュメントを取得します。存在しない場合はエラーが発生します。

このメソッドは、ソートが指定されていない場合、_id に自動的にソートを追加します。

Band.second_to_last!

Criteria#take

データベースから n 個のドキュメントのリストを取得します。パラメーターが指定されていない場合は 1 つのドキュメントのみを取得します。

このメソッドはドキュメントにソートを適用しないため、#first および #last とは異なるドキュメントを返す場合があります。

Band.take
Band.take(5)

Criteria#take!

データベースからドキュメントを取得するか、ドキュメントが存在しない場合はエラーを発生させます。

このメソッドはドキュメントにソートを適用しないため、#first および #last とは異なるドキュメントを返す場合があります。

Band.take!

Criteria#tally

指定されたフィールドの値とカウントのマッピングを取得します。

このメソッドはドット表記を受け入れるため、埋め込まれた関連付け内のフィールドを参照できます。

このメソッドは、埋め込みドキュメントで定義されているものも含め、:ref:`フィールド エイリアス <field-aliases>` を尊重します。

Band.all.tally(:name)
Band.all.tally('cities.name')
# Using the earlier definition of Manager,
# expands out to "managers.name" in the query:
Band.all.tally('managers.n')

Criteria#third

指定された条件の 3 番目のドキュメントを取得します。

このメソッドは、ソートが指定されていない場合、_id に自動的にソートを追加します。

Band.third

Criteria#third!

指定された条件に一致する 3 番目のドキュメントを取得します。存在しない場合はエラーが発生します。

このメソッドは、ソートが指定されていない場合、_id に自動的にソートを追加します。

Band.third!

Criteria#third_to_last

指定された条件に該当する最後から 3 番目のドキュメントを取得します。

このメソッドは、ソートが指定されていない場合、_id に自動的にソートを追加します。

Band.third_to_last

Criteria#third_to_last!

指定された条件に該当する最後から 3 番目のドキュメントを取得します。存在しない場合はエラーが発生します。

このメソッドは、ソートが指定されていない場合、_id に自動的にソートを追加します。

Band.third_to_last!

Mongoid は、関連付けアクセスを持つドキュメントを反復処理するときに n+1 問題を防ぐために、関連付けからドキュメントを早期ロードする機能を提供します。多態的な belongs_to 関連付けを除くすべての関連付けで、Eager 読み込みがサポートされています。

class Band
include Mongoid::Document
has_many :albums
end
class Album
include Mongoid::Document
belongs_to :band
end
Band.includes(:albums).each do |band|
p band.albums.first.name # Does not hit the database again.
end

MongoDB と Mongoid では、正規表現によるドキュメントのクエリが可能です。

次のモデル定義があるとします。

class Band
include Mongoid::Document
field :name, type: String
field :description, type: String
end
Band.create!(name: 'Sun Project', description: "Sun\nProject")

... シンプルな Ruby 正規表現を使用して自然な方法でクエリを実行できます。

Band.where(name: /project/i).first
# => #<Band _id: 5dc9f7d5ce4ef34893354323, name: "Sun Project", description: "Sun\nProject">

BSON::Regexp::Raw オブジェクトを明示的に構築することで、PCRE 構文を使用してクエリを実行することもできます。

Band.where(description: /\AProject/).first
# => #<Band _id: 5dc9f7d5ce4ef34893354323, name: "Sun Project", description: "Sun\nProject">
Band.where(description: BSON::Regexp::Raw.new('^Project')).first
# => nil
Band.where(description: BSON::Regexp::Raw.new('^Project', 'm')).first
# => #<Band _id: 5dc9f7d5ce4ef34893354323, name: "Sun Project", description: "Sun\nProject">

条件がモデルで定義されたフィールドを使用する場合、条件で指定されている値は、フィールドのルール(存在する場合)に従って変換されます。たとえば、Time フィールド、Date フィールド、暗黙的な Object フィールドを含み、deregistered_at というフィールドを意図的に定義していない次のモデル定義を検討してください。

class Voter
include Mongoid::Document
field :born_on, type: Date
field :registered_at, type: Time
field :voted_at
end

それぞれ DateTime の値を使用して born_on フィールドと registered_at フィールドをクエリするのは簡単です。

Voter.where(born_on: Date.today).selector
# => {"born_on"=>2020-12-18 00:00:00 UTC}
Voter.where(registered_at: Time.now).selector
# => {"registered_at"=>2020-12-19 04:33:36.939788067 UTC}

ただし、考えられるすべてのシナリオで Date インスタンスを提供する場合の動作の違いに注意してください。

Voter.where(born_on: Date.today).selector
# => {"born_on"=>2020-12-18 00:00:00 UTC}
Voter.where(registered_at: Date.today).selector
# => {"registered_at"=>2020-12-18 00:00:00 -0500}
Voter.where(voted_at: Date.today).selector
# => {"voted_at"=>Fri, 18 Dec 2020}
Voter.where(deregistered_at: Date.today).selector
# => {"deregistered_at"=>2020-12-18 00:00:00 UTC}

Time 型の registered_at フィールドを使用すると、日付はローカル時間(構成されたタイムゾーンに従って)として解釈されました。Date 型の born_on フィールドを使用すると、日付は UTC として解釈されました。型なしで定義された voted_at フィールド(つまり暗黙的に Object として)を使用すると、構築されたクエリで日付が変更されずに使用されました。存在しないフィールド deregistered_at を使用すると、日付は UTC であると解釈され、時刻に変換され、Date フィールドをクエリする動作と一致します。

スコープは、よりビジネス ドメイン スタイルの構文を使用して共通の基準を再利用する便利な方法を提供します。

名前付きスコープは、指定された名前によって参照される、クラスのロード時に定義される基準です。通常の基準と同様に、これらは遅延され、連鎖可能です。

class Band
include Mongoid::Document
field :country, type: String
field :genres, type: Array
scope :english, ->{ where(country: "England") }
scope :rock, ->{ where(:genres.in => [ "rock" ]) }
end
Band.english.rock # Get the English rock bands.

名前付きスコープは、パラメーターを受け取ったり機能を拡張したりするために、プロシージャとブロックを取ることができます。

class Band
include Mongoid::Document
field :name, type: String
field :country, type: String
field :active, type: Boolean, default: true
scope :named, ->(name){ where(name: name) }
scope :active, ->{
where(active: true) do
def deutsch
tap do |scope|
scope.selector.store("origin" => "Deutschland")
end
end
end
}
end
Band.named("Depeche Mode") # Find Depeche Mode.
Band.active.deutsch # Find active German bands.

デフォルトでは、Mongoid では、次の例に示すように、既存のクラス メソッドをシャドウするスコープを定義できます。

class Product
include Mongoid::Document
def self.fresh
true
end
scope :fresh, ->{ where(fresh: true) }
end

スコープが既存のクラス メソッドを上書きするときに Mongoid がエラーを発生させるようにするには、scope_overwrite_exception 構成オプションtrue に設定します。

デフォルトのスコープは、ほとんどのクエリに同じ基準を適用し、これらの基準をデフォルトとして指定する場合に役立ちます。デフォルトのスコープは、基準オブジェクトを返すプロシージャです。

class Band
include Mongoid::Document
field :name, type: String
field :active, type: Boolean
default_scope ->{ where(active: true) }
end
Band.each do |band|
# All bands here are active.
end

デフォルト スコープを指定すると、値が単純なリテラルである場合、新しいモデルのフィールドもデフォルト スコープで指定された値に初期化されます。

class Band
include Mongoid::Document
field :name, type: String
field :active, type: Boolean
field :num_tours, type: Integer
default_scope ->{ where(active: true, num_tours: {'$gt' => 1}) }
end
# active is set, num_tours is not set
Band.new # => #<Band _id: 5c3f7452ce4ef378295ca5f5, name: nil, active: true, num_tours: nil>

フィールド定義とデフォルト スコープの両方にデフォルト値が指定されている場合は、デフォルト スコープの値が優先されることに注意してください。

class Band
include Mongoid::Document
field :name, type: String
field :active, type: Boolean, default: true
default_scope ->{ where(active: false) }
end
Band.new # => #<Band _id: 5c3f74ddce4ef3791abbb088, name: nil, active: false>

前述のように、デフォルト スコープは新しいモデルのフィールドを初期化するため、ドット キーと単純なリテラル値を使用してデフォルト スコープを定義することは可能ですが、推奨されません。

class Band
include Mongoid::Document
field :name, type: String
field :tags, type: Hash
default_scope ->{ where('tags.foo' => 'bar') }
end
Band.create!
# => Created document: {"_id"=>BSON::ObjectId('632de48f3282a404bee1877b'), "tags.foo"=>"bar"}
Band.create!(tags: { 'foo' => 'bar' })
# => Created document: {"_id"=>BSON::ObjectId('632de4ad3282a404bee1877c'), "tags.foo"=>"bar", "tags"=>{"foo"=>"bar"}}
Band.all.to_a
# => [ #<Band _id: 632de4ad3282a404bee1877c, tags: {"foo"=>"bar"}> ]

Mongoid 8 では、Mongoid でドット付きキーを使用できるようになり、ドキュメントを作成するときに、スコープが属性にドット付きキーとして追加されます。

Band.new.attribute
# => {"_id"=>BSON::ObjectId('632de97d3282a404bee1877d'), "tags.foo"=>"bar"}

一方、クエリを実行する場合、Mongoid は埋め込まれたドキュメントを探します。

Band.create!
# => Created document: {"_id"=>BSON::ObjectId('632de48f3282a404bee1877b'), "tags.foo"=>"bar"}
Band.where
# => #<Mongoid::Criteria
selector: {"tags.foo"=>"bar"}
options: {}
class: Band
embedded: false>
# This looks for something like: { tags: { "foo" => "bar" } }
Band.count
# => 0

回避策としては、デフォルトのスコープを複雑なクエリとして定義します。

class Band
include Mongoid::Document
field :name, type: String
field :tags, type: Hash
default_scope ->{ where('tags.foo' => {'$eq' => 'bar'}) }
end
Band.create!(tags: { hello: 'world' })
Band.create!(tags: { foo: 'bar' })
# does not add a "tags.foo" dotted attribute
Band.count
# => 1

インラインまたはブロックを取ることができる unscoped を使用して、Mongoid にデフォルトのスコープを適用しないように指示できます。

Band.unscoped.where(name: "Depeche Mode")
Band.unscoped do
Band.where(name: "Depeche Mode")
end

また、Mongoid に後でデフォルトのスコープを明示的に再度適用するように指示して、常に存在するようにすることもできます。

Band.unscoped.where(name: "Depeche Mode").scoped

関連付けの一部であるモデルでデフォルトのスコープを使用している場合は、スコープを再適用するために関連付けを再ロードする必要があります。関連付け内のドキュメントの値を変更して、スコープ指定された関連付け内での可視性に影響する場合は、この点に注意することが重要です。

class Label
include Mongoid::Document
embeds_many :bands
end
class Band
include Mongoid::Document
field :active, default: true
embedded_in :label
default_scope ->{ where(active: true) }
end
label.bands.push(band)
label.bands # [ band ]
band.update_attribute(:active, false)
label.bands # [ band ] Must reload.
label.reload.bands # []

注意

デフォルトのスコープが適用されると、他のクエリ条件と区別されなくなります。これは、特に or および nor 演算子を使用する場合に、予期しない動作を引き起こす可能性があります。

class Band
include Mongoid::Document
field :name
field :active
field :touring
default_scope ->{ where(active: true) }
end
Band.where(name: 'Infected Mushroom')
# =>
# #<Mongoid::Criteria
# selector: {"active"=>true, "name"=>"Infected Mushroom"}
# options: {}
# class: Band
# embedded: false>
Band.where(name: 'Infected Mushroom').or(touring: true)
# =>
# #<Mongoid::Criteria
# selector: {"$or"=>[{"active"=>true, "name"=>"Infected Mushroom"}, {"touring"=>true}]}
# options: {}
# class: Band
# embedded: false>
Band.or(touring: true)
# =>
# #<Mongoid::Criteria
# selector: {"$or"=>[{"active"=>true}, {"touring"=>true}]}
# options: {}
# class: Band
# embedded: false>

最後の例では、2 つの条件(active: truetouring: true)が $and と結合されることが予想されるかもしれませんが、Band クラスにはすでにスコープが適用されているため、or の論理和分岐の 1 つになります。

実行時にブロック内のデフォルトのスコープを変更するには、with_scope メソッドを使用します。

class Band
include Mongoid::Document
field :country, type: String
field :genres, type: Array
scope :english, ->{ where(country: "England") }
end
criteria = Band.with_scope(Band.english) do
Band.all
end
criteria
# =>
# #<Mongoid::Criteria
# selector: {"country"=>"England"}
# options: {}
# class: Band
# embedded: false>

注意

with_scope 呼び出しがネストされている場合、ネストされた with_scope ブロックが完了すると、Mongoid 7 は親スコープではなく現在のスコープを nil に設定します。Mongoid 8 は現在のスコープを正しい親スコープに設定します。Mongoid 7.4 以降で Mongoid 8 の動作を実現するには、Mongoid.broken_scoping グローバル オプションを false に設定します。

基準オブジェクトを返すモデル上のクラス メソッドもスコープのように扱われ、連鎖させることもできます。

class Band
include Mongoid::Document
field :name, type: String
field :active, type: Boolean, default: true
def self.active
where(active: true)
end
end
Band.active

Mongoid は、複数のドキュメントの挿入、更新、削除を表現力豊かに実行したい場合に、基準に基づいた永続化操作を軽量にサポートします。

警告

次の操作では、orderlimitoffsetbatch_size などの基準の順序付けとページ区切りの条件は無視されます。

操作

Criteria#create

新しく保存されたドキュメントを作成します。

Band.where(name: "Photek").create

Criteria#create!

新しく永続化されたドキュメントを作成し、検証が失敗した場合に例外を発生させます。

Band.where(name: "Photek").create!

Criteria#build|new

新しい(未保存の)ドキュメントを作成します。

Band.where(name: "Photek").build
Band.where(name: "Photek").new

Criteria#update

最初に一致するドキュメントの属性を更新します。

Band.where(name: "Photek").update(label: "Mute")

Criteria#update_all

一致するすべてのドキュメントの属性を更新します。

Band.where(members: 2).update_all(label: "Mute")

Criteria#add_to_set

一致するすべてのドキュメントに対して $addToSet を実行します。

Band.where(name: "Photek").add_to_set(label: "Mute")

Criteria#bit

一致するすべてのドキュメントに対して $bit を実行します。

Band.where(name: "Photek").bit(likes: { and: 14, or: 4 })

Criteria#inc

一致するすべてのドキュメントに対して $inc を実行します。

Band.where(name: "Photek").inc(likes: 123)

Criteria#pop

一致するすべてのドキュメントに対して $pop を実行します。

Band.where(name: "Photek").pop(members: -1)
Band.where(name: "Photek").pop(members: 1)

Criteria#pull

一致するすべてのドキュメントに対して $pull を実行します。

Band.where(name: "Tool").pull(members: "Maynard")

Criteria#pull_all

一致するすべてのドキュメントに対して $pullAll を実行します。

Band.where(name: "Tool").
pull_all(:members, [ "Maynard", "Danny" ])

Criteria#push

一致するすべてのドキュメントに対して $push を実行します。

Band.where(name: "Tool").push(members: "Maynard")

Criteria#push_all

一致するすべてのドキュメントに対して $each を使用して $push を実行します。

Band.where(name: "Tool").
push_all(members: [ "Maynard", "Danny" ])

Criteria#rename

一致するすべてのドキュメントに対して $rename を実行します。

Band.where(name: "Tool").rename(name: :title)

Criteria#set

一致するすべてのドキュメントに対して $set を実行します。

Band.where(name: "Tool").set(likes: 10000)

Criteria#unset

一致するすべてのドキュメントに対して $unset を実行します。

Band.where(name: "Tool").unset(:likes)

Criteria#delete

データベース内の一致するドキュメントをすべて削除します。

Band.where(label: "Mute").delete

Criteria#destroy

すべてのコールバックを実行しながら、データベース内の一致するすべてのドキュメントを削除します。これにより、すべてのドキュメントがメモリに読み込まれるため、コストのかかる操作になる可能性があります。

Band.where(label: "Mute").destroy

Ruby MongoDB ドライバー バージョン2.14以降では、クエリ キャッシュ機能が提供されます。 有効にすると、クエリ キャッシュは以前に実行された検索および集計クエリの結果を保存し、クエリを再度実行するのではなく、将来的にそれらを再利用するため、アプリケーションのパフォーマンスが向上し、データベースの負荷が軽減されます。

ドライバーのクエリ キャッシュ動作の詳細については、ドライバー クエリ キャッシュのドキュメントを参照してください。

このセクションの残りの部分では、ドライバー 2.14 0 以降の使用を前提としています。

クエリ キャッシュは、ドライバーの名前空間または Mongoid の名前空間を使用して有効にできます。

MongoDB Ruby ドライバーは、Rack ウェブリクエストと ActiveJob ジョブ実行のクエリ キャッシュを自動的に有効にするミドルウェアを提供します。手順については、構成ページの「Query Cache Rack ミドルウェア」セクションを参照してください。

クエリ キャッシュ ミドルウェアは、ウェブリクエストやジョブの外部で実行されるコードには適用されないことに注意してください。

コード セグメントに対してクエリ キャッシュを手動で有効にするには、次を使用します。

Mongo::QueryCache.cache do
# ...
end

クエリ キャッシュは明示的に有効化および無効化することもできますが、上記のブロック形式を使用することをお勧めします。

begin
Mongo::QueryCache.enabled = true
# ...
ensure
Mongo::QueryCache.enabled = false
end

モデル クラスで first メソッドを呼び出すと、基になるクエリの _id フィールドによる昇順の並べ替えが適用されます。これにより、クエリ キャッシュで予期しない動作が発生する可能性があります。

たとえば、モデル クラスで all を呼び出してから first を呼び出すと、2 番目のクエリでは最初のクエリのキャッシュされた結果が使用されることが予想されます。ただし、2 番目のクエリに適用されるソートにより、両方の方法でデータベースがクエリされ、その結果が個別にキャッシュされます。

Band.all.to_a
#=> Queries the database and caches the results
Band.first
#=> Queries the database again because of the sort

キャッシュされた結果を使用するには、モデル クラスで all.to_a.first を呼び出します。

Mongoid を使用すると、バックグラウンドでデータベース クエリを非同期に実行できます。これは、さまざまなコレクションからドキュメントを取得する必要がある場合に役立ちます。

非同期クエリをスケジュールするには、Criteriaload_async メソッドを呼び出します。

class PagesController < ApplicationController
def index
@active_bands = Band.where(active: true).load_async
@best_events = Event.best.load_async
@public_articles = Article.where(public: true).load_async
end
end

上記の例では、3 つのクエリが非同期実行用にスケジュールされます。クエリの結果には、後で通常どおりアクセスできます。

<ul>
<%- @active_bands.each do -%>
<li><%= band.name %></li>
<%- end -%>
</ul>

クエリが非同期実行用にスケジュールされている場合でも、呼び出し元のスレッドで同期的に実行される可能性があります。クエリ結果にアクセスするタイミングに応じて、次の 3 つのシナリオが考えられます。

  1. スケジュールされた非同期タスクがすでに実行されている場合は、結果が返されます。

  2. タスクが開始されたがまだ完了していない場合、呼び出し元のスレッドはタスクが完了するまでブロックされます。

  3. タスクがまだ開始されていない場合は、実行キューから削除され、クエリは呼び出し元のスレッドで同期的に実行されます。

注意

load_async メソッドは Criteria オブジェクトを返しますが、クエリ結果にアクセスする場合を除き、このオブジェクトに対して操作を実行しないでください。クエリは load_async を呼び出した後すぐに実行されるようにスケジュールされているため、Criteria オブジェクトへのその後の変更は適用されない可能性があります。

非同期クエリはデフォルトで無効になっています。非同期クエリが無効になっている場合、load_async は必要に応じてブロックしながら、現在のスレッドでクエリを直ちに実行します。したがって、この場合、条件に対して load_async を呼び出すことは、クエリの実行を強制するために to_a を呼び出すこととほぼ同等です。

非同期クエリ実行を有効にするには、次の構成オプションを設定する必要があります。

development:
...
options:
# Execute asynchronous queries using a global thread pool.
async_query_executor: :global_thread_pool
# Number of threads in the pool. The default is 4.
# global_executor_concurrency: 4

戻る

CRUD 操作