Docs 菜单
Docs 主页
/ / /
Mongoid
/

字段类型

在此页面上

  • Overview
  • 字段类型
  • 非类型化字段
  • 哈希
  • 时间
  • Date
  • 日期时间
  • 时间戳
  • regexp
  • BigDecimal
  • StringifiedSymbol
  • 将字段类型指定为字符串或符号
  • 自定义字段类型
  • Phantom 自定义字段类型
  • 动态字段
  • 保留字符

在本指南中,您可以学习;了解Mongoid 支持的字段类型,您可以使用这些字段类型来定义MongoDB文档的模式。

MongoDB使用 BSON 类型来表示存储在文档字段中的数据类型。要在 Mongoid应用程序中使用BSON数据,Mongoid 必须在运行时将BSON 类型转换为Ruby类型。示例,从数据库检索文档时,Mongoid 会将BSONdouble 类型转换为使用RubyFloat 类型。当您再次保存文档时,Mongoid 会将字段转换回BSONdouble

要学习;了解有关在 Mongoid 中对文档进行建模的更多信息,请参阅 文档指南。

注意

修改模型类中的字段定义不会更改数据库中存储的任何数据。要更改数据库中字段的数据类型,必须再次重新保存数据。

您可以使用 fieldtype 宏在模型类中定义字段名称和类型。以下示例定义了 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 or Boolean

  • Date

  • DateTime

  • Float

  • Hash

  • Integer

  • Object

  • Bson::ObjectId

  • Range

  • Regexp

  • Set

  • String

  • Mongoid::StringifiedSymbol

  • Time

  • ActiveSupport::TimeWithZone

注意

Mongoid 不支持BSON::Int64BSON::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:按提供的值进行存储。

  • Time:将值的日期部分存储在值的时区域中。

  • DateTime:将值的日期部分存储在值的时区域中。

  • ActiveSupport::TimeWithZone:将值的日期部分存储在值的时区域中。

  • String:存储字符串中指定的日期。

  • Integer:将该值视为 UTC 时间戳,并将其转换为应用程序的已配置时区域。然后,Mongoid 会存储从该时间戳中获取的日期。

  • Float:将该值视为 UTC 时间戳,并将其转换为应用程序的已配置时区域。然后,Mongoid 会存储从该时间戳中获取的日期。

由于转换 TimeDateTime 会丢弃时间部分,因此我们建议在将 StringTimeDateTime 对象赋值给字段之前将其显式转换为 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

您也可以通过仅包含 CreatedUpdated 模块来选择仅包含 created_atupdated_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_atu_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 类型将正则表达式存储在字段中。

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">

重要

将BSON正则表达式转换为Ruby正则表达式可能会生成与原始正则表达式不同的正则表达式。这种差异是由于 Onigmo 和 PCRE 语法之间的差异造成的。要学习;了解有关 Mongoid 中正则表达式的更多信息,请参阅指定查询指南中的正则表达式部分。

您可以使用 BigDecimal 类型来存储更高精度的数字。 Mongoid 以两种不同的方式存储 BigDecimal 值,具体取决于您为 Mongoid.map_big_decimal_to_decimal128 配置属性设立的值:

  • 如果设立为 true,Mongoid 会将 BigDecimal 值存储为BSON Decimal128 值。

  • 如果设立为 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字段类型存储应作为符号向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

下表提供了可以指定为字符串或符号的字段类型:

类名
符号
字符串

Array

:array

"Array"

BigDecimal

:big_decimal

"BigDecimal"

BSON::Binary

:binary

"BSON::Binary"

Mongoid::Boolean

:boolean

"Mongoid::Boolean"

Date

:date

"Date"

DateTime

:date_time

"DateTime"

Float

:float

"Float"

Hash

:hash

"Hash"

Integer

:integer

"Integer"

BSON::ObjectId

:object_id

"BSON::ObjectId"

Range

:range

"Range"

Regexp

:regexp

"Regexp"

Set

:set

"Set"

String

:string

"String"

StringifiedSymbol

:stringified_symbol

"StringifiedSymbol"

Symbol

:symbol

"Symbol"

Time

:time

"Time"

您可以创建自定义字段类型并定义 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 在调用查询数据库的方法时使用此方法。

您可以创建自定义字段类型,将与应用程序中分配的值不同的值保存到数据库中。这对于在应用程序中拥有描述性值,同时在数据库中存储更紧凑的值非常有用。

以下示例创建了一个 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 异常。

后退

文档 (Document)