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, most uses will be with web applications where
form parameters always come to the server as strings. Mongoid provides an easy mechanism for
transforming these strings into their appropriate types through the definition of fields
in a Mongoid::Document
.
Keep in mind that the field definitions determine how Mongoid behaves when writing and reading fields from the database. Changing the Model definition in the Model file does not change existing data. If you’d like to update a field type or options of existing data, you must write a script to do so. You will get errors otherwise.
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 fields macro.
Below is a list of valid types for fields.
Array
BigDecimal
Boolean
Date
DateTime
Float
Hash
Integer
BSON::ObjectId
BSON::Binary
Range
Regexp
String
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
Accessing Field Values¶
When a field is defined, Mongoid provides several different ways of accessing the field.
You can define getters/setters that will be called when using the dot notation.
In cases where you want to set multiple field values at once, there are a few different ways of handling 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.
Defaults¶
You can tell a field in Mongoid to always have a default value if nothing has been provided. Defaults are either static values or lambdas/procs.
Be wary that default values that are not defined as lambdas or procs are evaluated at class load time, so the following 2 definitions are not equivalent. (One would probably prefer the second, which is evaluated at document creation time.)
If you want to set a default with a dependency on the document’s state, self inside a lambda or proc evaluates to the document instance.
When defining a default value as a proc, Mongoid will apply the default after all other
attributes are set. If you want this to happen before the other attributes, set pre_processed: true
.
Aliasing Fields¶
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.
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¶
If you define a field on your document that conflicts with a reserved method name in Mongoid,
the configuration will raise an error. For a list of these you may look at
Mongoid.destructive_fields
.
Custom Ids¶
For cases when you do not want to have BSON::ObjectId
ids, you can override Mongoid’s
_id
field and set them to whatever you like.
Dynamic Fields¶
By default, Mongoid doesn’t support dynamic fields. You can tell Mongoid that you want
to add dynamic fields by including Mongoid::Attributes::Dynamic
in a model.
Mongoid::Attributes::Dynamic
will allow attributes to get set and persisted on the
document even if a field was not defined for them. There is a slight ‘gotcha’ however when
dealing with dynamic attributes in that Mongoid is not completely lenient about the use of
method_missing
and breaking the public interface of the Document class.
When dealing with dynamic attributes, the following rules apply:
If the attribute exists in the document, Mongoid will provide you with your standard getter and setter methods. For example, consider a person who has an attribute of “gender” set on the document:
If the attribute does not already exist on the document, Mongoid will not provide you with
the getters and setters and will enforce normal method_missing
behavior. In this case you
must use the other provided accessor methods:
([]
and []=
) or (read_attribute
and write_attribute
).
Localized Fields¶
Mongoid now supports localized fields without the need of an additional gem.
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¶
When using fallbacks, Mongoid will automatically use them when a translation is not available.
For Rails applications, set the fallbacks configuration setting to true in your environment.
For non-Rails applications, you must include the fallbacks module straight to the I18n gem.
When the fallbacks are defined, if a translation is not present, Mongoid will fallback in order of the defined locales.
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.
Notes On 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
.
Readonly Attributes¶
You can tell Mongoid that certain attributes are readonly. This will allow documents to be created with these attributes, but changes to them will be filtered out.
If you explicitly try to update or remove the attribute by itself, then a ReadonlyAttribute
error will be raised.
Inheritance¶
Mongoid supports inheritance in both root and embedded documents. In scenarios where documents are inherited from their fields, relations, validations and scopes get copied down into their child documents, but not vise-versa.
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
.
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.