Field Types
On this page
Overview
In this guide, you can learn about the field types supported in Mongoid that you can use to define the schema for your MongoDB documents.
MongoDB uses BSON types to represent the data
types stored in document fields. To use BSON data in a Mongoid application,
Mongoid must convert the BSON types to Ruby types at runtime. For example,
when retrieving a document from the database, Mongoid translates a BSON
double
type to use the Ruby Float
type. When you save the
document again, Mongoid converts the field back to a BSON double
.
To learn more about modeling documents in Mongoid, see the Documents guide.
Note
Modifying the field definition in a model class does not change any data stored in the database. To change the data type of a field in the database, you must re-save the data again.
Field Types
You can define field names and types in model classes by using the field
and type
macros. The following example defines the fields of a Person
class:
class Person include Mongoid::Document field :name, type: String field :date_of_birth, type: Date field :weight, type: Float end
The following list provides the field types that you can use in Mongoid:
Array
Bson::Binary
BigDecimal
Mongoid::Boolean
orBoolean
Date
DateTime
Float
Hash
Integer
Object
Bson::ObjectId
Range
Regexp
Set
String
Mongoid::StringifiedSymbol
Time
ActiveSupport::TimeWithZone
Note
Mongoid does not support BSON::Int64
or BSON::Int32
as field types.
Mongoid saves these values to the database correctly, but when you retrieve
the documents, the fields are returned as Integer
types.
Similarly, when querying fields with the BSON::Decimal128
type, Mongoid
returns the fields as BigDecimal
types.
Untyped Fields
If you don't specify a type for a field, Mongoid interprets it as the default Object
type. An untyped field can store values of any type that is directly
serializable to BSON. You can leave a field untyped if the field might contain
different types of data, or if the type of the field's value is not known.
The following example defines a Product
class with an untyped field:
class Product include Mongoid::Document field :name, type: String field :properties end
The type of the properties
field is Object
but varies depending on
the type of data stored in that field. The following example saves data into the
properties
field in two different ways:
product = Product.new(properties: "color=white,size=large") # properties field saved as String: "color=white,size=large" product = Product.new(properties: {color: "white", size: "large"}) # properties field saved as Object: {:color=>"white", :size=>"large"}
Because Mongoid doesn't perform any type conversions on untyped fields when reading from the database, values that require special handling might not be retrieved correctly in as the value of an untyped field. Do not store the following BSON data types in untyped fields:
Date
: Returns asTime
in untyped fieldsDateTime
: Returns asTime
in untyped fieldsRange
: Returns asHash
in untyped fields
Hash
You can store Hash
data in a field by using the Hash
type. When you specify
a field as a Hash
, ensure that you follow the MongoDB Naming
Restrictions to ensure that the values
store properly in the database.
The following example creates a Person
class and specifies the url
field
as a Hash
.
class Person include Mongoid::Document field :first_name field :url, type: Hash end person = Person.new(url: {'home_page' => 'http://www.homepage.com'})
Time
You can store values as BSON Time
instances by using the Time
field value.
Time
fields are stored in the time zone configured for your application. To
learn more about configuring time zones, see the Time Zone Configuration
section of the Application Configuration guide.
The following example creates a Voter
class and specifies that the value of the
registered_at
field is a Time
type:
class Voter include Mongoid::Document field :registered_at, type: Time end Voter.new(registered_at: Date.today)
Note
Storing a Date
or DateTime
value in a field specified as Time
converts the value to Time
when assigned. If you store a string in a
Time
field, Mongoid parses the string by using the Time.parse
method. To learn more about how Mongoid converts queries, see the
Field Type Query Conversions section of the Specify a
Query guide.
Date
You can store the following value types in a field specified as a Date
:
Date
: Stores the value as provided.Time
: Stores the date portion of the value in the value's time zone.DateTime
: Stores the date portion of the value in the value's time zone.ActiveSupport::TimeWithZone
: Stores the date portion of the value in the value's time zone.String
: Stores the date specified in the string.Integer
: Takes the value as if it is a UTC timestamp and converts it into your application's configured time zone. Mongoid then stores the date taken from that timestamp.Float
: Takes the value as if it is a UTC timestamp and converts it into your application's configured time zone. Mongoid then stores the date taken from that timestamp.
Because converting a Time
or DateTime
discards the time portion, we
recommend explicitly converting String
, Time
, and DateTime
, objects
to Date
before assigning them to the field.
Note
When a database contains a string value for a Date
field, the driver
parses the value by using the Time.parse
method, then discards the time
portion. Time.parse
considers values without time zones to be in local
time. To learn more about how Mongoid converts queries, see the
Field Type Query Conversions section of the Specify a
Query guide.
DateTime
When you assign a value to a field defined as a DateTime
or query on these fields, Mongoid converts
the value to a UTC Time
value before sending it to the MongoDB server.
Mongoid saves the value with the time zone embedded in the DateTime
object. When
you retrieve the value, Mongoid converts the UTC time to the time zone
configured for your application.
The following example creates a Ticket
class and specifies the purchased_at
field as a DateTime
field:
class Ticket include Mongoid::Document field :purchased_at, type: DateTime end
If you save an integer or float value to a DateTime
field, the value is treated as
a Unix timestamp in UTC. The following example saves an integer value to the
purchased_at
field:
ticket.purchased_at = 1544803974 ticket.purchased_at # Outputs: Fri, 14 Dec 2018 16:12:54 +0000
If you save a string value to a DateTime
field, Mongoid saves the ticket
with the time zone specified. If a time zone is not specified, Mongoid saves the
value by using the timezone configured as the default for your application:
ticket.purchased_at = 'Mar 4, 2018 10:00:00 +01:00' ticket.purchased_at # Outputs: Sun, 04 Mar 2018 09:00:00 +0000
To learn more about configuring time zones, see the Time Zone Configuration section of the Application Configuration guide.
Note
Mongoid parses string values into DateTime
by using the Time.parse
method, which considers values without time zones to be in local time.
Timestamps
You can include timestamp fields in a class by including the Mongoid::Timestamps
module when you create your class. When you include the Mongoid::Timestamps
,
Mongoid creates the following fields in your class:
created_at
: Stores the time the document was created.updated_at
: Stores the time the document was last updated.
The following example creates a Post
class with timestamp fields:
class Post include Mongoid::Document include Mongoid::Timestamps end
You can also choose to include only the created_at
or updated_at
fields
by including only the Created
or Updated
modules. The following example
creates a Post
class with only the created_at
field, and a Post
class with only the updated_at
field:
class Post include Mongoid::Document include Mongoid::Timestamps::Created end class Post include Mongoid::Document include Mongoid::Timestamps::Updated end
You can shorten the timestamp field names to c_at
and u_at
by setting
the ::Short
option when including the module:
class Post include Mongoid::Document include Mongoid::Timestamps::Short # For c_at and u_at. end class Post include Mongoid::Document include Mongoid::Timestamps::Created::Short # For c_at only. end class Post include Mongoid::Document include Mongoid::Timestamps::Updated::Short # For u_at only. end
You can disable creating the timestamp field for specific operations by calling
the timeless
method on the method call. The following example disables the
timestamps for the save
operation:
post.timeless.save
Regexp
You can store regular expressions in a field by using the Regexp
type.
While MongoDB implements Perl Compatible Regular Expressions (PCRE),
Mongoid uses Ruby's Onigmo library. PCRE and
Onigmo provide generally similar functionality, but there are several syntax
differences. For example, Onigmo uses \A
and \z
to match the beginning and
end of a string, while PCRE uses ^
and $
.
When you declare a field as a Regexp
, Mongoid converts Ruby regular
expressions to BSON regular expressions when storing the result into your
database. The database returns the field as a Bson::Regexp::Raw
instance.
You can use the compile
method on BSON::Regexp::Raw
instances to convert
the data back to a Ruby regular expression.
The following example creates a Token
class and specifies the pattern
field as a Regexp
:
class Token include Mongoid::Document field :pattern, type: Regexp end token = Token.create!(pattern: /hello.world/m) token.pattern # Outputs: /hello.world/m # Reload the token from the database token.reload token.pattern # Outputs: #<BSON::Regexp::Raw:0x0000555f505e4a20 @pattern="hello.world", @options="ms">
Important
Converting a BSON regular expression to a Ruby regular expression might produce a different regular expression than the original. This difference is due to the differences between the Onigmo and PCRE syntaxes. To learn more about regular expressions in Mongoid, see the Regular Expressions section of the Specify a Query guide.
BigDecimal
You can use the BigDecimal
type to store numbers with increased precision.
Mongoid stores BigDecimal
values in two different ways, depending on the
value you set for the Mongoid.map_big_decimal_to_decimal128
configuration property:
If set to
true
, Mongoid storesBigDecimal
values as BSONDecimal128
values.If set to
false
(default), Mongoid storesBigDecimal
values as strings.
Consider the following limitations when setting the
Mongoid.map_big_decimal_to_decimal128
option to true
:
Decimal128
has a limited range and precision.Decimal128
has a maximum value of approximately10^6145
and a minimum of approximately-10^6145
, with a maximum of 34 bits of precision. If you are storing values that are outside of these limits, we recommend storing them as strings instead.Decimal128
accepts signedNaN
values, butBigDecimal
does not. Retrieving signedNaN
Decimal128
values from the database asBigDecimal
returns the value unsigned.Decimal128
maintains trailing zeroes, butBigDecimal
does not. Because of this, retrievingDecimal128
values from the database asBigDecimal
might result in a loss of precision.
Note
When you set the Mongoid.map_big_decimal_to_decimal128
option to false
and store a BigDecimal
into an untyped field, you cannot query the field
as a BigDecimal
. Because the value is stored as a string, querying
the untyped field for a BigDecimal
value does not find the value in the
database. To find the value, you must first convert the query value to a string.
You can avoid this issue by specifying the field as a BigDecimal
type,
instead of as untyped.
StringifiedSymbol
Use the StringifiedSymbol
field type to store values that should be exposed
as symbols to Ruby applications. StringifiedSymbol
allows you to use symbols
while ensuring interoperability with other drivers. This type stores all data on
the database as strings, and converts the strings to symbols when read by the
application. Values that cannot be directly converted to symbols, such as
integers and arrays, are converted into strings and then into symbols.
The following example defines the status
field as a StringifiedSymbol
and
demonstrates how the field is stored and returned:
class Post include Mongoid::Document field :status, type: StringifiedSymbol end # Save status as a symbol post = Post.new(status: :hello) # status is stored as "hello" on the database, but returned as a Symbol post.status # Outputs: :hello # Save status as a string post = Post.new(status: "hello") # status is stored as "hello" in the database, but returned as a Symbol post.status # Outputs: :hello
Specify Field Types as Strings or Symbols
You can use strings or symbols to specify certain field types in Mongoid, instead of
using their class names. The following example specifies the order_num
field by
using the class name, a string, and a symbol:
class Order include Mongoid::Document # Class Name field :order_num, type: Integer # Symbol field :order_num, type: :integer # String field :order_num, type: "integer" end
The following table provides the field types that can you can specify as strings or symbols:
Class Name | Symbol | String |
---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Custom Field Types
You can create custom field types and define how Mongoid serializes and deserializes them. To create a custom field type, define a class that implements the following methods:
mongoize
: Takes an instance of your custom type and converts it to an object that MongoDB can store.demongoize
: Takes an object from MongoDB and converts it to an instance of your custom type.evolve
: Takes an instance of your custom type and converts it to a criteria that MongoDB can use to query the database.
The following example creates a custom field type called Point
and
implements the preceding methods:
class Point attr_reader :x, :y def initialize(x, y) @x, @y = x, y end # Converts an object of this instance into an array def mongoize [ x, y ] end class << self # Takes any possible object and converts it to how it is # stored in the database. def mongoize(object) case object when Point then object.mongoize when Hash then Point.new(object[:x], object[:y]).mongoize else object end end # Gets the object as it's stored in the database and instantiates # this custom class from it. def demongoize(object) if object.is_a?(Array) && object.length == 2 Point.new(object[0], object[1]) end end # Converts the object supplied to a criteria and converts it # into a queryable form. def evolve(object) case object when Point then object.mongoize else object end end end end
In the preceding example, the mongoize
instance method accepts an instance
of your custom type object and converts it to an Array
to store in the
database. The mongoize
class method accepts objects of all types and
converts them to similar types that can be stored in the database. Mongoid uses
the mongoize
class method when it calls the getter and setter methods.
The demongoize
method converts the stored Array
value into the custom
Point
type. The Mongoid uses this method when it calls the getter.
The evolve
method converts the custom Point
type into a queryable
Array
type, and converts all other types to object
. Mongoid uses this
method when it calls a method that queries the database.
Phantom Custom Field Types
You can create a custom field type that saves a different value to the database than the value assigned in the application. This can be useful to have descriptive values in the application while storing more compact values in the database.
The following example creates a ColorMapping
type that uses the name of the
color in the application, but stores the color as an integer in the database:
class ColorMapping MAPPING = { 'black' => 0, 'white' => 1, }.freeze INVERSE_MAPPING = MAPPING.invert.freeze class << self def mongoize(object) MAPPING[object] end def demongoize(object) INVERSE_MAPPING[object] end def evolve(object) MAPPING.fetch(object, object) end end end class Profile include Mongoid::Document field :color, type: ColorMapping end profile = Profile.new(color: 'white') profile.color # Outputs: "white" # Sets "color" field to 0 in MongoDB profile.save!
Dynamic Fields
You can instruct Mongoid to create fields dynamically by including the
Mongoid::Attributes::Dynamic
module in your model. This allows Mongoid to
create fields based on an arbitrary hash, or based on the documents already
stored in the database.
The following example creates a Person
class with dynamic fields:
class Person include Mongoid::Document include Mongoid::Attributes::Dynamic end
Tip
You can specify both fixed fields and dynamic fields within the same class. In this case, Mongoid treats all attributes for properties with field definitions according to their field type, and all other attributes as dynamic.
When using dynamic fields in your application, you must initially set the value in one of the following ways:
Pass the attribute hash to the constructor.
Assign values by using the
attributes=
method.Assign values by using the
[]=
method.Assign values by using the
write_attribute
method.Work with values that are already present in the database.
If you don't initially set the value by using one of the preceding options,
invoking the attribute returns a NoMethodError
.
Reserved Characters
Both Mongoid and the MongoDB Query API reserve the .
character to separate field
names in nested documents and the $
character at the beginning of a
string to indicate a query operator. Because of this, you should avoid using these
characters in your field names.
If your application requires the use of these characters, you can access the
fields by calling the send
method. The following example creates a User
class with fields that contain reserved characters. It then accesses the fields
by using the send
method:
class User include Mongoid::Document field :"first.last", type: String field :"$_amount", type: Integer end user = User.first user.send(:"first.last") # Outputs: Mike.Trout user.send(:"$_amount") # Outputs: 42650000
You can also access these fields by calling the read_attribute
method.
Important
Because updating and replacing fields containing these reserved characters
requires special operators, calling getters and setters on these fields
raises an InvalidDotDollarAssignment
exception.