Join us at MongoDB.local London on 7 May to unlock new possibilities for your data. Use WEB50 to save 50%.
Register now >
Docs 菜单
Docs 主页
/ /

修改查询结果

在本指南中,您可以学习;了解如何自定义 Mongoid 返回查询结果的方式。 MongoDB允许您执行以下操作来修改结果的显示方式:

  • 返回指定字段

  • 对结果进行排序

  • 对结果进行分页

  • 预先加载关联

  • 返回原始数据

本指南中的示例使用 Band 模型,该模型表示乐队或音乐群组。 每个部分的 Band 模型的定义可能不同,以演示不同的查询功能。 某些部分还使用 Manager 模型(表示给定乐队的管理人员)或 Tour 模型(表示给定乐队的现场表演)。

在MongoDB中,投影是指定要从结果中包含或排除的字段的进程。Mongoid 提供以下操作符来项目字段:

  • only:指定要包含的字段

  • without:指定要排除的字段

only 方法只从数据库中检索指定字段。

以下代码仅返回 members字段值为 4 的文档中的 name字段:

Band.where(members: 4).only(:name)

注意

_id字段

在MongoDB中,即使您没有显式包含 _id字段也会包含在结果中。

如果尝试引用尚未加载的属性,Mongoid 会引发 Mongoid::Errors::AttributeNotLoaded 错误。

您还可以使用 only 方法包含嵌入式文档中的字段。

假设 Band 模型嵌入了多个 Tour 对象。 您可以项目Tour 模型中的字段,例如 year,如以下代码所示:

bands = Band.only(:name, 'tours.year')

然后,您可以从返回的文档访问权限嵌入式字段:

# Returns the first Tour object from
# the first Band in the results
bands.first.tours.first

您可以将引用关联的字段传递给 only 方法,但在加载嵌入式对象时会忽略投影。 Mongoid 加载引用关联的所有字段。 示例,当您访问权限前面代码中所示的嵌入式 Tour对象时,Mongoid 返回完整的对象,而不仅仅是 year字段。

注意

如果您连接到运行MongoDB 4.4 或更高版本的部署,则无法在同一查询中的投影中指定关联及其字段。

如果文档包含 has_onehas_and_belongs_to_many 关联,并且您希望 Mongoid 在调用 only 方法时加载这些关联,则必须在属性列表中包含带有外键的字段。

在以下示例中,BandManager 模型具有 has_and_belongs_to_many 关联:

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

以下代码演示了如果包含 manager_ids字段, Mongoid 如何加载关联的 Manager 对象:

# Returns null
Band.where(name: 'Astral Projection').only(:name).first.managers
# Returns the first Manager object
Band.where(name: 'Astral Projection').only(:name, :manager_ids).first.managers

您可以使用 without 方法从结果中明确排除字段。

以下代码从返回的 Band 对象中排除 year字段:

Band.where(members: 4).without(:year)

重要

_id字段

Mongoid 需要_id 字段来执行各种操作,因此您不能从结果中排除 _id字段或id 别名。如果将 _idid 传递给 without 方法,Mongoid 会将其忽略。

您可以使用 orderorder_by 方法指定 Mongoid 返回文档的顺序。

这些方法接受一个哈希值,该哈希值指示按哪些字段对文档进行排序,以及对每个字段使用升序还是降序。

可以使用整数、符号或字符串指定排序方向。 为了一致性,我们建议在整个应用程序中使用相同的排序语法。 以下列表提供了每种语法,并显示了如何对 nameyear 字段进行排序:

  • 整数 1(升序)和 -1(降序)

    • 示例: Band.order(name: 1, year: -1)

  • 符号 :asc:desc

    • 示例: Band.order(name: :asc, year: :desc)

  • 字符串 "asc""desc"

    • 示例: Band.order_by(name: "asc", year: "desc")

order 方法还接受以下排序规范:

  • 二元素数组的数组:

    • 字符串

      • 示例: Band.order([['name', 'asc'], ['year', 'desc']])

    • 符号

      • 示例: Band.order([[:name, :asc], [:year, :desc]])

  • asc 以及针对符号的 desc 方法

    • 示例: Band.order(:name.asc, :year.desc)

  • SQL语法

    • 示例: Band.order('name asc', 'year desc')

提示

除了使用 orderorder_by 之外,您还可以使用 ascdesc 方法来指定排序顺序:

Band.asc('name').desc('year')

当您链式排序规范时,第一次调用定义第一个排序顺序,最后一个调用定义应用先前排序后的最后一个排序顺序。

注意

按范围排序

如果在包含排序规范的模型上定义 默认作用域,则作用域排序优先于查询中指定的排序,因为首先会评估默认作用域。

Mongoid 提供了可在 Criteria 对象上使用的 limitskipbatch_size 分页方法。 以下部分介绍了如何使用这些操作符。

您可以使用 limit 方法来限制 Mongoid 返回的结果数量。

以下代码最多检索 5 个文档:

Band.limit(5)

注意

或者,可以使用 take 方法从数据库中检索指定数量的文档:

Band.take(5)

您可以使用 skip 方法或其别名 offset 跳过指定数量的结果。

如果将 limit 调用链接到 skip,则在跳过文档后应用限制,如以下示例所示:

Band.skip(2).limit(5)
# Skips the first two results and returns
# the following five results

提示

执行分页时,对排序结果使用skip 以确保结果一致。

以下代码在返回结果时跳过前 3 个文档:

Band.skip(3)
# Equivalent
Band.offset(3)

执行大型查询以及使用Criteria#each 等枚举器方法迭代查询结果时,Mongoid 会自动使用MongoDB getMore 命令分批加载结果。默认批处理大小为 1000,但您可以使用 batch_size 方法设立不同的值。

以下代码将批处理大小设置为 500

Band.batch_size(500)

当您查询具有关联的文档时,通过预先加载,您可以在加载基础文档的同时加载关联文档,而不是稍后延迟加载。这减少了数据库查询次数,并提高了关联密集型工作负载的性能。当您查询具有关联的文档时,Mongoid 提供两种预先加载关联文档的方法:includeseager_load

includes 方法使用针对每个关联的单独查询来加载关联。以下代码使用单独的查询加载乐队及其关联的专辑和标签:

Band.where(name: 'The Beatles').includes({ albums: :songs }, :labels).first

当您使用 includes 方法时,Mongoid 会对基类运行查询以获取所有基本文档,然后为其预先加载的每个关联运行单独查询。

上一个示例执行了以下查询:

  • 查找 Band 文档的一个查询

  • 一个查询即可加载关联的 Album 文档

  • 一个查询即可加载关联的 Label 文档

  • 一个查询即可加载关联的 Song 文档

eager_load 方法使用带有 $lookup 操作符的MongoDB聚合管道在单个查询中加载所有关联。以下代码使用单个聚合管道查询来加载乐队及其关联的专辑和标签,每个关联有 $lookup 个阶段:

Band.where(name: 'The Beatles').eager_load({ albums: :songs }, :labels).first

includeseager_load 方法都执行预先加载,但执行方式不同。includes 方法对基本文档运行一次查询,然后对每个关联进行单独查询。eager_load 方法使用具有 $lookup 个阶段的单个聚合管道来加载关联。

includes 相比,eager_load 方法执行的查询更少,使用的内存也更少。但是,该方法的效率因文档中的关联类型而异:

  • eager_load 对于 has_manyhas_and_belongs_to_many 更快,并且在大多数数据集上加载多个关联

  • includes 在大多数数据集上,belongs_tohas_one 和嵌套关联的速度更快

选择最适合您的使用案例和关联类型的方法。

您可以将 raw 方法链接到查询,以原始哈希形式返回结果,而无需创建模型实例。

以下代码以原始哈希值形式检索查询结果:

Band.where(country: 'Argentina').raw

默认下,raw 方法返回的文档与 Mongoid 从数据库接收的文档完全相同。您可以选择通过在 raw 方法中将 typed 选项设置为 true,将结果哈希中的字段转换为模型字段声明中的类型:

Band.where(country: 'Argentina').raw(typed: true)

如果您执行的查询返回原始结果,并且稍后想要将这些结果转换为模型,则可以对结果调用 raw(false)

# Retrieves raw results
results = Band.where(members: 4).raw
# ... Perform actions on results
# Returns instantiated model objects from raw results
bands = results.raw(false).to_a

有关可用于 Criteria 对象以修改查询结果的方法的完整目录,请参阅API文档中的 Mongoid::Criteria::Queryable 模块参考。

如需学习;了解有关构建查询的更多信息,请参阅“指定文档查询”指南。

要学习;了解Mongoid 数据建模,请参阅对数据建模指南。

后退

增删改查操作

在此页面上