Documents¶
On this page
Documents are the core objects in Mongoid and any object that is to be persisted to the
database must include Mongoid::Document
. The representation of a Document in MongoDB
is a BSON object that is very similar to a Ruby hash or JSON object. Documents can be stored
in their own collections in the database, or can be embedded in other Documents n levels deep.
Storage¶
Mongoid by default stores documents in a collection that is the pluralized form of the class name.
For the following Person
class, the collection the document would get stored in would be named people
.
Model class names cannot end with “s”, because it will be considered as the pluralized form of the word. For example “Status” would be considered as the plural form of “Statu”, which will cause a few known problems.
This is a limitation of the ActiveSupport::Inflector#classify
which Mongoid uses to convert
from filenames and collection names to class names. You can overcome this by specifying a custom
inflection rule for your model class. For example, the following code will take care of the model
named Status
.
The collection for the model’s documents can be changed at the class level if you would like them persisted elsewhere. You can also change the database and client the model gets persisted in from the defaults.
The store_in
macro can also take lambdas - a common case for this is multi-tenant applications.
When a document is stored in the database the ruby object will get serialized into BSON and have a structure like so:
Fields¶
Even though MongoDB is a schemaless database and allows data to be stored as strings, Mongoid permits the application to declare the type of data stored in the various fields of a document. Field type declarations affect the following:
1. When assigning values to fields, the values are converted to the specified type.
2. When persisting data to MongoDB, the data is sent in an appropriate type, permitting richer data manipulation within MongoDB or by other tools.
3. When querying documents, query parameters are converted to the specified type before being sent to MongoDB.
4. When retrieving documents from the database, field values are converted to the specified type.
Field type definitions determine how Mongoid behaves when constructing the queries, retrieving and writing fields from the database. Changing the field definitions in a model class does not alter data already stored in MongoDB. To update type or contents of fields of existing documents, the field must be re-saved to the database. Note that, due to Mongoid tracking which attributes on a model change and only saving the changed ones, it may be necessary to explicitly write a field value when changing the type of an existing field without changing the stored values.
Consider a simple class for modeling a person in an application. A person may
have a first name, last name, and middle name. We can define these attributes
on a person by using the field
macro.
Below is a list of valid types for fields.
Array
BigDecimal
Boolean
Date
DateTime
Float
Hash
Integer
BSON::ObjectId
BSON::Binary
Range
Regexp
Set
String
StringifiedSymbol
Symbol
Time
TimeWithZone
If you decide not to specify the type of field with the definition, Mongoid will treat it as an object and not try to typecast it when sending the values to the database. This can be advantageous as the lack of attempted conversion will yield a slight performance gain. However some types are not supported if not defined as fields. You can safely omit type specifications when:
- You’re not using a web front end and values are already properly cast.
- All of your fields are strings.
Types that are not supported as dynamic attributes since they cannot be cast are:
BigDecimal
Date
DateTime
Range
The StringifiedSymbol Type¶
The StringifiedSymbol
field type is the recommended field type for storing
values that should be exposed as symbols to Ruby applications. When using the Symbol
field type,
Mongoid defaults to storing values as BSON symbols. For more information on the
BSON symbol type, see here.
However, the BSON symbol type is deprecated and is difficult to work with in programming languages
without native symbol types, so the StringifiedSymbol
type allows the use of symbols
while ensuring interoperability with other drivers. The StringifiedSymbol
type stores all data
on the database as strings, while exposing values to the application as symbols.
An example usage is shown below:
All non-string values will be stringified upon being sent to the database (via to_s
), and
all values will be converted to symbols when returned to the application. Values that cannot be
converted directly to symbols, such as integers and arrays, will first be converted to strings and
then symbols before being returned to the application.
For example, setting an integer as status
:
If the StringifiedSymbol
type is applied to a field that contains BSON symbols, the values
will be stored as strings instead of BSON symbols on the next save. This permits transparent lazy
migration from fields that currently store either strings or BSON symbols in the database to the
StringifiedSymbol
field type.
Updating Container Fields¶
Be aware that, until MONGOID-2951 is resolved, all fields including container ones must be assigned to for their values to be persisted to the database.
For example, adding to a set like this does not work:
Instead, the field value must be modified outside of the model and assigned back to the model as follows:
Accessing Field Values¶
Mongoid provides several ways of accessing field values.
Note
All of the access methods described below raise
ActiveModel::MissingAttributeError
when the field being accessed is
projected out, either by virtue of not being included in
only or by virtue of being included in
without. This applies to both reads and writes.
Getters & Setters¶
The recommended way is to use the getter and setter methods generated for each declared field:
To use this mechanism, each field must be explicitly declared, or the model class must enable dynamic fields.
Custom Getters & Setters¶
It is possible to explicitly define the getter and setter methods to provide
custom behavior when reading or writing fields, for example value
transformations or storing values under different field names. In this case
read_attribute
and write_attribute
methods can be used to read and
write the values directly into the attributes hash:
read_attribute
& write_attribute
¶
The read_attribute
and write_attribute
methods can be used explicitly
as well. Note that if a field specifies its storage field name, both read_attribute
and write_attribute
accept either the declared field name or the storage field name for operations:
read_attribute
and write_attribute
do not require that a field with
the used name is defined, but writing field values with write_attribute
does not cause the respective field to be defined either:
When read_attribute
is used to access a missing field, it returns nil
.
Hash Access¶
Mongoid model instances define the []
and []=
methods to provide
Hash
style acccess to the attributes. []
is an alias for
read_attribute
and []=
is an alias for write_attribute
; see
the section on read_attribute and write_attribute
for the detailed description of their behavior.
Bulk Attribute Writes¶
In cases where you want to set multiple field values at once, there are a few different ways of accomplishing this as well.
Hash Fields¶
When using a field of type Hash, be wary of adhering to the legal key names for mongoDB, or else the values will not store properly.
Time Fields¶
Time
fields store values as Time
instances in the configured
time zone.
Date
and DateTime
instances are converted to Time
instances upon
assignment to a Time
field:
In the above example, the value was interpreted as the beginning of today in local time, because the application was not configured to use UTC times.
Date Fields¶
Mongoid allows assignment of values of several types to Date
fields:
Date
- the provided date is stored as is.Time
,DateTime
,ActiveSupport::TimeWithZone
- the date component of the value is taken in the value’s time zone.String
- the date specified in the string is used.Integer
,Float
- the value is taken to be a UTC timestamp which is converted to the configured time zone (note thatMongoid.use_utc
has no effect on this conversion), then the date is taken from the resulting time.
In other words, if a date is specified in the value, that date is used without first converting the value to the configured time zone.
As a date & time to date conversion is lossy (it discards the time component),
especially if an application operates with times in different time zones it is
recommended to explicitly convert String
, Time
and DateTime
objects to Date
objects before assigning the values to fields of type
Date
.
DateTime Fields¶
MongoDB stores all times as UTC timestamps. When assigning a value to a
DateTime
field, or when querying a DateTime
field, Mongoid
converts the passed in value to a UTC Time
before sending it to the
MongoDB server.
Time
, ActiveSupport::TimeWithZone
and DateTime
objects embed
time zone information, and the value persisted is the specified moment in
time, in UTC. When the value is retrieved, the time zone in which it is
returned is defined by the configured time zone settings.
Mongoid also supports casting integers and floats to DateTime
. When
doing so, the integers/floats are assumed to be Unix timestamps (in UTC):
If a string is used as a DateTime
field value, the behavior depends on
whether the string includes a time zone. If no time zone is specified,
the default Mongoid time zone is used:
If a time zone is specified, it is respected:
Regular Expression Fields¶
MongoDB supports storing regular expressions in documents, and querying using regular expressions. Of note for Ruby applications, MongoDB uses Perl-compatible regular expressions (PCRE) and Ruby uses Onigmo, which is a fork of Oniguruma regular expression engine. The two regular expression implementations generally provide equivalent functionality but have several important syntax differences.
When a field is declared to be of type Regexp, Mongoid converts Ruby regular
expressions to BSON regular expressions and stores the result in MongoDB.
Retrieving the field from the database produces a BSON::Regexp::Raw
instance:
Use #compile
method on BSON::Regexp::Raw
to get back the Ruby regular
expression:
Note that, if the regular expression was not originally a Ruby one, calling
#compile
on it may produce a different regular expression. For example,
the following is a PCRE matching a string that ends in “hello”:
Compiling this regular expression produces a Ruby regular expression that matches strings containing “hello” before a newline, besides strings ending in “hello”:
This is because the meaning of $
is different between PCRE and Ruby
regular expressions.
Defaults¶
A field can be configured to have a fixed default value:
The default value can also be specified as a Proc
:
Note
Default values that are not Proc
instances are evaluated at class load
time, meaning the following two definitions are not equivalent:
The second definition is most likely the desired one, which causes the time of submission to be set to the current time at the moment of document instantiation.
To set a default which depends on the document’s state, use self
inside the Proc
instance which would evaluate to the document instance
being operated on:
When defining a default value as a Proc
, Mongoid will apply the default
after all other attributes are set and associations are initialized.
To have the default be applied before the other attributes are set,
use the pre_processed: true
field option:
The pre_processed: true
option is also necessary when specifying a custom
default value via a Proc
for the _id
field, to ensure the _id
is set correctly via associations:
Specifying Storage Field Names¶
One of the drawbacks of having a schemaless database is that MongoDB must store all field information along with every document, meaning that it takes up a lot of storage space in RAM and on disk. A common pattern to limit this is to alias fields to a small number of characters, while keeping the domain in the application expressive. Mongoid allows you to do this and reference the fields in the domain via their long names in getters, setters, and criteria while performing the conversion for you.
Field Aliases¶
It is possible to define field aliases. The value will be stored in the destination field but can be accessed from either the destination field or from the aliased field:
Aliases can be removed from model classes using the unalias_attribute
method.
Unaliasing id
¶
unalias_attribute
can be used to remove the predefined id
alias.
This is useful for storing different values in id
and _id
fields:
Custom Fields¶
You can define custom types in Mongoid and determine how they are serialized and deserialized. You simply need to provide three methods on it for Mongoid to call to convert your object to and from MongoDB friendly values.
The instance method mongoize
takes an instance of your object, and converts it
into how it will be stored in the database. In our example above, we want to store our
point object as an array in the form [ x, y ]
.
The class method demongoize
takes an object as how it was stored in the database,
and is responsible for instantiating an object of your custom type. In this case, we
take an array and instantiate a Point
from it.
The class method mongoize
takes an object that you would use to set on your model
from your application code, and create the object as it would be stored in the database.
This is for cases where you are not passing your model instances of your custom type in the setter:
The class method evolve
takes an object, and determines how it is to be transformed
for use in criteria. For example we may want to write a query like so:
Note that when accessing custom fields from the document, you will get a new instance of that object with each call to the getter. This is because Mongoid is generating a new object from the raw attributes on each access.
We need the point object in the criteria to be transformed to a Mongo friendly value when
it is not as well, evolve
is the method that takes care of this. We check if the passed
in object is a Point
first, in case we also want to be able to pass in ordinary arrays instead.
Reserved Names¶
Attempting to define a field on a document that conflicts with a reserved
method name in Mongoid will raise an error. The list of reserved names can
be obtained by invoking the Mongoid.destructive_fields
method.
Field Redefinition¶
By default Mongoid allows redefining fields on a model. To raise an error
when a field is redefined, set the duplicate_fields_exception
configuration option to true
.
With the option set to true, the following example will raise an error:
To define the field anyway, use the overwrite: true
option:
Custom Ids¶
By default, Mongoid defines the _id
field on documents to contain a
BSON::ObjectId
value which is automatically generated by Mongoid.
It is possible to replace the _id
field definition to change the type
of the _id
values or have different default values:
It is possible to omit the default entirely:
If the default on _id
is omitted, and no _id
value is provided by
your application, Mongoid will persist the document without the _id
value. In this case, if the document is a top-level document, an _id
value will be assigned by the server; if the document is an embedded document,
no _id
value will be assigned. Mongoid will not automatically retrieve
this value, if assigned, when the document is persisted - you
must obtain the persisted value (and the complete persisted document) using
other means:
Omitting _id
fields is more common in embedded documents.
Mongoid also defines the id
field aliased to _id
. The id
alias can be removed if desired (such as to integrate
with systems that use the id
field to store value different from _id
.
BSON Symbol Type¶
New applications should use the StringifiedSymbol field type
to store Ruby symbols in the database. The StringifiedSymbol
field type
provides maximum compatibility with other applications and programming languages
and has the same behavior in all circumstances.
Mongoid also provides the deprecated Symbol
field type for serializing
Ruby symbols to BSON symbols. Because the BSON specification deprecated the
BSON symbol type, the bson gem will serialize Ruby symbols into BSON strings
when used on its own. However, in order to maintain backwards compatibility
with older datasets, the mongo gem overrides this behavior to serialize Ruby
symbols as BSON symbols. This is necessary to be able to specify queries for
documents which contain BSON symbols as fields.
To override the default behavior and configure the mongo
gem (and thereby
Mongoid as well) to encode symbol values as strings, include the following code
snippet in your project:
Dynamic Fields¶
By default, Mongoid requires all fields that may be set on a document to
be explicitly defined using field
declarations. Mongoid also supports
creating fields on the fly from an arbitrary hash or documents stored in
the database. When a model uses fields not explicitly defined, such fields
are called dynamic fields.
To enable dynamic fields, include Mongoid::Attributes::Dynamic
module
in the model:
It is possible to use field
declarations and dynamic fields in the same
model class. Attributes for which there is a field
declaration will be
treated according to the field
declaration, with remaining attributes
being treated as dynamic fields.
Attribute values in the dynamic fields must initially be set by either
passing the attribute hash to the constructor, mass assignment via
attributes=
, mass assignment via []=
, using write_attribute
,
or they must already be present in the database.
If an attribute is not present in a particular model instance’s attributes
hash, both the reader and the writer for the corresponding field are not
defined, and invoking them raises NoMethodError
:
Attributes can always be read using mass attribute access or read_attribute
(this applies to models not using dynamic fields as well):
Special Characters in Field Names¶
Mongoid permits dynamic field names to include spaces and punctuation:
Localized Fields¶
Mongoid supports localized fields via i18n.
By telling the field to localize
, Mongoid will under the covers store the field
as a hash of locale/value pairs, but normal access to it will behave like a string.
You can get and set all the translations at once by using the corresponding _translations
method.
Fallbacks¶
Mongoid integrates with i18n fallbacks. To use the fallbacks, the respective functionality must be explicitly enabled.
In a Rails application, set the config.i18n.fallbacks
configuration setting
to true
in your environment and specify the fallback languages:
In a non-Rails application, include the fallbacks module into the I18n backend you are using and specify the fallback languages:
When fallbacks are enabled, if a translation is not present in the active language, translations will be looked up in the fallback languages:
Note
In i18n 1.1, the behavior of fallbacks changed to always require an explicit list of fallback locales rather than falling back to the default locale when no fallback locales have been provided.
Querying¶
When querying for localized fields using Mongoid’s criteria API, Mongoid will automatically alter the criteria to match the current locale.
Indexing¶
If you plan to be querying extensively on localized fields, you should index each of the locales that you plan on searching on.
Dirty Tracking¶
Mongoid supports tracking of changed or “dirty” fields with an API that mirrors that of Active Model. If a defined field has been modified in a model the model will be marked as dirty and some additional behavior comes into play.
Viewing Changes¶
There are various ways to view what has been altered on a model. Changes are recorded from the time a document is instantiated, either as a new document or via loading from the database up to the time it is saved. Any persistence operation clears the changes.
Resetting Changes¶
You can reset changes of a field to its previous value by calling the reset method.
Persistence¶
Mongoid uses dirty tracking as the core of its persistence operations. It looks at the
changes on a document and atomically updates only what has changed, unlike other frameworks
that write the entire document on each save. If no changes have been made, Mongoid will
not hit the database on a call to Model#save
.
Viewing Previous Changes¶
After a document has been persisted, you can see what the changes were previously by
calling Model#previous_changes
.
Read-Only Attributes¶
You can tell Mongoid that certain attributes are read-only. This will allow
documents to be created with these attributes, but changes to them will be
ignored when using mass update methods such as update_attributes
:
If you explicitly try to update or remove a read-only attribute by itself,
a ReadonlyAttribute
exception will be raised:
Inheritance¶
Mongoid supports inheritance in both top level and embedded documents. When a child document inherits from a parent document, the parent document’s fields, associations, validations and scopes are copied to the child document.
In the above example, Canvas
, Browser
and Firefox
will all save in the canvases
collection. An additional attribute _type
is stored in order to make sure when loaded
from the database the correct document is returned. This also holds true for the embedded
documents Circle
, Rectangle
, and Shape
.
Note
When searching for a Circle
, the query will only return documents in the shape collection
where the _type
(or whatever the discriminator key was set to) field has the value Circle
(or
whatever the discriminator value was set to), all other discriminator values will be considered an object
of the Shape class.
Similarly, when querying by parent classes (Canvas
in this example), any documents in the collection
that do not have a discriminator value, or whose discriminator value does not map to either the parent
or any of its descendants, will be returned as instances of the parent class.
Changing the Discriminator Key¶
Mongoid supports changing the discriminator key from the default _type
. There are a few
cases where one might want to do this:
- For optimization: The user might want to use a shorter key like
_t
. - When trying to work with an existing system: It’s possible the user is working with an existing system or dataset that has predefined keys.
There are two ways to change the discriminator key, on the class level and on the global level.
To change the discriminator key on the class level the user can set it directly on the parent class using
the discriminator_key=
method.
Take the above example:
Here a call to the discriminator_key=
setter was added to the parent class. Now, on
creation of a Rectangle or Circle, a shape_type
field will be added.
Note that the discriminator key can only be modified in the parent class, and an error will be raised if trying to set it on the child class.
If the discriminator key is changed after the child class is created, a new field is added with the new discriminator key value, and the old field will remain unchanged. For example:
In this case, on creation of a Rectangle or Circle, there will be both a shape_type
and a _type
field that both default to Rectangle
or Circle
respectively.
The discriminator key can also be set on the global level. Meaning, all classes will
use the globally set discriminator key instead of _type
. Take the above example:
After setting the global discriminator key, all classes will use _the_type
as
the discriminator key and will not contain a _type
field.
Note that when defining the discriminator key on the global level, it must be set before the
child class is defined for the child class to use that global value.
On the global level, however, if the user does not set the discriminator key before defining a child
class, the discriminator field will use the default _type
and not the new global setting in
that child class.
Changing the Discriminator Value¶
Mongoid also supports changing the discriminator value from the default value, which is the class name.
One can change the discriminator value by using the discriminator_value=
method on that specific class.
Take the above example:
Here, a call to the discriminator_value=
setter was added to Circle
.
Now, on creation of a Circle
, the document will contain a field with the key _type
(or whatever the discriminator_key
was changed to)
and the value “round thing.”
Note
Because the discriminator value overrides are declared in child classes,
the child classes potentially found by a query must be loaded prior to
sending that query. In the above example, the Circle
class definition
must be loaded when querying on Shape
if the returned documents could
potentially be instances of Circle
(since autoloading wouldn’t resolve
"round thing"
to Circle
).
Querying Subclasses¶
Querying for subclasses is handled in the normal manner, and although the documents are all in the same collection, queries will only return documents of the correct type, similar to Single Table Inheritance in ActiveRecord.
Associations¶
You can add any type of subclass to a has one or has many association, through either normal setting or through the build and create methods on the association:
Timestamping¶
Mongoid supplies a timestamping module in Mongoid::Timestamps
which
can be included to get basic behavior for created_at
and
updated_at
fields.
You may also choose to only have specific timestamps for creation or modification.
If you want to turn off timestamping for specific calls, use the timeless method:
If you’d like shorter timestamp fields with aliases on them to save space, you can include the short versions of the modules.