BSON 教程
在此页面上
本教程讨论如何使用 Ruby BSON 库。
安装
可以从 Rubygems 安装BSON库 手动或使用捆绑器。
要手动安装 gem:
gem install bson
要使用捆绑器安装 gem,请在 Gemfile
中包含以下内容:
gem 'bson'
BSON 库与 MRI >= 2.5 和 JRuby >= 9.2 兼容。
与 ActiveSupport 一起使用
默认情况下,不会加载 ActiveSupport 定义的类(例如 TimeWithZone)的序列化,以避免 BSON 对 ActiveSupport 的硬依赖。 在也使用 ActiveSupport 的应用程序中使用 BSON 时,必须明确要求 ActiveSupport 相关代码:
require 'bson' require 'bson/active_support'
BSON 序列化
通过在 Ruby 对象上调用to_bson
来获取 Ruby 对象的原始 BSON 表示,这将返回BSON::ByteBuffer
。 例如:
"Shall I compare thee to a summer's day".to_bson 1024.to_bson
要从 BSON 生成对象,请对要实例化的类调用from_bson
并向其传递BSON::ByteBuffer
实例。
String.from_bson(byte_buffer) BSON::Int32.from_bson(byte_buffer)
字节缓冲区
BSON 库 4.0 引入在 MRI 和 JRuby 中使用原生字节缓冲区,而不是使用StringIO
,以提高性能。
写作
要创建用于写入的ByteBuffer
(即 序列化为 BSON),实例化不带参数的BSON::ByteBuffer
:
buffer = BSON::ByteBuffer.new
要将原始字节写入字节缓冲区而不进行转换,请使用put_byte
和put_bytes
方法。 它们将字节字符串作为参数并将该字符串复制到缓冲区中。 put_byte
强制该参数为长度为 1 的字符串; put_bytes
接受任意长度的字符串。 字符串可以包含 null 字节。
buffer.put_byte("\x00") buffer.put_bytes("\xff\xfe\x00\xfd")
注意
put_byte
且put_bytes
在将参数写入字节缓冲区之前不写入 BSON 类型字节。
后续写入方法写入 BSON 规范 中特定类型的对象 。请注意,方法名称指示的类型优先于参数的类型,例如,如果将浮点值赋予put_int32
,则会将其强制转换为整数,并将生成的整数写入字节缓冲区。
要将 UTF-8 字符串(BSON 类型 0x02)写入字节缓冲区,请使用put_string
:
buffer.put_string("hello, world")
请注意,BSON 字符串始终以 UTF-8 编码。 因此,该参数必须采用 UTF-8 格式或可转换为 UTF-8 的编码格式(即 非二进制)。 如果参数采用的编码不是 UTF-8,则首先将该字符串转换为 UTF-8,然后将 UTF-8 编码版本写入缓冲区。 该字符串必须采用其声明的编码有效,如果编码为 UTF-8,则必须为有效的 UTF-8。 该字符串可能包含 null 字节。
BSON 规范还定义了 CString 类型,例如用于文档键。 要将 CString 写入缓冲区,请使用put_cstring
:
buffer.put_cstring("hello, world")
与常规字符串一样,BSON 中的 CString 必须采用 UTF-8 编码。 如果参数不是 UTF-8 格式,则会将其转换为 UTF-8 格式,并将生成的字符串写入缓冲区。 与put_string
不同,传递给put_cstring
的参数的 UTF-8 编码不能有任何 null 字节,因为 BSON 中的 CString 序列化格式以 null 终止。
与put_string
不同, put_cstring
也接受符号和整数。 在所有情况下,参数在写入之前都会进行字符串化:
buffer.put_cstring(:hello) buffer.put_cstring(42)
要将 32 位或 64 位整数写入字节缓冲区,请分别使用put_int32
和put_int64
方法。 请注意,Ruby 整数可以是任意大;如果写入的值超出 32 位或 64 位整数的范围,则put_int32
和put_int64
会引发RangeError
。
buffer.put_int32(12345) buffer.put_int64(123456789012345)
注意
如果put_int32
或put_int64
提供了浮点参数,则这些参数首先会被强制转换为整数,然后将整数写入字节缓冲区。
要将 64 位浮点值写入字节缓冲区,请使用put_double
:
buffer.put_double(3.14159)
要获取字节字符串形式的序列化数据(例如,通过套接字发送数据),请对缓冲区调用to_s
:
buffer = BSON::ByteBuffer.new buffer.put_string('testing') socket.write(buffer.to_s)
注意
ByteBuffer
分别追踪读取和写入位置。无法在写入时倒回缓冲区 - rewind
只影响读取位置。
读取
要创建用于读取的ByteBuffer
(即 从 BSON 反序列化),使用字节字符串作为参数来实例化BSON::ByteBuffer
:
buffer = BSON::ByteBuffer.new(string) # a read mode buffer.
从缓冲区读取数据是通过以下 API 完成的:
buffer.get_byte # Pulls a single byte from the buffer. buffer.get_bytes(value) # Pulls n number of bytes from the buffer. buffer.get_cstring # Pulls a null-terminated string from the buffer. buffer.get_double # Pulls a 64-bit floating point from the buffer. buffer.get_int32 # Pulls a 32-bit integer (4 bytes) from the buffer. buffer.get_int64 # Pulls a 64-bit integer (8 bytes) from the buffer. buffer.get_string # Pulls a UTF-8 string from the buffer.
要从缓冲区的开头重新开始读取,请使用rewind
:
buffer.rewind
注意
ByteBuffer
分别追踪读取和写入位置。rewind
仅影响读取位置。
支持的类
在 BSON 规范中具有表示形式并将具有为其定义的to_bson
方法的核心 Ruby 类是: Object
、 Array
、 FalseClass
、 Float
、 Hash
、 Integer
、 BigDecimal
、 NilClass
、 Regexp
、 String
、 Symbol
(已弃用)、 Time
、 TrueClass
。
除了核心 Ruby 对象之外,BSON 还提供了一些特定于规范的特殊类型:
BSON::Binary
使用BSON::Binary
对象存储任意二进制数据。 Binary
对象可以从二进制字符串构造,如下所示:
BSON::Binary.new("binary_string") # => <BSON::Binary:0x47113101192900 type=generic data=0x62696e6172795f73...>
默认情况下,使用 BSON 二进制子类型 0 ( :generic
) 创建Binary
对象。 可以显式指定子类型以指示字节对特定类型的数据进行编码:
BSON::Binary.new("binary_string", :user) # => <BSON::Binary:0x47113101225420 type=user data=0x62696e6172795f73...>
有效子类型为:generic
、 :function
、 :old
、 :uuid_old
、 :uuid
、 :md5
和:user
。
可以使用data
和type
属性从Binary
实例中检索数据和子类型,如下所示:
binary = BSON::Binary.new("binary_string", :user) binary.data => "binary_string" binary.type => :user
注意
BSON::Binary
对象始终以BINARY
编码存储数据,而无论传递给构造函数的字符串采用哪种编码:
str = "binary_string" str.encoding # => #<Encoding:US-ASCII> binary = BSON::Binary.new(str) binary.data # => "binary_string" binary.data.encoding # => #<Encoding:ASCII-8BIT>
UUID 方法
要从符合 RFC 4122 的字符串表示形式创建 UUID BSON::Binary(二进制子类型 4),请使用from_uuid
方法:
uuid_str = "00112233-4455-6677-8899-aabbccddeeff" BSON::Binary.from_uuid(uuid_str) # => <BSON::Binary:0x46986653612880 type=uuid data=0x0011223344556677...>
要将 UUID BSON::Binary 字符串化为符合 RFC 4122 的表示形式,请使用to_uuid
方法:
binary = BSON::Binary.new("\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xAA\xBB\xCC\xDD\xEE\xFF".force_encoding('BINARY'), :uuid) => <BSON::Binary:0x46942046606480 type=uuid data=0x0011223344556677...> binary.to_uuid => "00112233-4455-6677-8899aabbccddeeff"
在调用from_uuid
和to_uuid
方法时,可以显式指定标准表示形式:
binary = BSON::Binary.from_uuid(uuid_str, :standard) binary.to_uuid(:standard)
请注意, :standard
表示形式只能与子类型为:uuid
(而非:uuid_old
)的二进制文件一起使用。
旧版 UUID
存储在子类型 3 ( :uuid_old
) 的 BSON::Binary 对象中的数据可以按三种不同字节顺序之一持久保存,具体取决于创建数据的驱动程序。 字节顺序为 CSharp 传统、Java 传统和 Python 传统。 Python 旧版字节顺序与标准 RFC 4122 字节顺序相同; C# 旧版字节顺序和 Java 旧版字节顺序交换了一些字节。
包含旧版 UUID 的二进制对象不会对 UUID 的存储格式进行编码。因此,与旧版 UUID 格式相互转换的方法将所需的格式或表示形式作为参数。应用程序可以复制旧版 UUID 二进制对象,而不知道它们以哪种字节顺序存储数据。
提供了以下处理旧版 UUID 的方法,以便与以旧版 UUID 格式存储数据的现有部署实现互操作性。建议新应用程序仅使用:uuid
(子类型 4)格式,该格式符合 RFC 4122。
要将旧版 UUID BSON::Binary 字符串化,请使用to_uuid
方法指定所需的表示形式。接受的表示形式为:csharp_legacy
、 :java_legacy
和:python_legacy
。 请注意,如果不指定表示形式,则无法对旧版 UUID BSON::Binary 进行字符串化。
binary = BSON::Binary.new("\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xAA\xBB\xCC\xDD\xEE\xFF".force_encoding('BINARY'), :uuid_old) => <BSON::Binary:0x46942046606480 type=uuid data=0x0011223344556677...> binary.to_uuid # => ArgumentError (Representation must be specified for BSON::Binary objects of type :uuid_old) binary.to_uuid(:csharp_legacy) # => "33221100-5544-7766-8899aabbccddeeff" binary.to_uuid(:java_legacy) # => "77665544-3322-1100-ffeeddccbbaa9988" binary.to_uuid(:python_legacy) # => "00112233-4455-6677-8899aabbccddeeff"
要从 UUID 的字符串表示形式创建旧版 UUID BSON::Binary,请使用from_uuid
方法指定所需的表示形式:
uuid_str = "00112233-4455-6677-8899-aabbccddeeff" BSON::Binary.from_uuid(uuid_str, :csharp_legacy) # => <BSON::Binary:0x46986653650480 type=uuid_old data=0x3322110055447766...> BSON::Binary.from_uuid(uuid_str, :java_legacy) # => <BSON::Binary:0x46986653663960 type=uuid_old data=0x7766554433221100...> BSON::Binary.from_uuid(uuid_str, :python_legacy) # => <BSON::Binary:0x46986653686300 type=uuid_old data=0x0011223344556677...>
这些方法可用于从一种表示形式转换为另一种表示形式:
BSON::Binary.from_uuid('77665544-3322-1100-ffeeddccbbaa9988',:java_legacy).to_uuid(:csharp_legacy) # => "33221100-5544-7766-8899aabbccddeeff"
BSON::Code
表示 JavaScript 代码的字符串。
BSON::Code.new("this.value = 5;")
BSON::CodeWithScope
注意
从 MongoDB 4.2.1 开始, CodeWithScope
类型已弃用。 从 MongoDB 4.4 开始,各种服务器命令和操作符(例如$where
)已删除对CodeWithScope
的支持。 使用 MongoDB 4.4 及更高版本时,请使用其他 BSON types 和操作符。
表示带有哈希值的 JavaScript 代码字符串。
BSON::CodeWithScope.new("this.value = age;", age: 5)
BSON::DBRef
这是BSON::Document
的子类,为 DBRef 的collection、ID 和数据库提供访问器。
BSON::DBRef.new({"$ref" => "collection", "$id" => "id"}) BSON::DBRef.new({"$ref" => "collection", "$id" => "id", "database" => "db"})
注意
BSON::DBRef 构造函数将验证给定的哈希值,如果不是有效的 DBRef,则会引发 ArgumentError。 如果提供的 DBRef 无效, BSON::ExtJSON.parse_obj
和Hash.from_bson
不会引发错误,而是会解析哈希或反序列化 BSON::文档。
注意
如果所有 BSON 文档是有效的 DBRef,则它们都将被反序列化为 BSON::DBRef 的实例,否则它们将被反序列化为 BSON::Document 的实例。 即使从Hash
类进行调用也是如此:
bson = {"$ref" => "collection", "$id" => "id"}.to_bson.to_s loaded = Hash.from_bson(BSON::ByteBuffer.new(bson)) => {"$ref"=>"collection", "$id"=>"id"} loaded.class => BSON::DBRef
为了向后兼容 MongoDB Ruby 驱动程序版本 2.17 及更早版本,还可以使用旧版驱动程序 API 来构造BSON::DBRef
。 此 API 已弃用,并将在bson-ruby
的未来版本中删除:
BSON::DBRef.new("collection", BSON::ObjectId('61eeb760a15d5d0f9f1e401d')) BSON::DBRef.new("collection", BSON::ObjectId('61eeb760a15d5d0f9f1e401d'), "db")
BSON::Document
这是Hash
的子类,它将所有键存储为字符串,但允许使用符号键访问它们。
BSON::Document[:key, "value"] BSON::Document.new
注意
所有 BSON 文档都会反序列化为 BSON::文档(或 BSON::DBRef,如果它们恰好是有效的 DBRef)的实例,即使是从Hash
类进行调用也是如此:
bson = {test: 1}.to_bson.to_s loaded = Hash.from_bson(BSON::ByteBuffer.new(bson)) => {"test"=>1} loaded.class => BSON::Document
BSON::MaxKey
表示 BSON 中的值,该值始终比另一个值更高。
BSON::MaxKey.new
BSON::MinKey
表示 BSON 中的值,与另一个值相比,该值始终为较低值。
BSON::MinKey.new
BSON::ObjectId
表示给定计算机上对象的 12 字节唯一标识符。
BSON::ObjectId.new
BSON::Timestamp
表示具有起始值和增量值的特殊时间。
BSON::Timestamp.new(5, 30)
BSON::Undefined
表示未提供的值的占位符。
BSON::Undefined.new
BSON::Decimal128
表示基于十进制的 128 位浮点值,能够以精确的精度模拟十进制舍入。
# Instantiate with a String BSON::Decimal128.new("1.28") # Instantiate with a BigDecimal d = BigDecimal(1.28, 3) BSON::Decimal128.new(d)
BSON::Decimal128 与 BigDecimal
BigDecimal
from_bson
和to_bson
方法在底层使用相同的BSON::Decimal128
方法。 这会导致对可序列化为 BSON 的BigDecimal
值以及可从现有decimal128
BSON 值反序列化的值施加一些限制。 进行此更改是因为将BigDecimal
实例序列化为BSON::Decimal128
实例可以提高 MongoDB 中查询和实例的灵活性。对BigDecimal
的限制如下:
decimal128
的范围和精度有限,而BigDecimal
在范围和精度方面没有限制。decimal128
的最大值约为10^6145
,最小值约为-10^6145
,最大精度为 34 位。decimal128
能够接受带符号的NaN
值,而BigDecimal
不能。 所有反序列化为BigDecimal
实例的带符号NaN
值都将是无符号的。decimal128
在序列化到 BSON 和从 BSON 反序列化时保留尾随零。 但是,BigDecimal
不保留尾随零,因此使用BigDecimal
可能会导致精度不足。
注意
在 BSON 5.0 中, decimal128
默认反序列化为BigDecimal
。 为了将 BSON 文档中的decimal128
值反序列化为BSON::Decimal128
,可以在from_bson
上设置mode: :bson
选项。
Symbol
BSON 规范定义了一种符号类型,允许往返 Ruby Symbol
值(即 Ruby Symbol``is encoded into a BSON symbol
and a BSON symbol is decoded into a Ruby ``Symbol
)。 但是,由于大多数编程语言没有原生符号类型,为了促进互操作性,MongoDB 弃用了 BSON 符号类型,鼓励使用字符串。
注意
在 BSON 中,哈希键始终是字符串。 非字符串值在用作哈希键时将被字符串化:
Hash.from_bson({foo: 'bar'}.to_bson) # => {"foo"=>"bar"} Hash.from_bson({1 => 2}.to_bson) # => {"1"=>2}
默认情况下,BSON 库将Symbol
哈希值编码为字符串,并将 BSON 符号解码为 Ruby Symbol
值:
{foo: :bar}.to_bson.to_s # => "\x12\x00\x00\x00\x02foo\x00\x04\x00\x00\x00bar\x00\x00" # 0x02 is the string type Hash.from_bson(BSON::ByteBuffer.new("\x12\x00\x00\x00\x02foo\x00\x04\x00\x00\x00bar\x00\x00".force_encoding('BINARY'))) # => {"foo"=>"bar"} # 0x0E is the symbol type Hash.from_bson(BSON::ByteBuffer.new("\x12\x00\x00\x00\x0Efoo\x00\x04\x00\x00\x00bar\x00\x00".force_encoding('BINARY'))) # => {"foo"=>:bar}
要强制将 Ruby 符号编码为 BSON 符号,请将 Ruby 符号包装在BSON::Symbol::Raw
中:
{foo: BSON::Symbol::Raw.new(:bar)}.to_bson.to_s # => "\x12\x00\x00\x00\x0Efoo\x00\x04\x00\x00\x00bar\x00\x00"
JSON 序列化
某些 BSON 类型在 JSON 中具有特殊表示形式。 它们如下所示,在对其调用to_json
时会自动在表单中进行序列化。
对象 | JSON |
---|---|
BSON::Binary | { "$binary" : "\x01", "$type" : "md5" } |
BSON::Code | { "$code" : "this.v = 5" } |
BSON::CodeWithScope | { "$code" : "this.v = value", "$scope" : { v => 5 }} |
BSON::DBRef | { "$ref" : "collection", "$id" : { "$oid" : "id" }, "$db" : "database" } |
BSON::MaxKey | { "$maxKey" : 1 } |
BSON::MinKey | { "$minKey" : 1 } |
BSON::ObjectId | { "$oid" : "4e4d66343b39b68407000001" } |
BSON::Timestamp | { "t" : 5, "i" : 30 } |
Regexp | { "$regex" : "[abc]", "$options" : "i" } |
时间实例
Ruby 中的时间可以达到纳秒精度。 BSON(和 MongoDB)中的时间只能具有毫秒精度。 当 Ruby Time
实例序列化为 BSON 或扩展 JSON 时,时间将精确到毫秒。
注意
时间一如既往地四舍五入。 如果时间早于 Unix 纪元(UTC 时间 1970 年 1 月 1 日 00:00:00),时间的绝对值将增加:
time = Time.utc(1960, 1, 1, 0, 0, 0, 999_999) time.to_f # => -315619199.000001 time.floor(3).to_f # => -315619199.001
注意
JRuby(从9.2.11.0版本开始) 将 Unix 之前的纪元时间向上舍入而不是向下舍入 。bson-Ruby 可以解决这个问题,并在 JRuby 上进行序列化时正确减少时间。
由于此下限,强烈建议应用程序使用整数数学来执行所有时间计算,因为浮点计算的不精确性可能会产生意外的结果。
DateTime 实例
BSON 仅支持将时间存储为自 Unix 纪元以来的秒数。 Ruby 的DateTime
实例可以序列化为 BSON,但当反序列化 BSON 时,这些时间将作为Time
实例返回。
DateTime
Ruby 中的日历类支持非公历。 当非公历DateTime
实例被序列化时,它们首先会转换为公历,并将公历中的相应日期存储在数据库中。
日期实例
BSON 仅支持将时间存储为自 Unix 纪元以来的秒数。 Ruby 的Date
实例可以序列化为 BSON,但当反序列化 BSON 时,这些时间将作为Time
实例返回。
序列化Date
实例时,使用的时间值是Date
所指日期的午夜 (UTC)。
正则表达式
MongoDB 和 Ruby 都提供了使用正则表达式的工具,但它们使用正则表达式引擎。 以下小节详细介绍了 Ruby 正则表达式和 MongoDB 正则表达式之间的区别,并描述了如何使用两者。
Ruby 与 MongoDB 正则表达式
MongoDB Server 使用 通过 PCRE 库实现的与 Perl 兼容的正则表达式 和 Ruby 正则表达式 使用 Onigmo 正则表达式引擎 实现 ,这是 Oniguruma 的一个分支 。这两个正则表达式实现通常提供相同的功能,但有几个重要的语法差异,如下所述。
遗憾的是,没有简单的方法可以以编程方式将 PCRE 正则表达式转换为等效的 Ruby 正则表达式,并且当前没有针对 PCRE 的 Ruby 绑定。
选项/标志/修饰符
Ruby 和 PCRE 正则表达式都支持修饰符。 这些在 Ruby 中也称为“选项”,在 PCRE 中也称为“标志”。 s
和m
修饰符在 Ruby 和 PCRE 中的含义有所不同:
Ruby 没有
s
修饰符,而 Rubym
修饰符执行与 PCREs
修饰符相同的功能,即使句点 (.
) 匹配包括换行符在内的任何字符。 令人困惑的是,Ruby 文档将m
修饰符称为“启用多行模式”。Ruby 始终以与 PCRE 的多行模式等效的方式运行,该模式由 PCRE 正则表达式中的
m
修饰符启用。 在 Ruby 中,^
锚点始终指行首,$
锚点始终指行尾。
在编写用于 Ruby 和 PCRE 环境(包括 MongoDB Server 和大多数其他 MongoDB)的表达式(以下称为“可移植表达式”)时,请避免使用^
和$
锚点。以下部分提供了有关创作可移植正则表达式的变通方法和建议。
^
锚点
在 Ruby 正则表达式中, ^
锚点始终指行首。 在 PCRE 正则表达式中, ^
锚点默认指输入的开头,而m
标志会将其含义更改为行的开头。
Ruby 和 PCRE 正则表达式都支持\A
锚点来引用输入的开头,而与修饰符无关。
编写可移植正则表达式时:
使用
\A
锚点来引用输入的开头。使用
^
锚点引用行首(这需要在 PCRE 正则表达式中设置m
标志)。 或者使用以下结构之一,其作用与修饰符无关:-(?:\A|(?<=\n))
(处理 LF 和 CR+LF 行尾)-(?:\A|(?<=[\r\n]))
(处理 CR、LF 和 CR+LF 行尾)
$
锚点
在 Ruby 正则表达式中, $
锚点始终指代行尾。 在 PCRE 正则表达式中, $
锚点默认指输入的末尾,而m
标志会将其含义更改为行尾。
Ruby 和 PCRE 正则表达式都支持\z
锚点来引用输入结束,而与修饰符无关。
编写可移植正则表达式时:
使用
\z
锚点来引用输入的结尾。使用
$
锚点引用行首(这需要在 PCRE 正则表达式中设置m
标志)。 或者使用以下结构之一,其作用与修饰符无关:-(?:\z|(?=\n))
(处理 LF 和 CR+LF 行尾)-(?:\z|(?=[\n\n]))
(处理 CR、LF 和 CR+LF 行尾)
BSON::Regexp::Raw
类
由于没有简单的方法以编程方式将 PCRE 正则表达式转换为等效的 Ruby 正则表达式,因此 bson-Ruby 提供了BSON::Regexp::Raw
类来保存 MongoDB/PCRE 正则表达式。 在本文档中,此类的实例称为“BSON 正则表达式”。
可以使用作为字符串的正则表达式文本和可选的 PCRE 修饰符来创建此类的实例:
BSON::Regexp::Raw.new("^b403158") # => #<BSON::Regexp::Raw:0x000055df63186d78 @pattern="^b403158", @options=""> BSON::Regexp::Raw.new("^Hello.world$", "s") # => #<BSON::Regexp::Raw:0x000055df6317f028 @pattern="^Hello.world$", @options="s">
BSON::Regexp
模块包含在 Ruby Regexp
类中,因此可以省略BSON::
前缀:
Regexp::Raw.new("^b403158") # => #<BSON::Regexp::Raw:0x000055df63186d78 @pattern="^b403158", @options=""> Regexp::Raw.new("^Hello.world$", "s") # => #<BSON::Regexp::Raw:0x000055df6317f028 @pattern="^Hello.world$", @options="s">
正则表达式转换
要将 Ruby 正则表达式转换为 BSON 正则表达式,请实例化一个BSON::Regexp::Raw
对象,如下所示:
regexp = /^Hello.world/ bson_regexp = BSON::Regexp::Raw.new(regexp.source, regexp.options) # => #<BSON::Regexp::Raw:0x000055df62e42d60 @pattern="^Hello.world", @options=0>
请注意, BSON::Regexp::Raw
构造函数接受 Ruby 数字选项和 PCRE 修饰符字符串。
要将 BSON 正则表达式转换为 Ruby 正则表达式,请对 BSON 正则表达式调用compile
方法:
bson_regexp = BSON::Regexp::Raw.new("^hello.world", "s") bson_regexp.compile # => /^hello.world/m bson_regexp = BSON::Regexp::Raw.new("^hello", "") bson_regexp.compile # => /^hello.world/ bson_regexp = BSON::Regexp::Raw.new("^hello.world", "m") bson_regexp.compile # => /^hello.world/
请注意,在第一个示例中, s
PCRE 修饰符会转换为m
Ruby 修饰符,而后两个示例则会转换为相同的正则表达式,即使原始 BSON 正则表达式具有不同的含义。
当 BSON 正则表达式使用不可移植的^
和$
锚点时,到 Ruby 正则表达式的转换可能会改变其含义:
BSON::Regexp::Raw.new("^hello.world", "").compile =~ "42\nhello world" # => 3
当 Ruby 正则表达式转换为 BSON 正则表达式时(例如,作为查询的一部分发送到服务器),BSON 正则表达式始终具有m
修饰符集,反映^
和$
正则表达式中的锚点。
读取和写入
Ruby 和 BSON 表达式都实现了用于序列化为 BSON 的to_bson
方法:
regexp_ruby = /^b403158/ # => /^b403158/ regexp_ruby.to_bson # => #<BSON::ByteBuffer:0x007fcf20ab8028> _.to_s # => "^b403158\x00m\x00" regexp_raw = Regexp::Raw.new("^b403158") # => #<BSON::Regexp::Raw:0x007fcf21808f98 @pattern="^b403158", @options=""> regexp_raw.to_bson # => #<BSON::ByteBuffer:0x007fcf213622f0> _.to_s # => "^b403158\x00\x00"
Regexp
和BSON::Regexp::Raw
类都实现了from_bson
类方法,该方法从 BSON 字节缓冲区反序列化正则表达式。 这两个类的方法都会返回一个BSON::Regexp::Raw
实例,必须使用compile
方法将该实例转换为 Ruby 正则表达式,如上所述。
byte_buffer = BSON::ByteBuffer.new("^b403158\x00\x00") regex = Regexp.from_bson(byte_buffer) # => #<BSON::Regexp::Raw:0x000055df63100d40 @pattern="^b403158", @options=""> regex.pattern # => "^b403158" regex.options # => "" regex.compile # => /^b403158/
键顺序
BSON 文档保留键的顺序,因为文档存储为键值对列表。 Ruby 中的哈希值也会保留键顺序;因此,在将哈希序列化为 BSON 文档时,将遵循 Ruby 中指定的键顺序;而在将 BSON 文档反序列化为哈希时,文档中的键顺序将与哈希中的键顺序相匹配。
重复键
BSON 规范允许 BSON 文档具有重复键,因为文档存储为键值对列表。 应用程序应避免生成此类文档,因为当 BSON 文档包含重复键时,MongoDB Server的行为是未定义的。
由于在 Ruby 哈希中不能有重复键,因此当将 Ruby 哈希序列化为 BSON 文档时,不会生成重复键。 (在 Ruby 中仍然可以手工制作具有重复键的 BSON 文档,并且其他一些 MongoDB BSON 库可能允许创建具有重复键的 BSON 文档。)
请注意,由于 BSON 文档中的键始终存储为字符串,因此指定与 Ruby 中的字符串和符号相同的键仅保留最新的规范:
BSON::Document.new(test: 1, 'test' => 2) => {"test"=>2}
加载具有重复键的 BSON 文档时,重复键的最后一个值会覆盖同一键的先前值。