Docs 菜单
Docs 主页
/ / /
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>

从 Mongoid7.1 开始,逻辑操作符(andornornot )已更改为具有与 ActiveRecord 相同的语义 。获取or在 Mongoid 7中的语义。 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 _----------------------

ornor 分别生成 $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/}]}

使用 any_of 向 Criteria 对象添加一个析取操作,同时保持目前为止构建的所有条件不变。

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 可以在没有参数的情况下调用方法,在这种情况下,该方法将对下一个指定的条件取反。还可以使用一个或多个哈希条件或Criteria对象来调用not ,这些对象都会取反并添加到条件中。

# 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 Server 文档中所述$not 的注意事项/限制条件。

默认情况下,当条件添加到查询中时,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 提供了三种合并策略:

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

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

  • Union:如果同一字段上已存在使用相同运算符的条件,则新条件的值将添加到现有条件的值中,并将结果存储为运算符值。

以下代码片段使用 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 作为其值类型。当与 Array 兼容的类型(如 Range)与这些操作符方法一起使用时,Mongoid会扩展这些类型的值:

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 字段,所以不能通过 without 省略该字段(以及其 id 别名):

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_by 上提供 order 方法,用于指定文档的顺序。这些方法采用哈希值,表示根据哪些字段对文档进行排序,以及对每个字段使用升序还是降序。

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 的范围(包括默认范围),此操作有时可能会产生令人惊异的结果。例如,在下面的代码片段中,乐队首先按名称排序,因为默认范围中的顺序优先于查询中给出的顺序,其原因是首先会评估默认范围:

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>”,包括在嵌入式文档中定义的字段别名。

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

根据提供的属性查找文档。如果未找到,则引发错误或返回 nil,具体取决于以下的值: raise_not_found_error 配置选项。

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

与计数相同,但会缓存对数据库的后续调用

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

Criteria#pick

从一个文档中获取所提供字段的值。对于未设置的字段和不存在的字段,返回 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

设置条件的读取偏好。

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>”,包括在嵌入式文档中定义的字段别名。

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 时间。使用未定义类型(因此隐式为 Object)的 voted_at 字段时,日期在构建的查询中未经修改地使用。使用不存在的字段 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.

命名作用域可以采用 proc 和区块来接受参数或扩展功能。

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 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 作业运行启用查询缓存。有关说明,请参阅配置页面的查询缓存 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

后退

增删改查操作