Nodejs mongoose中自建的_id是怎么生成的?
Nodejs mongoose中自建的_id是怎么生成的?
从官方mongoose v3.8.7的手册中看到关于Schema的_id属性,发现这个属性在new一个模型的时候就已经生成了,这个时候根本就没有和MongoDB通信! 我好奇mongoose是如何生成这个_id的值的,它能保证唯一性么?
另外,官方提到可以关闭这个特性,但是
var schema = new Schema({ name: String }, { _id: false });
var Page = mongoose.model('Page', schema);
var p = new Page({ name: 'mongodb.org' });
console.log(p); // { name: 'mongodb.org' }
// MongoDB will create the _id when inserted
p.save(function (err) {
if (err) return handleError(err);
Page.findById(p, function (err, doc) {
if (err) return handleError(err);
console.log(doc); // { name: ‘mongodb.org’, _id: ‘50341373e894ad16347efe12’ }
})
})
实际测试发现根本就不会save成功,会提示:
[Error: document must have an _id before saving]
请问如何关闭_id后实现保存?
Node.js Mongoose 中自建的 _id
是怎么生成的?
背景介绍
在使用 Mongoose 构建 Node.js 应用时,经常会遇到 _id
的问题。Mongoose 默认会在每个文档中自动创建并管理 _id
字段,该字段通常用于唯一标识数据库中的每一条记录。但是,如果你希望自定义 _id
或者禁用默认的 _id
功能,可能会遇到一些挑战。
_id
的生成方式
在 Mongoose 中,_id
字段默认是一个 ObjectId
类型。ObjectId
是一种特殊的字符串格式,它由12个字节组成(即24个十六进制字符),包含了时间戳、机器标识符、进程ID和随机数等信息,以确保其唯一性。
如何关闭 _id
如果你想禁用 Mongoose 自动为每个文档生成 _id
,可以通过在 Schema
定义中设置 { _id: false }
来实现。然而,需要注意的是,这样做会导致某些功能失效,比如 findById
方法。
var schema = new Schema({ name: String }, { _id: false });
var Page = mongoose.model('Page', schema);
// 创建一个新的页面对象
var p = new Page({ name: 'mongodb.org' });
// 尝试保存这个对象
p.save(function (err) {
if (err) return console.error(err);
console.log("保存成功!");
});
上述代码尝试保存一个没有 _id
的文档时,会抛出错误 [Error: document must have an _id before saving]
,因为 Mongoose 需要 _id
来识别文档。
如何在禁用 _id
后实现保存
如果确实需要禁用 _id
并且仍希望保存数据,可以考虑以下两种方法:
-
手动添加
_id
: 在保存文档之前,手动为文档添加一个唯一的_id
。var id = mongoose.Types.ObjectId(); var p = new Page({ _id: id, name: 'mongodb.org' });
-
使用自定义 ID 管理器: 使用一个外部服务来生成唯一 ID,并将其赋值给文档。
const uuid = require('uuid'); var p = new Page({ _id: uuid.v4(), // 使用 UUID 作为 ID name: 'mongodb.org' });
通过这些方法,你可以在禁用 Mongoose 的 _id
生成功能后,仍然能够成功保存数据到 MongoDB。
关于_id生成,可以看mongodb权威指南2.6.5节。简要说因为mongodb是分布式的,所以就没设计成关系数据库那样自增的主键,例如mysql。 由12字节,按照“时间戳+机器+PID+计数器”,来保证同一时间,同一机器,同一进程,所产生的_id不同(1秒钟可以有1677216不同的_id)。如果是分布式情况下,这样如果让4个条件全部碰撞的概率是微乎及微。
如果关闭_id后实现保存,不清楚
可能是我表述不够清楚,我是想知道mongoose是如何生存这个_id的,我现在就是不太信任mongoose,所以想关闭它的这个机制,而交给mongoDB来创建_id~~
跟mongoose没关系,就是mongodb对每一条数据都会生成一个唯一id。
但是你不需要建立跟mongodb的连接,mongoose也会帮你创建_id,我就是想关闭mongoose的这个特性而交给mongodb来处理_id的创建工作!
Mongoose 既然该默认这么干,那么它可能使用了跟 mongodb 一样的逻辑在创建这个 _id。
_id 的作用只是为了保证不重复而已啊,这跟 UUID 一样。只要算法的逻辑是一样的,那么不管在 mongoose 层面生成还是在 mongodb 层面生成,我认为这都是无须担心的。
至于为何在 mongo shell 中可以让 mongodb 自动生成 _id,有没有可能是这样:其实 mongodb 并不是在你保存后自动帮你生成了 _id,而是 mongo shell 在 save 前帮你生成了 _id,再存入 mongodb?
mysql 的自增不好在客户端做,所以我们认为在服务端做是更合适的。但是保证一段 string 的不重复,在客户端和服务端做都是有保证的。
况且,mongoose 之所以允许你把 _id 设为不自动生成。也并不是说,不添加 _id 就能存入 mongodb 让 mongodb 自动生成 _id。它只是方便某些自定义 _id (比如 email、身份证号)的逻辑更方便而已。
恩,我认为你分析的很合理,补充一下,mongoose提供禁止_id的开关,我看github上的bug列表中有人提到是为了适应内嵌文档的。
like this:
var childSchema = new Schema({ name: 'string'}, {_id: false});
var parentSchema = new Schema({
children: [childSchema]
})
在 Mongoose 中,_id
字段默认是自动创建的,并且其值通常是 ObjectId 类型。ObjectId 是一个全局唯一的标识符,由时间戳、机器标识、进程 ID 和随机数组成,这确保了它的唯一性。
如果你想自定义 _id
的生成方式或完全禁用它,可以通过配置 Schema
来实现。但如果你禁用了 _id
,那么在保存文档时需要手动设置 _id
字段,否则会抛出错误。
示例代码
禁用 _id
并手动设置
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
// 创建一个新的 Schema
const schema = new Schema({ name: String }, { _id: false });
// 自定义 _id 的生成方式
schema.add({
_id: {
type: String,
default: () => {
return mongoose.Types.ObjectId().toString();
}
}
});
const Page = mongoose.model('Page', schema);
const page = new Page({ name: 'mongodb.org', _id: mongoose.Types.ObjectId().toString() });
page.save((err) => {
if (err) {
console.error(err);
} else {
console.log('Document saved successfully!');
}
});
解释
- 禁用
_id
: 通过{ _id: false }
关闭默认的_id
生成。 - 添加自定义
_id
: 使用schema.add
方法添加一个自定义的_id
字段,并使用默认值函数生成一个唯一的 ObjectId。 - 保存文档: 在保存文档之前,必须手动设置
_id
字段,以避免出现[Error: document must have an _id before saving]
错误。
通过这种方式,你可以灵活地控制 _id
的生成方式,并且确保文档可以正常保存到数据库中。