文档菜单
文档首页
/ / /
Mongoid
/

查询

在本页

  • 条件语法
  • 字段语法
  • MQL 语法
  • 符号运算符语法
  • 字段
  • 基于定义字段的查询
  • 查询原始值
  • 字段别名
  • 嵌套文档
  • 逻辑运算
  • 运算符组合
  • 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" ])

查询方法返回Mongoid::Criteria 对象,它们是 MongoDB 查询语言 (MQL) 的可链式和惰性评估包装器。查询在迭代其结果集时执行。例如

# 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 支持指定单个条件的三种方式

  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 对象上执行 andornornot 逻辑操作。这些方法接受一个或多个条件哈希或另一个 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>

从 Mongoid 7.1 版本开始,逻辑运算符(andornornot)的语义已更改,与以下相同ActiveRecord的语义。为了获得与Mongoid 7.0及更早版本中行为的or语义,请使用下面描述的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

/ 行为 _----------------------

分别产生 MongoDB 操作符 $or$nor,使用接收者和所有参数作为操作数。例如

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"}

如果接收者上只有另一个 / 条件,则新条件将添加到现有列表中

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

使用 any_of 在保持现有条件不变的情况下,将析取条件添加到 Criteria 对象中

any_of 将其参数构建的析取条件添加到 Criteria 对象中的现有条件。例如

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 将其参数构建的否定析取条件("非")添加到 Criteria 对象中的现有条件。例如

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

not 方法可以不传参数调用,在这种情况下,它将否定指定的下一个条件。not 还可以与一个或多个哈希条件或 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"}}

注意

在 MongoDB 服务器中,$not 不能与字符串参数一起使用。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 与数组或正则表达式,请注意 $not 在 MongoDB 服务器文档中 所述的注意事项/限制

默认情况下,当条件被添加到查询中时,Mongoid 考虑每个条件都是完整且独立的,与查询中可能存在的其他任何条件无关。例如,调用 in 两次会添加两个独立的 $in 条件。

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

某些操作方法支持增量构建条件。在这种情况下,当添加使用受支持的运算符的字段条件时,如果该字段已存在使用同一运算符的条件,则根据指定的 合并策略 合并运算符表达式。

Mongoid 提供了三种合并策略。

  • 覆盖:新的操作符实例将使用相同操作符在该字段上替换任何现有条件。

  • 交集:如果该字段上已经存在使用相同操作符的条件,现有条件的值将与新条件的值相交,并将结果存储为操作符值。

  • 并集:如果该字段上已经存在使用相同操作符的条件,新条件的值将添加到现有条件的值中,并将结果存储为操作符值。

以下代码片段展示了所有策略,以 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 实例上调用 overrideintersectunion 来请求策略。请求的策略将应用于查询中下一个调用的条件方法。如果下一个调用的条件方法不支持合并策略,则策略将被重置,如下例所示

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 时没有激活任何策略。

警告

合并策略目前假定之前的条件已被添加到查询的最顶层,但实际上情况并非总是如此(条件可能位于 $and 子句之下)。使用复杂条件与合并策略可能会导致构建不正确的查询。这种行为 计划在未来修复

以下操作符方法支持合并策略

  • all

  • in

  • nin

此方法集合可能在 Mongoid 的未来版本中扩展。为了未来兼容性,只有当下一个方法调用是一个支持合并策略的操作符时,才调用策略方法。

请注意,合并策略目前仅在通过指定方法添加条件时应用。在以下示例中,未应用合并策略,因为第二个条件是通过where添加的,而不是通过in

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 扩展与这些操作符方法一起使用的与Array兼容的类型,例如Range

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>

此匹配器查找具有数组字段的文档,其中数组值之一符合所有条件。例如:

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

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_onehas_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方法及其别名order_by,以指定文档的排序。这些方法接受一个哈希,表示要按哪些字段排序文档,以及是否对每个字段使用升序或降序。

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接受一个二维数组,指定排序顺序。字段名和方向可以是字符串或符号。

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')

最后,有ascdesc方法可以用来替代order/order_by

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的scop,包括默认scop,这有时会导致令人惊讶的结果。例如,在下面的代码片段中,乐队首先按名称排序,因为默认scop中的顺序优先于查询中给出的顺序,因为默认scop首先被评估

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 上提供了分页运算符 limitskipbatch_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在Criteria对象上提供了find方法,用于根据_id值查找文档。

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值出现多次,则只返回相应的文档一次

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

返回的文档没有顺序,可能以与提供的_id值不同的顺序返回,如上述示例所示。

如果数据库中找不到任何_id值,则find的行为取决于raise_not_found_error配置选项的值。如果该选项设置为true,当任何_id未找到时,find将引发Mongoid::Errors::DocumentNotFound异常。如果该选项设置为false并且find提供了一个要查找的单个_id,并且没有匹配的文档,则find返回nil。如果该选项设置为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 <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

根据给定的标准获取第五个文档。

此方法在没有给定排序的情况下自动添加_id的排序。

Band.fifth

Criteria#fifth!

根据给定的标准获取第五个文档,如果不存在则引发错误。

此方法在没有给定排序的情况下自动添加_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提供的标准是组合的。以下创建三个乐队

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 而不是 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

根据给定的标准获取第四个文档。

此方法在没有给定排序的情况下自动添加_id的排序。

Band.fourth

Criteria#fourth!

根据给定的标准获取第四个文档,如果未找到则引发错误。

此方法在没有给定排序的情况下自动添加_id的排序。

Band.fourth!

Criteria#length|size

与count相同,但缓存了对数据库后续调用的调用。

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

Criteria#pick

获取单个文档中提供的字段的值。对于未设置的字段和非存在的字段返回nil。

此方法不对文档应用排序,因此不一定返回来自第一个文档的值。

此方法接受点表示法,因此允许引用内嵌关联中的字段。

此方法尊重:ref:`field aliases <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 <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

设置标准的读取偏好。

Band.all.read(mode: :primary)

Criteria#second

获取给定标准的第二个文档。

此方法在没有给定排序的情况下自动添加_id的排序。

Band.second

Criteria#second!

获取给定标准的第二个文档,如果不存在则抛出错误。

此方法在没有给定排序的情况下自动添加_id的排序。

Band.second!

Criteria#second_to_last

获取给定标准的倒数第二个文档。

此方法在没有给定排序的情况下自动添加_id的排序。

Band.second_to_last

Criteria#second_to_last!

获取给定标准的倒数第二个文档,如果不存在则抛出错误。

此方法在没有给定排序的情况下自动添加_id的排序。

Band.second_to_last!

Criteria#take

从数据库获取n个文档的列表,如果没有提供参数则只获取一个。

此方法不对文档进行排序,因此可能返回与#first和#last不同的文档。

Band.take
Band.take(5)

Criteria#take!

从数据库获取文档,如果不存在则抛出错误。

此方法不对文档进行排序,因此可能返回与#first和#last不同的文档。

Band.take!

Criteria#tally

获取提供的字段的值到计数的映射。

此方法接受点表示法,因此允许引用内嵌关联中的字段。

此方法尊重:ref:`field aliases <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

获取给定标准的第三个文档。

此方法在没有给定排序的情况下自动添加_id的排序。

Band.third

Criteria#third!

获取给定标准的第三个文档,如果不存在则抛出错误。

此方法在没有给定排序的情况下自动添加_id的排序。

Band.third!

Criteria#third_to_last

获取给定标准的倒数第三个文档。

此方法在没有给定排序的情况下自动添加_id的排序。

Band.third_to_last

Criteria#third_to_last!

获取给定标准的倒数第三个文档,如果不存在则抛出错误。

此方法在没有给定排序的情况下自动添加_id的排序。

Band.third_to_last!

Mongoid提供了一种从关联中预加载文档的功能,以防止在迭代具有关联访问的文档时出现n+1问题。预加载支持所有关联,除了多态belongs_to关联。

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_onregistered_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}

当使用类型为Timeregistered_at字段时,日期被解释为本地时间(根据配置的时间区域)。当使用类型为Dateborn_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 # []

注意

默认范围应用后,它不再与其他查询条件区分开来。在特定情况下,使用 ornor 运算符可能会导致意外行为

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>

在最后一个例子中,您可能期望这两个条件(active: truetouring: true)将与一个 $and 结合,但由于 Band 类已经应用了范围,它成为了 or 中的一个析取分支。

您可以使用 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 8 的行为,在 Mongoid 7.4 和更高版本中,将全局选项 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 及以上提供查询缓存功能。启用后,查询缓存将保存先前执行的 find 和聚合查询的结果,并在将来重用这些结果,而不是再次执行查询,从而提高应用程序性能并减少数据库负载。

请查阅驱动查询缓存文档,以了解关于驱动查询缓存行为的详细信息。

本节的其余部分假设正在使用2.14.0或更高版本的驱动程序。

可以使用驱动程序的命名空间或Mongoid的命名空间来启用查询缓存。

MongoDB Ruby 驱动程序提供了中间件,可以自动为Rack网络请求和ActiveJob作业运行启用查询缓存。请参阅配置页面上的查询缓存Rack中间件部分以获取说明。

请注意,查询缓存中间件不适用于Web请求和/或作业之外执行的代码。

要手动为代码段启用查询缓存,请使用

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

虽然我们建议使用上面描述的块形式,但查询缓存也可以显式地启用和禁用。

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

在模型类上调用 first 方法会对底层查询按照 _id 字段进行升序排序。这可能会导致查询缓存产生意外行为。

例如,当在一个模型类上先调用 all,然后调用 first 时,人们会期望第二个查询使用第一个查询的缓存结果。然而,由于对第二个查询的排序,这两个方法都将查询数据库并分别缓存它们的结果。

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 允许在后台异步运行数据库查询。当需要从不同的集合获取文档时,这可能是很有益的。

为了调度异步查询,请在 Criteria 上调用 load_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

在上面的示例中,将调度三个查询以异步执行。稍后可以像往常一样访问查询结果。

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

即使查询已调度为异步执行,它也可能在调用者的线程上同步执行。具体取决于何时访问查询结果,有三种可能的场景:

  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 操作