Overview
在本指南中,您可以学习;了解如何自定义 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_one 或 has_and_belongs_to_many 关联,并且您希望 Mongoid 在调用 only 方法时加载这些关联,则必须在属性列表中包含带有外键的字段。
在以下示例中,Band 和 Manager 模型具有 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 别名。如果将 _id 或 id 传递给 without 方法,Mongoid 会将其忽略。
对结果进行排序
您可以使用 order 和 order_by 方法指定 Mongoid 返回文档的顺序。
这些方法接受一个哈希值,该哈希值指示按哪些字段对文档进行排序,以及对每个字段使用升序还是降序。
可以使用整数、符号或字符串指定排序方向。 为了一致性,我们建议在整个应用程序中使用相同的排序语法。 以下列表提供了每种语法,并显示了如何对 name 和 year 字段进行排序:
整数
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')
提示
除了使用 order 或 order_by 之外,您还可以使用 asc 和 desc 方法来指定排序顺序:
Band.asc('name').desc('year')
当您链式排序规范时,第一次调用定义第一个排序顺序,最后一个调用定义应用先前排序后的最后一个排序顺序。
对结果进行分页
Mongoid 提供了可在 Criteria 对象上使用的 limit、skip 和 batch_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
以下代码在返回结果时跳过前 3 个文档:
Band.skip(3) # Equivalent Band.offset(3)
生成结果批次
执行大型查询以及使用Criteria#each 等枚举器方法迭代查询结果时,Mongoid 会自动使用MongoDB getMore 命令分批加载结果。默认批处理大小为 1000,但您可以使用 batch_size 方法设立不同的值。
以下代码将批处理大小设置为 500:
Band.batch_size(500)
预先加载关联
当您查询具有关联的文档时,通过预先加载,您可以在加载基础文档的同时加载关联文档,而不是稍后延迟加载。这减少了数据库查询次数,并提高了关联密集型工作负载的性能。当您查询具有关联的文档时,Mongoid 提供两种预先加载关联文档的方法:includes 和 eager_load。
include 方法
includes 方法使用针对每个关联的单独查询来加载关联。以下代码使用单独的查询加载乐队及其关联的专辑和标签:
Band.where(name: 'The Beatles').includes({ albums: :songs }, :labels).first
当您使用 includes 方法时,Mongoid 会对基类运行查询以获取所有基本文档,然后为其预先加载的每个关联运行单独查询。
上一个示例执行了以下查询:
查找
Band文档的一个查询一个查询即可加载关联的
Album文档一个查询即可加载关联的
Label文档一个查询即可加载关联的
Song文档
ager_load 方法
eager_load 方法使用带有 $lookup 操作符的MongoDB聚合管道在单个查询中加载所有关联。以下代码使用单个聚合管道查询来加载乐队及其关联的专辑和标签,每个关联有 $lookup 个阶段:
Band.where(name: 'The Beatles').eager_load({ albums: :songs }, :labels).first
性能考虑因素
includes 和 eager_load 方法都执行预先加载,但执行方式不同。includes 方法对基本文档运行一次查询,然后对每个关联进行单独查询。eager_load 方法使用具有 $lookup 个阶段的单个聚合管道来加载关联。
与 includes 相比,eager_load 方法执行的查询更少,使用的内存也更少。但是,该方法的效率因文档中的关联类型而异:
eager_load对于has_many、has_and_belongs_to_many更快,并且在大多数数据集上加载多个关联includes在大多数数据集上,belongs_to、has_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 模块参考。
如需学习;了解有关构建查询的更多信息,请参阅“指定文档查询”指南。