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

继承

本页内容

  • 概述
  • 更改区分键
  • 更改区分值
  • 查询子类
  • 关联
  • 持久化上下文

Mongoid 支持在顶级文档和嵌入式文档中实现继承。当一个子文档继承自父文档时,父文档的字段、关联、验证和作用域都会复制到子文档中。

class Canvas
include Mongoid::Document
field :name, type: String
embeds_many :shapes
end
class Browser < Canvas
field :version, type: Integer
scope :recent, ->{ where(:version.gt => 3) }
end
class Firefox < Browser
end
class Shape
include Mongoid::Document
field :x, type: Integer
field :y, type: Integer
embedded_in :canvas
end
class Circle < Shape
field :radius, type: Float
end
class Rectangle < Shape
field :width, type: Float
field :height, type: Float
end

在上面的示例中,CanvasBrowserFirefox 都会保存在 canvases 集合中。为了确保从数据库加载时返回正确的文档,会存储一个额外的属性 _type。这也适用于嵌入式文档 CircleRectangleShape

注意

当搜索 Circle 时,查询只会返回形状集合中 _type(或设置的任何区分键)字段值为 Circle(或设置的任何区分值)的文档,所有其他区分值将被视为 Shape 类的对象。

类似地,当按父类(本例中的 Canvas)查询时,任何集合中没有区分值,或者区分值没有映射到父类或其任何子类的文档都将作为父类的实例返回。

Mongoid 支持将区分键从默认的 _type 进行更改。在某些情况下,可能需要这样做

  1. 为了优化:用户可能希望使用更短的关键字,例如 _t

  2. 在尝试与现有系统协同工作时:用户可能正在使用具有预定义关键字的存在系统或数据集。

有两种方式可以更改判别符关键字,在类级别和全局级别。要在类级别更改判别符关键字,用户可以直接在父类上使用 discriminator_key= 方法进行设置。以上面的示例

class Shape
include Mongoid::Document
field :x, type: Integer
field :y, type: Integer
embedded_in :canvas
self.discriminator_key = "shape_type"
end
class Circle < Shape
field :radius, type: Float
end
class Rectangle < Shape
field :width, type: Float
field :height, type: Float
end

在此示例中,在父类中添加了对 discriminator_key= 设置器的调用。现在,在创建 Rectangle 或 Circle 时,将添加一个 shape_type 字段。

请注意,判别符关键字只能在父类中修改,如果试图在子类中设置它,则会引发错误。

如果子类创建后更改判别符关键字,将添加一个新的字段,其关键字值为新的判别符关键字,而旧字段将保持不变。例如

class Shape
include Mongoid::Document
field :x, type: Integer
field :y, type: Integer
embedded_in :canvas
end
class Circle < Shape
field :radius, type: Float
end
class Rectangle < Shape
field :width, type: Float
field :height, type: Float
end
Shape.discriminator_key = "shape_type"

在这种情况下,在创建 Rectangle 或 Circle 时,将同时存在一个 shape_type 和一个 _type 字段,这两个字段都默认为 RectangleCircle

判别符关键字也可以在全局级别设置。这意味着所有类都将使用全局设置的判别符关键字,而不是 _type。以上面的示例

Mongoid.discriminator_key = "_the_type"
class Shape
include Mongoid::Document
field :x, type: Integer
field :y, type: Integer
embedded_in :canvas
end
class Circle < Shape
field :radius, type: Float
end
class Rectangle < Shape
field :width, type: Float
field :height, type: Float
end

设置全局判别符关键字后,所有类都将使用 _the_type 作为判别符关键字,并且不会包含 _type 字段。

请注意,在全局级别定义判别符关键字时,必须在定义子类之前设置,以便子类可以使用该全局值。然而,在全局级别,如果用户在定义子类之前没有设置判别符关键字,判别符字段将使用默认的 _type,而不是该子类中的新全局设置。

Mongoid 还支持从默认值更改判别符值,默认值是类名。可以通过在特定类上使用 discriminator_value= 方法来更改判别符值。

以上面的示例

class Shape
include Mongoid::Document
field :x, type: Integer
field :y, type: Integer
embedded_in :canvas
end
class Circle < Shape
field :radius, type: Float
self.discriminator_value = "round thing"
end
class Rectangle < Shape
field :width, type: Float
field :height, type: Float
end

在此示例中,向 Circle 添加了对 discriminator_value= 设置器的调用。现在,在创建 Circle 时,文档将包含一个具有 _type 键(或更改后的 discriminator_key)的值 "round thing" 的字段。

注意

由于判别器值覆盖在子类中声明,因此查询可能找到的子类必须在发送查询之前加载。在上面的示例中,当在Shape上查询时,必须加载Circle类的定义,如果返回的文档可能是Circle的实例(因为自动加载不会将"round thing"解析为Circle)。

查询子类的处理方式正常,尽管文档都在同一个集合中,但查询只会返回正确类型的文档,类似于ActiveRecord中的单表继承。

# Returns Canvas documents and subclasses
Canvas.where(name: "Paper")
# Returns only Firefox documents
Firefox.where(name: "Window 1")

您可以通过正常设置或通过关联上的构建和创建方法将任何类型的子类添加到一对一或一对多关联中。

firefox = Firefox.new
# Builds a Shape object
firefox.shapes.build({ x: 0, y: 0 })
# Builds a Circle object
firefox.shapes.build({ x: 0, y: 0 }, Circle)
# Creates a Rectangle object
firefox.shapes.create({ x: 0, y: 0 }, Rectangle)
rect = Rectangle.new(width: 100, height: 200)
firefox.shapes

Mongoid 允许子类的持久化上下文从父类的持久化上下文改变。这意味着,通过使用 store_in 方法,我们可以将子类的文档存储在不同的集合中(以及不同的数据库、客户端)中,而不仅仅是存储在父类的集合中。

class Shape
include Mongoid::Document
store_in collection: :shapes
end
class Circle < Shape
store_in collection: :circles
end
class Square < Shape
store_in collection: :squares
end
Shape.create!
Circle.create!
Square.create!

在子类上设置集合会导致这些子类的文档存储在指定的集合中,而不是存储在父类的集合中。

> db.shapes.find()
{ "_id" : ObjectId("62fe9a493282a43d6b725e10"), "_type" : "Shape" }
> db.circles.find()
{ "_id" : ObjectId("62fe9a493282a43d6b725e11"), "_type" : "Circle" }
> db.squares.find()
{ "_id" : ObjectId("62fe9a493282a43d6b725e12"), "_type" : "Square" }

如果只有部分子类设置了集合,而其他子类没有设置,则设置了集合的子类将文档存储在这些集合中,而没有设置集合的子类将文档存储在父类的集合中。

注意

请注意,改变子类的存储集合会导致该子类的文档在查询其父类时不再出现在结果中。

返回

字段定义

© . All rights reserved.