Inheritance
On this page
Overview
In this guide, you can learn how to implement inheritance into your Mongoid models. Inheritance allows you to apply the characteristics of one "parent" class to one or more "child" classes.
Mongoid supports inheritance in top-level and embedded documents. When a child model class inherits from a parent class, Mongoid copies the parent class's fields, associations, validations, and scopes to the child class.
Assign Inheritance
When creating a child model class, use the <
character to implement
inheritance from a specified parent class. The following model classes
demonstrate how to create parent and child classes between the
Person
, Employee
, and Manager
models:
class Person include Mongoid::Document field :name, type: String end class Employee < Person field :company, type: String field :tenure, type: Integer scope :new_hire, ->{ where(:tenure.lt => 1) } end class Manager < Employee end
Mongoid saves instances of Person
, Employee
, and Manager
in
the people
collection. Mongoid sets the _type
discriminator
field to the model class name in documents to ensure that documents are
returned as the expected types when you perform read operations.
Embedded Documents
You can also implement an inheritance pattern in embedded associations.
Similar to the behavior of top-level model classes, Mongoid sets the
_type
discriminator field in embedded documents depending on the
model class used to create them.
The following example adds an embedded association to the Person
model and creates parent and child models for the embedded Info
class:
class Person include Mongoid::Document field :name, type: String embeds_many :infos end ... class Info include Mongoid::Document field :active, type: Boolean embedded_in :person end class Phone < Info field :value, type: Float field :country, type: String end class Email < Info field :value, type: String field :category, type: String end
Query Behavior
When you query on a child model class, the query returns only documents
in which the value of the _type
field matches the queried class or
further child classes. For example, if you query on the Employee
class, the query returns documents from the people
collection in
which the _type
value is either "Employee"
or "Manager"
. All
other discriminator values are considered as instances of the parent
Person
class.
When querying on a parent class such as Person
, Mongoid returns
documents that meet any of the following criteria:
Discriminator value is the name of the parent class or any of the child classes. For example,
"Person"
,"Employee"
, or"Manager"
.Lacks a discriminator value.
Discriminator value does not map to either the parent or any of its child classes. For example,
"Director"
or"Specialist"
.
Change the Discriminator Key
You might change the discriminator key from the default field name
_type
for any of the following reasons:
Optimization: You can select a shorter key such as
_t
.Consistency with an existing system: You might be using an existing system or dataset that has predefined keys.
You can change the discriminator key on the class level
or on the global level. To change the discriminator key on the class
level, you can set the custom key name on the parent class by using the
discriminator_key
method.
The following example demonstrates how to set a custom discriminator key when defining a model class:
class Person include Mongoid::Document field :name, type: String self.discriminator_key = "sub_type" end
When you create an instance of Person
or any of its child classes,
Mongoid adds the sub_type
field to documents in MongoDB.
Note
You can change the discriminator key only on the parent class. Mongoid raises an error if you set a custom key on any child class.
If you change the discriminator key after defining a child class, Mongoid adds the new key field, but the old field is unchanged. For example, suppose you add the following code to your application after defining your model classes:
Person.discriminator_key = "sub_type"
In this case, when you create an instance of a child class such as
Employee
, Mongoid adds both the sub_type
and _type
fields to
the document.
You can also change the discriminator key at the global level, so that
all classes use the specified key instead of the _type
field.
You can set a global key by adding the following code to your application before defining any model classes:
Mongoid.discriminator_key = "sub_type"
All classes use sub_type
as the discriminator key and do not include
the _type
field.
Note
You must set the discriminator key on the global level before defining
any child classes for the classes to use that global value. If you set
the global key after defining child classes, your saved documents
contain the default _type
field.
Change the Discriminator Value
You can customize the value that Mongoid sets as the discriminator value
in MongoDB. Use the discriminator_value
method when defining a
class to customize the discriminator value, as shown in the following
example:
class Employee include Mongoid::Document field :company, type: String self.discriminator_value = "Worker" end
When you create an instance of Employee
, the document's _type
discriminator field has a value of "Worker"
instead of the class
name.
Note
Because the discriminator value customization is declared in child classes, you must load the child classes retrieved by a query before sending that query.
In the preceding example, the Employee
class definition must be
loaded before you query on Person
if the returned documents include
instances of Employee
. Autoloading cannot resolve the
discriminator value "Worker"
to return documents as instances of
Employee
.
Embedded Associations
You can create any type of parent class or child class in an embedded
association by assignment or by using the build
and create
methods. You can pass desired model class as the second parameter to the
build
and create
methods to instruct Mongoid to create that
specific instance as an emdedded document.
The following code creates an instance of Employee
, then
demonstrates how to add embedded documents by using the different
creation methods:
# Creates a new Employee instance e = Employee.create( name: "Lance Huang", company: "XYZ Communications", tenure: 2 ) # Builds an Info object e.infos.build({ active: true }) # Builds a Phone object e.infos.build( { active: true, value: 1239007777, country: "USA" }, Phone ) # Creates an Email object e.infos.create( { active: true, value: "l.huang@company.com", category: "work" }, Email ) # Creates and assigns an Email object p = Email.new(active: false, value: "lanceh11@mymail.com", category: "personal" ) e.infos << p # Saves the Employee instance to database e.save
The following document is stored in the people
database:
{ "_id": {...}, "name": "Lance Huang", "company": "XYZ Communications", "tenure": 2, "_type": "Employee", "infos": [ { "_id": {...}, "active": true, "value": "l.huang@company.com", "category": "work", "_type": "Email" }, { "_id": {...}, "active": false, "value": "lanceh11@mymail.com", "category": "personal", "_type": "Email" }, { "_id": {...}, "active": true, "_type": "Info" }, { "_id": {...}, "active": true, "value": 1239007777, "country": "USA", "_type": "Phone" } ] }
Persistence Contexts
You can change the persistence context of a child class from
the persistence context of its parent to store the document in a
different location than the default. By using the store_in
method,
you can store an instance of a child class in a different collection,
database, or cluster than an instance of the parent model.
The following model definitions demonstrate how to use the
store_in
method to store instances of Employee
and Manager
in a different collection than the people
collection:
class Person include Mongoid::Document end class Employee < Person # Specifies "employees" as target collection store_in collection: :employees end class Manager < Employee # Specifies "managers" as target collection store_in collection: :managers end
Note
Mongoid still adds the discriminator field to stored documents.
If you set an alternate target collection on some child classes and not others, instances of the classes without specified collections are stored in the collection associated with the parent class.
Note
When you change the target collection for a child class, instances of that class do not appear in the results from queries on the parent class.
Additional Information
To learn more about configuring the target collection for your operations, see the Persistence Configuration guide.