MongoDB 数据类型

目录

[BSON 类型](#BSON 类型)

[二进制数据(Binary Data)](#二进制数据(Binary Data))

ObjectId

ObjectId定义

文档中的ObjectId

ObjectId的单调性

字符串(String)

时间戳(Timestamps)

日期(Date)

BSON类型的排序

数值类型的比较

字符串比较

数组的比较

对象的比较

日期和时间戳

不存在字段

二进制数据


Mongodb是文档型数据库,文档表示一条数据记录。文档由键值对构成。

//一个文档示例
{
  "key_1": value_1,
  "key_2": value_2
  ...
}

其中,键值对中的键,或叫做字段名称,是由字符类型来定义的。而值的类型,是BSON类型中的某一个类型。

BSON 类型

BSON是一个二进制序列格式,用来保存文档,实现在mongodb中的远程调用。Mongodb支持不同的BSON类型,现在可用的BSON类型,其数字代码,字符识别码列在下表中。

|--------------------|------|--------------|--------------|
| 类型 | 数字代码 | 别名 | 中文名 |
| Double | 1 | "double" | 浮点型 |
| String | 2 | "string" | 字符串 |
| Object | 3 | "object" | 对象 |
| Array | 4 | "array" | 数组 |
| Binary data | 5 | "binData" | 二进制数据 |
| ObjectId | 7 | "objectId" | 对象id |
| Boolean | 8 | "bool" | 布尔 |
| Date | 9 | "date" | 日期 |
| Null | 10 | "null" | 空值 |
| Regular Expression | 11 | "regex" | 正则表达式 |
| JavaScript | 13 | "javascript" | javascript脚本 |
| 32-bit Integer | 16 | "int" | 整型 |
| Timestamp | 17 | "timestamp" | 时间戳 |
| 64-bit Integer | 18 | "long" | 长整型 |
| Decimal128 | 19 | "decimal" | 小数 |
| Min Key | -1 | "minKey" | 最小值 |
| Max Key | 127 | "maxKey" | 最大值 |

在Mongodb中,可以使用数字代码和别名作为过滤条件查询文档。在聚合操作中,可以获取字段类型的别名。或者根据返回的字段类型实现不同的逻辑。

如查询字段类型是数组类型的数据

//使用字符串"array"和数字类型4查询字段类型是数组的数据
db.collection.find({<field_name>: {$type: "array"}})
db.collection.find({<field_name>: {$type: 4}})

二进制数据(Binary Data)

BSON的二进制数据由字节数组组成。每一个二进制数据也有一个标志解析二进制数据的子类型。下面表描述了子类型。

|-----|---------------|
| 数字 | 子类型 |
| 0 | 通用二进制数据 |
| 1 | Function data |
| 2 | Binary(old) |
| 3 | UUID(old) |
| 4 | UUID |
| 5 | MD5加密 |
| 6 | 加密BSON数值 |
| 7 | 压缩时序数据 |
| 128 | 用户自定义 |

ObjectId

使用mongodb时 , 当集合中的文档没有_id字段时,会为文档自动生成一个ObjectId类型的_id。日常查询过程中,也能看到_id是ObjectId里带有一段字符串, 这个字符串代表什么意义,和其他类型的ID有什么相同或不同。本文研究mongodb官方文档,并通过实践来解释ObjectID这个数据类型。

ObjectId定义

ObjectId是一种小型的,几乎是唯一的,易于产生和排序的数据结构。长度是12个字节,由3部分组成

  • 4个字节的时间戳,表明objectId的创建时间,以秒为单位的unix时间
  • 5个字节的随机字符串,在运行机器上的进程中是唯一的
  • 3个字节的增长计数器,初始值是一个随机数

其中时间戳和增长计数器与其他BSON类型不同,是按照高位优先排序。

如果使用数字类型来创建ObjectID, ObjectID中的时间戳,会被数字值来替代。

//使用数字类型定义3个ObjectID
db.inventory.insertMany([{
    part: 'AB307',
    _id: ObjectId(1)
},{
    part: 'AB307',
    _id: ObjectId(2)
},{
    part: 'AB307',
    _id: ObjectId(3)
}])

// 产生的id中,数字类型替代了4个字节的时间戳
{
	"acknowledged" : true,
	"insertedId" : ObjectId("000000013e53cb5bc48f4e54")
}
{
	"acknowledged" : true,
	"insertedId" : ObjectId("000000023e53cb5bc48f4e55")
}
{
	"acknowledged" : true,
	"insertedId" : ObjectId("000000033e53cb5bc48f4e56")
}

上面代码的运行结果中可以看到,4个字节长度的00000001,00000002,00000003,替代了时间错。而后面3个字节长度的增长计数器,8f4e54,8f4e55,8f4e56在每一条新纪录插入时,都会增长。而中间3e53cb5bc4这个长度为5个字节的字符串,是由运行机器进程中随机产生的字符串。

时间戳+随机字符串+自增长字符串,保证了objectId的唯一性。

文档中的ObjectId

Mongodb的每一个文档都需要有唯一的_id字段作为主键。如果没有_id字段,mongodb自动添加一个ObjectId作为_id字段。这对于设置{upsert:true}的更新语句插入数据同样适用。

使用ObjectId作为_id字段的好处

  • 在mongosh中,通过ObjectId.getTimestamp()方法,获取文档创建时间

    //运行代码
    ObjectId("655ef22e69185fac9ce3ce92").getTimestamp()
    //获取创建时间
    ISODate("2023-11-23T14:33:18.000+08:00")

  • 按照_id排序,等价于按照文档创建时间排序

ObjectId的单调性

ObjectId虽然按照时间来产生,并带有随机数保证其唯一性。但objectId并不是单调的,受到两个因素影响

  • 产生objectId仅仅以秒为单位,记录下来unix时间。当同一秒钟产生多个objectId时,并不能保证每一个objectid的排列顺序。
  • 通过client产生的ObjectId, 其时间错可能与服务器时间不一致

字符串(String)

BSON类型的字符串是UTF-8格式的。各种语言连接mongodb时,通常都会使用UTF-8格式的字符串。这种格式保证了大部分语言的通用性。

时间戳(Timestamps)

BSON 的timestamps类型是一种内部使用的特殊格式。这种类型与常见的日期(Date)类型不同。timestamps类型是一个64位的数值

    • 前面32位的unix时间,精确到秒
    • 后32位的自增型操作数。表示在一秒内的操作数。

虽然BSON格式是小端序的,因此首先存储最低有效位,但在所有平台上,mongod实例总是在序数值之前比较time_t值,而不考虑端序。

在同一个mongodb运行实例中,timestamp值是唯一的。

在复制集中,oplog有一个时间类型的ts字段。这个字段使用timestamp类型表示操作时间。

当在文档中插入一个空的timestamp数值时,mongodb会将空的timestamp数字换成当前时间。但如果_id字段包含一个空的timestamp值时,mongodb不会替换空的timestamp.

db.test.insert({ts: new Timestamp(), _id: {ts: new Timestamp()}})
{
	"acknowledged" : true,
	"insertedId" : {
		"ts" : Timestamp(0, 0)
	}
}

db.test.find()
{
	"_id" : {
		"ts" : Timestamp(0, 0) //_id字段中的时间戳未被替换
	},
	"ts" : Timestamp(1703731318, 1) //顶层字段的时间戳被替换成当前的操作时间
}

日期(Date)

日期类型是一个64位字符,表示了字1970年1月1日以来经过的毫秒数。日期时间是有符号的,负数表示1970年以前的时间。64位的字符,可以表示过去和现在共2.9亿年的时间。

var mydate1 = new Date()
var mydate2 = ISODate()
console.log(mydate1.toString())
console.log(mydate2.toString())
console.log(mydate1.getMonth())

Thu Dec 28 2023 10:48:15 GMT+0800 (China Standard Time)
Thu Dec 28 2023 10:48:15 GMT+0800 (China Standard Time)
11

BSON类型的排序

在mongodb对BSON类型进行排序时,按照下面的类型进行排序。从小到大的顺序依次为

|----|--------------------------------------|
| 序号 | 类型 |
| 1 | MinKey |
| 2 | Null |
| 3 | Numbers(ints,longs,doubles,decimals) |
| 4 | String |
| 5 | Object |
| 6 | Array |
| 7 | BinData |
| 8 | ObjectId |
| 9 | Boolean |
| 10 | Date |
| 11 | Timestamp |
| 12 | RegularExpression |
| 13 | MaxKey |

这个排序怎样理解,不妨使用下面的测试用例来解释。插入13条各种类型的数据。当使用正序排列,即从小到大排列时,顺序从MinKey到MaxKey. 当使用倒序排列时,顺序相反。

db.test.insertMany([
    {value: MinKey()},
    {value: null},
    {value: 1},
    {value: "1"},
    {value: {a: 1}},
    {value: [1]},
    {value: new BinData(5,"1")},
    {value: ObjectId(1)},
    {value: true},
    {value: new Date()},
    {value: new Timestamp()},
    {value: /a/},
    {value: MaxKey()},
    ])
    
//正序排列
db.test.find().sort('value')
/* 1 createdAt:12/28/2023, 11:04:43 AM*/
{
	"_id" : ObjectId("658ce5cb9ef5a82e298557ef"),
	"value" : MinKey()
},

/* 2 createdAt:12/28/2023, 11:04:43 AM*/
{
	"_id" : ObjectId("658ce5cb9ef5a82e298557f0"),
	"value" : null
},

/* 3 createdAt:12/28/2023, 11:04:43 AM*/
{
	"_id" : ObjectId("658ce5cb9ef5a82e298557f1"),
	"value" : 1
},

/* 4 createdAt:12/28/2023, 11:04:43 AM*/
{
	"_id" : ObjectId("658ce5cb9ef5a82e298557f4"),
	"value" : [ 1 ]
},

/* 5 createdAt:12/28/2023, 11:04:43 AM*/
{
	"_id" : ObjectId("658ce5cb9ef5a82e298557f2"),
	"value" : "1"
},

/* 6 createdAt:12/28/2023, 11:04:43 AM*/
{
	"_id" : ObjectId("658ce5cb9ef5a82e298557f3"),
	"value" : {
		"a" : 1
	}
},

/* 7 createdAt:12/28/2023, 11:04:43 AM*/
{
	"_id" : ObjectId("658ce5cb9ef5a82e298557f5"),
	"value" : MD5("")
},

/* 8 createdAt:12/28/2023, 11:04:43 AM*/
{
	"_id" : ObjectId("658ce5cb9ef5a82e298557f6"),
	"value" : ObjectId("000000019ef5a82e298557ee")
},

/* 9 createdAt:12/28/2023, 11:04:43 AM*/
{
	"_id" : ObjectId("658ce5cb9ef5a82e298557f7"),
	"value" : true
},

/* 10 createdAt:12/28/2023, 11:04:43 AM*/
{
	"_id" : ObjectId("658ce5cb9ef5a82e298557f8"),
	"value" : ISODate("2023-12-28T11:04:43.958+08:00")
},

/* 11 createdAt:12/28/2023, 11:04:43 AM*/
{
	"_id" : ObjectId("658ce5cb9ef5a82e298557f9"),
	"value" : Timestamp(1703732683, 1)
},

/* 12 createdAt:12/28/2023, 11:04:43 AM*/
{
	"_id" : ObjectId("658ce5cb9ef5a82e298557fa"),
	"value" : /a/
},

/* 13 createdAt:12/28/2023, 11:04:43 AM*/
{
	"_id" : ObjectId("658ce5cb9ef5a82e298557fb"),
	"value" : MaxKey()
}
//倒序排列
/* 1 createdAt:12/28/2023, 11:04:43 AM*/
{
	"_id" : ObjectId("658ce5cb9ef5a82e298557fb"),
	"value" : MaxKey()
},

/* 2 createdAt:12/28/2023, 11:04:43 AM*/
{
	"_id" : ObjectId("658ce5cb9ef5a82e298557fa"),
	"value" : /a/
},

/* 3 createdAt:12/28/2023, 11:04:43 AM*/
{
	"_id" : ObjectId("658ce5cb9ef5a82e298557f9"),
	"value" : Timestamp(1703732683, 1)
},

/* 4 createdAt:12/28/2023, 11:04:43 AM*/
{
	"_id" : ObjectId("658ce5cb9ef5a82e298557f8"),
	"value" : ISODate("2023-12-28T11:04:43.958+08:00")
},

/* 5 createdAt:12/28/2023, 11:04:43 AM*/
{
	"_id" : ObjectId("658ce5cb9ef5a82e298557f7"),
	"value" : true
},

/* 6 createdAt:12/28/2023, 11:04:43 AM*/
{
	"_id" : ObjectId("658ce5cb9ef5a82e298557f6"),
	"value" : ObjectId("000000019ef5a82e298557ee")
},

/* 7 createdAt:12/28/2023, 11:04:43 AM*/
{
	"_id" : ObjectId("658ce5cb9ef5a82e298557f5"),
	"value" : MD5("")
},

/* 8 createdAt:12/28/2023, 11:04:43 AM*/
{
	"_id" : ObjectId("658ce5cb9ef5a82e298557f3"),
	"value" : {
		"a" : 1
	}
},

/* 9 createdAt:12/28/2023, 11:04:43 AM*/
{
	"_id" : ObjectId("658ce5cb9ef5a82e298557f2"),
	"value" : "1"
},

/* 10 createdAt:12/28/2023, 11:04:43 AM*/
{
	"_id" : ObjectId("658ce5cb9ef5a82e298557f1"),
	"value" : 1
},

/* 11 createdAt:12/28/2023, 11:04:43 AM*/
{
	"_id" : ObjectId("658ce5cb9ef5a82e298557f4"),
	"value" : [ 1 ]
},

/* 12 createdAt:12/28/2023, 11:04:43 AM*/
{
	"_id" : ObjectId("658ce5cb9ef5a82e298557f0"),
	"value" : null
},

/* 13 createdAt:12/28/2023, 11:04:43 AM*/
{
	"_id" : ObjectId("658ce5cb9ef5a82e298557ef"),
	"value" : MinKey()
}

数值类型的比较

mongodb比较数字类型时,int,long,double,decimal按照同一个类型进行比较。按照值大小进行排序。

字符串比较

默认字符串按照简单字节方法比较。即从第一个字符开始进行比较。当用户指定字符序时,mongodb比较字符串时,采用用户指定的字符序。

数组的比较

  • 升序排列时,获取数组中最小的元素进行比较
  • 降序排列时,比较数组中最大的元素
  • 比较单元素数组与非数组数据时,mongodb会直接比较该数组中的元素和非数组元素值的大小。
  • 空数组排列优先级别小于null或没有该字段的文档数据。

对象的比较

  • 递归比较对象中的每一个键值对
  • 字段类型相同时,比较字段名
  • 字段名相同时,比较字段值
  • 当前字段名,字段值都相同时,按照同样的方式比较下一个字段。字段较少的对象排列优先级小于字段多的对象。

日期和时间戳

日期排在时间错前

不存在字段

mongodb将不存在的字段看做空对象。即文档{ }与{a:null}的排列顺序相同

二进制数据

  • 首先比较数据长度
  • 数据长度相同时,比较子类型
  • 数据长度和子类型相同时,按字节比较。
相关推荐
cyt涛1 小时前
MyBatis 学习总结
数据库·sql·学习·mysql·mybatis·jdbc·lombok
Rookie也要加油2 小时前
01_SQLite
数据库·sqlite
liuxin334455662 小时前
教育技术革新:SpringBoot在线教育系统开发
数据库·spring boot·后端
看山还是山,看水还是。3 小时前
MySQL 管理
数据库·笔记·mysql·adb
fishmemory7sec3 小时前
Koa2项目实战2(路由管理、项目结构优化)
数据库·mongodb·koa
momo小菜pa3 小时前
【MySQL 09】表的内外连接
数据库·mysql
Jasonakeke3 小时前
【重学 MySQL】四十九、阿里 MySQL 命名规范及 MySQL8 DDL 的原子化
数据库·mysql
程序猿小D3 小时前
第二百六十九节 JPA教程 - JPA查询OrderBy两个属性示例
java·开发语言·数据库·windows·jpa
小宇成长录3 小时前
Mysql:数据库和表增删查改基本语句
数据库·mysql·数据库备份
团儿.4 小时前
解锁MySQL高可用新境界:深入探索MHA架构的无限魅力与实战部署
数据库·mysql·架构·mysql之mha架构