Documents

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.

Defining Documents in Mongoid

Consider a simple class for modeling a person in an application. A person may have a first name, last name, and middle initial. We can define these attributes on a person by defining fields on our person object. Fields will default to type String in Mongoid.

person.rb:
class Person
  include Mongoid::Document
  field :first_name
  field :middle_initial
  field :last_name
end

Fields other than strings must define a type in order to be properly typecasted when being set. Currently the valid types in Mongoid are: Array, BigDecimal, Boolean, Date, DateTime, Float, Integer, String, Symbol, Time and any object that inherits from Mongoid::Document. BigDecimals will get converted to and from Strings in the database.

person.rb:
class Person
  include Mongoid::Document
  field :birthday, :type => Date
end

Fields may also have default values, set by adding a default option when defining the field. Note that default values MUST match the type of the field, and can accept lambdas.

person.rb:
class Person
  include Mongoid::Document
  field :blood_alcohol_level, :type => Float, :default => 0.0
end
Instantiating Documents

Documents are instantiated by calling new on the document and passing it a hash of attributes. If an attribute exists in the hash that is not defined as a field or is an association, then an exception will get raised. You can also pass a block.

person = Person.new(:first_name => "Ludwig", :last_name => "Beethoven")

person = Person.new do |p|
  p.first_name = "Ludwig"
  p.last_name = "Beethoven"
end
Controlling Access to Fields

You can protect attributes from mass assignment by using attr_protected, or provide the inverse functionality with attr_accessible. This is for cases where sensitive fields cannot be accidentally set through a form submission or similar.

When using attr_protected all other fields will be able to be set via mass assignment.

class Person
  include Mongoid::Document
  field :first_name
  attr_protected :_id
end

When using attr_accessible all other fields will not be able to be set via mass assignment.

class Person
  include Mongoid::Document
  field :first_name
  field :last_name
  attr_accessible :first_name, :last_name
end
Dynamic Attributes

By default Mongoid supports dynamic attributes - that is it 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:
    person[:gender] = "Male"
    person.gender # => returns "Male"
    person.gender = "Female" #=> will set gender to "Female"
  • 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 ):
    person[:gender] # => returns nil
    person[:gender] = "Male" # => set gender to "Male"
    
    person.read_attribute(:age) # => returns nil
    person.write_attribute(:age, 35) # => sets age to 35
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.

Fork me on GitHub!