BSON类型
BSON 是一种二进制序列化格式,用在 MongoDB 中存储文档和进行远程过程调用。BSON 规范位于 bsonspec.org。
每个 BSON 类型都同时具有整数和字符串标识符,如下表所列:
类型 | 数值 | 别名 | 注意 |
---|---|---|---|
double | 1 | "double" | |
字符串 | 2 | "string" | |
对象 | 3 | "object" | |
阵列 | 4 | "array" | |
二进制数据 | 5 | "binData" | |
未定义 | 6 | "undefined" | 已弃用。 |
ObjectId | 7 | "objectId" | |
布尔 | 8 | "bool" | |
Date | 9 | "date" | |
null | 10 | "null" | |
正则表达式 | 11 | "regex" | |
数据库指针 | 12 | "dbPointer" | 已弃用。 |
JavaScript | 13 | "javascript" | |
符号 | 14 | "symbol" | 已弃用。 |
32 位整数 | 16 | "int" | |
时间戳 | 17 | "timestamp" | |
64 位整型 | 18 | "long" | |
Decimal128 | 19 | "decimal" | |
Min key | -1 | "minKey" | |
Max key | 127 | "maxKey" |
$type
操作符支持使用这些值按 BSON 类型查询字段。$type
还支持number
别名,它匹配整数、十进制、double 和长整型 BSON 类型。$type
聚合操作符返回其参数的 BSON 类型。如果
$isNumber
聚合操作符的参数是 BSON 整数、十进制、双精度浮点数或长整型,则该操作符会返回true
。
要确定某一字段的类型,请参阅类型检查。
如果将 BSON 转换为 JSON,请参阅扩展 JSON 参考资料。
以下部分介绍了特定 BSON 类型的特别注意事项。
二进制数据
BSON 二进制binData
值是字节数组。 binData
值有一个子类型,用于指示如何解释二进制数据。 下表显示了子类型:
数值 | 说明 |
---|---|
0 | 通用二进制子类型 |
1 | 函数数据 |
2 | 二进制(旧版) |
3 | UUID(旧) |
4 | UUID |
5 | MD5 |
6 | 加密的 BSON 值 |
7 | 压缩时间序列数据 5.2 版本中的新增功能。 |
8 | 敏感数据,例如密钥或密码。 MongoDB 不会记录子类型为 8 的二进制数据的字面值。相反,MongoDB 会记录占位符值 |
9 | 向量数据是由相同类型的数字组成的密集数组。 |
128 | 自定义数据 |
ObjectId
ObjectId(对象标识符)很小,可能是唯一的,生成速度快并且是有序的。ObjectId 值的长度为 12 个字节,包含:
一个 4 字节时间戳,它表示 ObjectId 的创建时间,并以自 UNIX 纪元以来的秒数为单位进行测量。
每个进程会生成一次 5 字节随机值。这个随机值对于机器和进程是唯一的。
三字节递增计数器(初始化为随机值)。
对于时间戳和计数器值,最高有效字节在字节序列中最先出现(大端字节序)。对于其他 BSON 值,最低有效字节最先出现(小端字节序)。
如果使用整数值创建对象标识符(ObjectId),则该整数将替换时间戳。
在MongoDB中,存储在标准集合中的每个文档都需要一个唯一的_id字段作为主键。如果插入的文档省略了_id
字段,则MongoDB驾驶员会自动为 _id
字段生成 ObjectId。
这也适用于通过执行 upsert: true 的更新操作插入的文档。
MongoDB 客户端应添加一个具有唯一 ObjectId 的 _id
字段。为 _id
字段使用 ObjectId 还能带来以下好处:
您可以使用
ObjectId.getTimestamp()
方法访问mongosh
中的ObjectId
创建时间。ObjectID 大致按创建时间排序,但并非完全有序。在包含
ObjectId
值的_id
字段上对集合排序,大致相当于按创建时间排序。
使用 ObjectId()
方法可设置和检索 ObjectId 值。
从 MongoDB 5.0 开始,mongosh
将取代旧版的 mongo
shell。ObjectId()
方法在 mongosh
中的运行方式与在旧版的 mongo
shell 中的运行方式不同。有关旧版方法的更多信息,请参阅旧版的 mongo Shell。
字符串
BSON 字符串是 UTF-8 编码。通常,在序列化和反序列化 BSON 时,每种编程语言的驱动程序会从该语言的字符串格式转换为 UTF-8。这样就可以在 BSON 字符串中轻松存储大多数国际字符。[1]此外,MongoDB$regex
查询支持在正则表达式字符串中使用 UTF-8。
[1] | 对于使用 UTF-8 字符集的字符串,使用 sort() 排序通常可以得到正确的结果。但是,由于 sort() 内部使用 C++ strcmp api,因此某些字符的排序处理可能不正确。 |
时间戳
BSON 具一种特殊的时间戳类型,供 MongoDB 内部使用,与常规的 Date 类型无关。此内部时间戳类型是 64 位值,其中:
最高的 32 位有效位则是
time_t
值(自 UNIX 纪元以来的秒数)对于给定秒内的操作,最低有效的 32 位是递增的
ordinal
。
虽然 BSON 格式是小端字节序,因此首先存储最低有效位,但在所有平台上,mongod
实例始终先比较 time_t
值,然后再比较 ordinal
值,不受字节序的影响。
在复制中,oplog 有一个 ts
字段。该字段的值反映利用 BSON 时间戳值确定的操作时间。
在单个 mongod
实例中,oplog 中的时间戳值总是唯一的。
注意
这种 BSON 时间戳类型供 MongoDB 内部使用。对于多数情况,应用程序开发中需要使用 BSON 日期类型。请参阅日期,获取更多信息。
Date
“BSON Date”(BSON 日期)是一个 64 位整数,它表示自 UNIX 纪元(1970 年 1 月 1 日)以来的毫秒数。因此,过去与未来的可表示日期范围便可达到约 2.9 亿年。
官方 BSON 规范将 BSON 日期类型称为 UTC 日期时间。
BSON Date 类型为有符号值。[2] 负值代表 1970 年之前的日期。
要在 mongosh
中构造 Date
,您可以使用 new Date()
或 ISODate()
构造函数。
使用 New Date() 构造函数构造日期
要使用 new Date()
构造函数构造 Date
,运行以下命令:
var mydate1 = new Date()
mydate1
变量输出包装为ISODate 的日期和时间:
mydate1
ISODate("2020-05-11T20:14:14.796Z")
使用ISODate() 构造函数构造日期
要使用 ISODate()
构造函数构造 Date
,运行以下命令:
var mydate2 = ISODate()
mydate2
变量存储包装为ISODate 的日期和时间:
mydate2
ISODate("2020-05-11T20:14:14.796Z")
将日期转换为字符串
要以 string
格式打印 Date
,请使用 toString()
方法:
mydate1.toString()
Mon May 11 2020 13:14:14 GMT-0700 (Pacific Daylight Time)
返回日期的月份部分
您还可以返回 Date
值的月份部分。月份从零开始索引,因此一月是 0
月。
mydate1.getMonth()
4
[2] | 在 2.0 版本之前,Date 值被错误解释为无符号整数,从而会影响针对 Date 字段的排序、范围查询和索引。由于升级时不会重新创建索引,因此使用早期版本对 Date 值创建索引时请重新创建索引,且 1970 年之前的日期对您的应用程序来说均为相对日期。 |
decimal128
BSON数据类型
decimal128
是 128 位十进制表示形式,用于在四舍五入很重要的情况下存储非常大或非常精确的数字。它创建于 2009 8 月,作为 IEEE 754-2008 浮点修订版的一部分。使用BSON数据类型时如果需要高精度,则应使用 decimal128
。
decimal128
支持 34 位十进制数的精度或有效数以及 -6143 到 +6144 的指数范围。有效数在 decimal128
标准中未进行规范化,从而允许有多种可能的表示形式:10 x 10^-1 = 1 x 10^0 = .1 x 10^1 = .01 x 10^2
等。能力分别以 10^6144
和 10^-6143
的顺序存储最大值和最小值,从而允许精度很高。
将 decimal128
与 NumberDecimal()
构造函数结合使用
在MongoDB中,您可以使用 NumberDecimal()
构造函数以 decimal128
格式存储数据。如果将该十进制值作为字符串传入, MongoDB会按如下方式将该值存储在数据库中:
NumberDecimal("9823.1297")
您还可以将该十进制值作为 double
传入:
NumberDecimal(1234.99999999999)
您还应考虑您的编程语言对 decimal128
的用法和支持。以下语言本身不支持此功能,需要插件或额外的包才能获得该功能:
Python:decimal.Decimal 模块可用于浮点运算。
Java: Java BigDecimal 类支持
decimal128
数字。Node.js:有多个包提供支持,例如npm上提供的 js-big-decimal 或节点 bigdecimal。
用例
以编程方式执行数学计算时,有时会收到意外的结果。以下 Node.js示例会产生不正确的结果:
> 0.1 0.1 > 0.2 0.2 > 0.1 * 0.2 0.020000000000000004 > 0.1 + 0.1 0.010000000000000002
同样,以下Java示例会产生不正确的输出:
1 class Main { 2 public static void main(String[] args) { 3 System.out.println("0.1 * 0.2:"); 4 System.out.println(0.1 * 0.2); 5 } 6 }
1 0.1 * 0.2: 2 0.020000000000000004
在Python、 Ruby、 Rust和其他语言中进行相同的计算会产生相同的结果。发生这种情况是因为二进制浮点数不能很好地表示 10 基值。
示例,上述示例中使用的 0.1
在二进制中表示为 0.0001100110011001101
。大多数时候,这不会导致任何重大问题。但是,在金融或银行等对精度很重要的应用程序中,请使用 decimal128
作为数据类型。