Overview
Mongoid 中的关联允许您在模型之间创建关系。在本指南中,您可以学习;了解引用关联如何允许您在两个模型之间创建一种关系,其中一个模型引用另一个模型。 Mongoid 支持以下引用关联类型:
has_onehas_manybelongs_tohas_and_belongs_to_many
以下部分描述了如何使用每种关联类型。
有一个
您可以使用 has_one 宏来声明由一个类表示的文档也包含由单独的子类表示的文档。 以下示例创建了一个与 Studio 类具有 has_one关系的 Band 类:
class Band include Mongoid::Document has_one :studio end
当您声明 has_one 关联时,子类还必须使用引用父类的 belongs_to 关联。 以下示例显示了前面的 Band 类中引用的 Studio 类:
class Studio include Mongoid::Document belongs_to :band end
要学习;了解有关belongs_to 宏的详情,请参阅 属于部分。
您可以使用验证来确保子类存在于父类中,如以下示例所示:
class Band include Mongoid::Document has_one :studio validates_presence_of :studio end
有很多
您可以使用 has_many 宏来声明一个类表示的文档包含由另一个类表示的多个子文档。 以下示例创建了一个与 Members 类具有 has_many关系的 Band 类:
class Band include Mongoid::Document has_many :members end
当您声明 has_many 关联时,子类还必须使用引用父类的 belongs_to 关联。 以下示例显示了前面的 Band 类中引用的 Member 类:
class Member include Mongoid::Document belongs_to :band end
要学习;了解有关belongs_to 宏的更多信息,请参阅“属于”部分。
您可以使用验证来确保子类存在于父类中,如以下示例所示:
class Band include Mongoid::Document has_many :members validates_presence_of :members end
要学习;了解有关 Mongoid 中验证的更多信息,请参阅 验证指南。
检索关联信息
您可以对 has_many 关联使用 any? 方法,以确定该关联是否包含任何文档,而无需从数据库中检索整个文档设立。
以下示例使用 any? 方法来确定 Band 类中的文档是否包含任何 Members 文档:
band = Band.first band.members.any?
您还可以使用带有过滤的 any? 方法来查找与指定条件匹配的文档,如以下示例所示:
band = Band.first band.members.any? { |member| member.instrument == 'piano' }
您可以向 any? 方法提供类名,以按类名过滤结果。 这对于多态关联非常有用:
class Drummer < Member end band = Band.first band.members.any?(Drummer)
注意
将关联类的数据加载到 Mongoid 后,对 any? 方法的后续调用不会查询数据库。 相反,Mongoid 使用已加载到内存中的数据。
您还可以调用 exists? 方法来确定关联中是否有任何持久化文档。 exists? 方法始终查询数据库并仅检查已保存到数据库的文档。 exists? 方法不允许过滤,也不接受任何参数。
以下示例使用 exists? 方法来确定 Band 类中是否存在任何持久的 Members 文档:
band = Band.create! # Member is not persisted. band.members.build band.members.exists? # Outputs: false # Persist the member band.members.map(&:save!) band.members.exists? # Outputs: true
属于关联
使用 belongs_to 宏声明一个类表示的文档是另一个类表示的文档的子文档。 默认下,父类的 _id字段存储在子类中。 以下示例创建一个 Members 类,其中包含与 Band 类的 belongs_to 关联:
class Members include Mongoid::Document belongs_to :band end
您可以通过将 optional 选项设置为 true,允许 Mongoid 将文档持久保存到数据库,而不存储关联父类的 _id,如以下示例所示:
class Members include Mongoid::Document belongs_to :band, optional: true end
提示
通过在应用程序的配置设置中将 belongs_to_required_by_default 配置选项设置为 false,您可以全局更改 belongs_to 关联的默认行为,以便不需要其父类。
您可以在子类中指定 belongs_to 关联,而无需在父类中指定匹配的 has_one 或 has_many 关联。 这样做时,您无法从父类访问权限子文档的字段,但可以访问权限存储在子类中的父字段,例如父文档的 _id字段。 在以下示例中,Band 类无法访问权限Members 类,但 Members 类可以访问权限Band 类:
class Band include Mongoid::Document end class Members include Mongoid::Document belongs_to :band end
为清楚起见,您可以选择将 inverse_of 选项设立为 nil,以指示父类不包含与子类的 has_one 或 has_many 关联,如以下示例所示:
class Band include Mongoid::Document end class Members include Mongoid::Document belongs_to :band, inverse_of: nil end
拥有并且属于许多
使用 has_and_belongs_to_many 宏声明类模型包含与另一个类的多对多关系。 在多对多关系中,一个类中的每个文档都可以与另一个类中的多个文档相关联。 以下示例创建了一个与 Members 类具有 has_and_belongs_to_many关系的Band 类。 一个 Band文档可以引用多个 Members 文档,一个 Members文档可以引用多个 Band 文档。
class Band include Mongoid::Document has_and_belongs_to_many :members end class Members include Mongoid::Document has_and_belongs_to_many :bands end
当您声明 has_and_belongs_to_many 关联时,两个模型实例存储关联文档的 _id 值的列表。 您可以将 inverse_of 选项设立为 nil,以将关联文档的 _id 值仅存储在一个模型实例中。 以下示例提示 Mongoid 将关联文档的 _id 值仅存储在 Band 类中:
class Band include Mongoid::Document has_and_belongs_to_many :tags, inverse_of: nil end class Tag include Mongoid::Document end
提示
当您更新具有 has_and_belongs_to_many 关联的文档时,Mongoid 会设置更新文档的 updated_at字段,但不会设立关联文档的 updated_at字段。
查询引用的关联
您可以使用聚合管道跨引用的关联查询文档。 聚合管道允许您跨多个集合创建查询并将数据处理为指定的格式。 要学习;了解有关使用聚合管道的更多信息,请参阅聚合指南。
对于简单的查询,可以直接查询关联。 直接查询集合时,只能查询集合本身中的字段和值。 您无法直接查询与您正在查询的集合关联的集合。
示例,考虑以下 Band 和 Tour 类:
class Band include Mongoid::Document has_many :tours field :name, type: String end class Tour include Mongoid::Document belongs_to :band field :year, type: Integer end
以下示例查询 Tour 类中 year 值为 2000 或更大的文档,并保存这些文档的 band_id。 然后,它会在 Band 类中查询具有这些 band_id 值的文档。
band_ids = Tour.where(year: {'$gte' => 2000}).pluck(:band_id) bands = Band.find(band_ids)