Learn the "why" behind slow queries and how to fix them in our 2-Part Webinar.
Register now >
Docs 菜单
Docs 主页
/ /

Use BSON Field Types

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

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

To learn more about modeling documents in Mongoid, see the Use the Document Module in Your Model guide.

注意

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

您可以使用 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'})

您可以使用 Time字段值将值存储为BSON Time 实例。 Time 字段存储在为应用程序配置的时区域中。 要学习;了解有关配置时区的更多信息,请参阅应用程序配置指南的时区配置部分。

以下示例创建一个 Voter 类并指定 registered_at字段的值是 Time 类型:

class Voter
include Mongoid::Document
field :registered_at, type: Time
end
Voter.new(registered_at: Date.today)

注意

在指定为 Time 的字段中存储 DateDateTime 值会在赋值时将该值转换为 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 类型将正则表达式存储在字段中。

While 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类方法接受所有类型的对象,并将它们转换为可以存储在数据库中的类似类型。Mongoid 在调用 getter 和 setter 方法时使用 mongoize 类方法。

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 异常。

后退

字段行为

在此页面上