字段类型
在此页面上
Overview
在本指南中,您可以学习;了解Mongoid 支持的字段类型,您可以使用这些字段类型来定义MongoDB文档的模式。
MongoDB使用 BSON 类型来表示存储在文档字段中的数据类型。要在 Mongoid应用程序中使用BSON数据,Mongoid 必须在运行时将BSON 类型转换为Ruby类型。示例,从数据库检索文档时,Mongoid 会将BSONdouble
类型转换为使用RubyFloat
类型。当您再次保存文档时,Mongoid 会将字段转换回BSONdouble
。
要学习;了解有关在 Mongoid 中对文档进行建模的更多信息,请参阅 文档指南。
注意
修改模型类中的字段定义不会更改数据库中存储的任何数据。要更改数据库中字段的数据类型,必须再次重新保存数据。
字段类型
您可以使用 field
和 type
宏在模型类中定义字段名称和类型。以下示例定义了 Person
类的字段:
class Person include Mongoid::Document field :name, type: String field :date_of_birth, type: Date field :weight, type: Float end
以下列表提供了可以在 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
注意
Mongoid 不支持BSON::Int64
或 BSON::Int32
作为字段类型。 Mongoid 会将这些值正确保存到数据库,但当您检索文档时,这些字段会以 Integer
类型返回。
同样,在查询 BSON::Decimal128
类型的字段时,Mongoid 以 BigDecimal
类型返回字段。
非类型化字段
如果您没有为字段指定类型,Mongoid 会将其解释为默认的Object
类型。无类型字段可以存储可直接序列化为BSON的任何类型的值。如果字段可能包含不同类型的数据,或者字段值的类型未知,则可以将该字段留为非类型化。
以下示例定义了一个具有非类型化字段的 Product
类:
class Product include Mongoid::Document field :name, type: String field :properties end
properties
字段的类型为 Object
,但会根据该字段中存储的数据类型而有所不同。以下示例通过两种不同方式将数据保存到 properties
字段:
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"}
由于 Mongoid 在读取数据库时不会对非类型化字段执行任何类型转换,因此可能无法将需要特殊处理的值作为非类型化字段的值正确检索。请勿在非类型化字段中存储以下BSON数据类型:
Date
:在非类型化字段中返回为Time
DateTime
:在非类型化字段中返回为Time
Range
:在非类型化字段中返回为Hash
哈希
您可以使用Hash
Hash
类型将 数据存储在字段中。当您将字段指定为Hash
时,请确保遵循MongoDB命名限制,以确保这些值正确存储在数据库中。
以下示例创建一个 Person
类并将 url
字段指定为 Hash
。
class Person include Mongoid::Document field :first_name field :url, type: Hash end person = Person.new(url: {'home_page' => 'http://www.homepage.com'})
时间
您可以使用 字段值将值存储为BSONTime
Time
实例。Time
字段存储在为应用程序配置的时区域中。要学习;了解有关配置时区的更多信息,请参阅应用程序配置指南的时区配置部分。
以下示例创建一个 Voter
类并指定 registered_at
字段的值是 Time
类型:
class Voter include Mongoid::Document field :registered_at, type: Time end Voter.new(registered_at: Date.today)
注意
在指定为Date
DateTime
的字段中存储 或Time
值会在赋值时将该值转换为Time
。如果您将字符串存储在Time
字段中,Mongoid 将使用Time.parse
方法解析该字符串。要进一步学习;了解Mongoid 如何转换查询,请参阅 指定查询指南的 字段类型查询转换 部分。
Date
您可以在指定为 Date
的字段中存储以下值类型:
Date
:按提供的值进行存储。Time
:将值的日期部分存储在值的时区域中。DateTime
:将值的日期部分存储在值的时区域中。ActiveSupport::TimeWithZone
:将值的日期部分存储在值的时区域中。String
:存储字符串中指定的日期。Integer
:将该值视为 UTC 时间戳,并将其转换为应用程序的已配置时区域。然后,Mongoid 会存储从该时间戳中获取的日期。Float
:将该值视为 UTC 时间戳,并将其转换为应用程序的已配置时区域。然后,Mongoid 会存储从该时间戳中获取的日期。
由于转换 Time
或 DateTime
会丢弃时间部分,因此我们建议在将 String
、Time
和 DateTime
对象赋值给字段之前将其显式转换为 Date
。
注意
当数据库包含Date
字段的字符串值时,驾驶员会使用Time.parse
方法解析该值,然后丢弃时间部分。Time.parse
将没有时区的值视为当地时间。要进一步学习;了解Mongoid 如何转换查询,请参阅 指定查询指南的 字段类型查询转换 部分。
日期时间
当您为定义为 DateTime
的字段赋值或对这些字段查询时,Mongoid 会在将其发送到MongoDB 服务器之前将该值转换为 UTC Time
值。 Mongoid 将该值与时区域一起保存在 DateTime
对象中。当您检索该值时,Mongoid 会将 UTC 时间转换为为应用程序配置的时区域。
以下示例创建一个 Ticket
类并将 purchased_at
字段指定为 DateTime
字段:
class Ticket include Mongoid::Document field :purchased_at, type: DateTime end
如果将整数或浮点值保存到 DateTime
字段,则该值将被视为 UTC 格式的 Unix 时间戳。以下示例将整数值保存到 purchased_at
字段:
ticket.purchased_at = 1544803974 ticket.purchased_at # Outputs: Fri, 14 Dec 2018 16:12:54 +0000
如果将字符串值保存到 DateTime
字段,Mongoid 会使用指定的时区域保存票证。如果未指定时区域,Mongoid 会使用应用程序配置为默认时区的时区来保存值:
ticket.purchased_at = 'Mar 4, 2018 10:00:00 +01:00' ticket.purchased_at # Outputs: Sun, 04 Mar 2018 09:00:00 +0000
要学习;了解有关配置时区的更多信息,请参阅应用程序配置指南的时区配置部分。
注意
Mongoid 使用 Time.parse
方法将字符串值解析为 DateTime
,该方法将没有时区的值视为采用当地时间。
时间戳
您可以通过在创建类时包含 Mongoid::Timestamps
模块,在类中包含时间戳字段。当您包含 Mongoid::Timestamps
时,Mongoid 会在您的类中创建以下字段:
created_at
:存储文档的创建时间。updated_at
:存储文档上次更新时间。
以下示例创建了一个带有时间戳字段的 Post
类:
class Post include Mongoid::Document include Mongoid::Timestamps end
您也可以通过仅包含 Created
或 Updated
模块来选择仅包含 created_at
或 updated_at
字段。以下示例创建了一个仅包含 created_at
字段的Post
类以及一个仅包含 updated_at
字段的Post
类:
class Post include Mongoid::Document include Mongoid::Timestamps::Created end class Post include Mongoid::Document include Mongoid::Timestamps::Updated end
您可以通过在包含该模块时设置 ::Short
选项,将时间戳字段名称缩短为 c_at
和 u_at
:
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
您可以通过在方法调用上调用 timeless
方法来禁用为特定操作创建时间戳字段。以下示例为 save
操作禁用时间戳:
post.timeless.save
regexp
您可以使用 Regexp
类型将正则表达式存储在字段中。
MongoDB实施Perl兼容正则表达式 (PCRE) ,而 Mongoid 使用 Ruby 的 Onigmo 库。 PCRE 和 Onigmo 提供的功能大体相似,但存在一些语法差异。示例,Onigmo 使用\A
和\z
来匹配字符串的开头和结尾,而 PCRE 使用^
和$
。
当您将字段声明为 Regexp
时,Mongoid 在将结果存储到数据库时会将Ruby正则表达式转换为BSON正则表达式。数据库会将该字段作为 Bson::Regexp::Raw
实例返回。您可以在 BSON::Regexp::Raw
实例上使用 compile
方法,将数据转换回Ruby正则表达式。
以下示例创建一个 Token
类并将 pattern
字段指定为 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">
BigDecimal
您可以使用 BigDecimal
类型来存储更高精度的数字。 Mongoid 以两种不同的方式存储 BigDecimal
值,具体取决于您为 Mongoid.map_big_decimal_to_decimal128
配置属性设立的值:
如果设立为
true
,Mongoid 会将BigDecimal
值存储为BSONDecimal128
值。如果设立为
false
(默认),Mongoid 将BigDecimal
值存储为字符串。
将 Mongoid.map_big_decimal_to_decimal128
选项设置为 true
时,请考虑以下限制:
Decimal128
范围和精度有限。Decimal128
的最大值约为10^6145
,最小值约为-10^6145
,最大精度为 34 位。如果您要存储的值超出了这些限制,我们建议您将其存储为字符串。Decimal128
接受带符号的NaN
值,但BigDecimal
不接受。从数据库中检索有符号NaN
Decimal128
值作为BigDecimal
时,会返回无符号值。Decimal128
保留尾随零,但BigDecimal
不保留。因此,从数据库检索Decimal128
值作为BigDecimal
可能会导致精度损失。
注意
当您将 Mongoid.map_big_decimal_to_decimal128
选项设立为 false
并将 BigDecimal
存储到非类型化字段中时,您无法将该字段作为 BigDecimal
查询。由于该值存储为字符串,因此在非类型化字段中查询 BigDecimal
值不会在数据库中找到该值。要查找该值,必须首先将查询值转换为字符串。
您可以将该字段指定为 BigDecimal
类型(而不是非类型化)来避免此问题。
StringifiedSymbol
使用 StringifiedSymbol
字段类型存储应作为符号向Ruby应用程序公开的值。 StringifiedSymbol
允许您使用符号,同时确保与其他驱动程序的互操作性。此类型将数据库中的所有数据存储为字符串,并在应用程序读取时将字符串转换为符号。无法直接转换为符号的值(例如整数和数组)将转换为字符串,然后转换为符号。
以下示例将 status
字段定义为 StringifiedSymbol
,并演示了如何存储和返回该字段:
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
将字段类型指定为字符串或符号
在 Mongoid 中,您可以使用字符串或符号来指定某些字段类型,而不是使用它们的类名。以下示例使用类名称、字符串和符号指定 order_num
字段:
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
下表提供了可以指定为字符串或符号的字段类型:
类名 | 符号 | 字符串 |
---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
自定义字段类型
您可以创建自定义字段类型并定义 Mongoid 如何对其进行序列化和反序列化。要创建自定义字段类型,请定义一个实现以下方法的类:
mongoize
:获取自定义类型的实例并将其转换为MongoDB可以存储的对象。demongoize
:从MongoDB获取一个对象并将其转换为自定义类型的实例。evolve
:获取自定义类型的实例并将其转换为MongoDB可用于查询数据库的条件。
以下示例创建了一个名为 Point
的自定义字段类型并实现了上述方法:
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
在前面的示例中,mongoize
实例方法接受自定义类型对象的实例,并将其转换为Array
以存储在数据库中。mongoize
类方法接受所有类型的对象,并将它们转换为可以存储在数据库中的类似类型。 Mongoidmongoize
在调用 getter 和 setter 方法时使用 类方法。
demongoize
方法将存储的 Array
值转换为自定义 Point
类型。 Mongoid 在调用 getter 时使用此方法。
evolve
方法将自定义 Point
类型转换为可查询的 Array
类型,并将所有其他类型转换为 object
。 Mongoid 在调用查询数据库的方法时使用此方法。
Phantom 自定义字段类型
您可以创建自定义字段类型,将与应用程序中分配的值不同的值保存到数据库中。这对于在应用程序中拥有描述性值,同时在数据库中存储更紧凑的值非常有用。
以下示例创建了一个 ColorMapping
类型,该类型在应用程序中使用颜色的名称,但将颜色作为整数存储在数据库中:
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!
动态字段
您可以通过在模型中包含 Mongoid::Attributes::Dynamic
模块来指示 Mongoid 动态创建字段。这允许 Mongoid 基于任意哈希或基于已存储在数据库中的文档创建字段。
以下示例将创建一个带有动态字段的 Person
类:
class Person include Mongoid::Document include Mongoid::Attributes::Dynamic end
提示
您可以在同一个类中同时指定固定字段和动态字段。在这种情况下,Mongoid 根据字段类型对待具有字段定义的属性的所有属性,并将所有其他属性视为动态属性。
在应用程序中使用动态字段时,首先必须通过以下方式之一设立值:
将属性哈希传递给构造函数。
使用
attributes=
方法赋值。使用
[]=
方法赋值。使用
write_attribute
方法赋值。使用数据库中已存在的值。
如果最初未使用上述选项之一设立该值,则调用该属性将返回 NoMethodError
。
保留字符
Mongoid 和MongoDB查询API都保留 .
字符来分隔嵌套文档中的字段名称,并保留字符串开头的 $
字符来指示查询运算符。因此,应避免在字段名称中使用这些字符。
如果您的应用程序需要使用这些字符,您可以通过调用 send
方法访问权限这些字段。以下示例创建了一个 User
类,其中的字段包含保留字符。然后,它使用 send
方法访问这些字段:
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
您还可以通过调用 read_attribute
方法访问权限这些字段。
重要
由于更新和替换包含这些保留字符的字段需要特殊操作符,因此对这些字段调用 getter 和 setter 会引发 InvalidDotDollarAssignment
异常。