mongoose中自建的_id是怎么生成的?
发布于 10 年前 作者 kazaff 20355 次浏览 最后一次编辑是 8 年前

从官方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后实现保存?

14 回复

关于_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的创建工作!

@kazaff 照我的理解mongoose只是一个ORM的框架,至于如何生成 _id和存储_id,是交给数据库做的。在java中ibatis跟mysql/oracle的关系一样,至于Mysql的自增长模式或oracle的seq模式,上层框架只需要提供对接,而不需要代替数据库完成这一件事情。

如果mongoose没有生成_id,那你的担心完全没有必要,你在哪里看到mongoose生成_id?大家一起帮忙想想看

@kazaff

我找了mongoose v3.8.7 的手册,你的例子也是手册里的。 首先,你所说的“不需要建立跟mongodb的连接,mongoose也会帮你创建_id”,在手册上指的是,类似Mysql“列”的概念,而不是“列中的值”。

官网地址:http://mongoosejs.com/docs/guide.html#_id 原文“Mongoose assigns each of your schemas an _id field by default if one is not passed into the Schema constructor. The type assiged is an ObjectId to coincide with MongoDBs default behavior. If you don’t want an _id added to your schema at all, you may disable it using this option. Pass this option during schema construction to prevent documents from getting an _id created by Mongoose (parent documents will still have an _id created by MongoDB when inserted). Passing the option later using Schema.set(’_id’, false) will not work. See issue #1512.”

表达意思是:Mongoose会在Collection构造的时候添加一个_id字段,如果你不在构造的时候显式传递。这个字段的类型是ObjectId,跟MongoDB的所有类型数据库中保持一致。如果你不打算添加_id字段,你必须禁用掉这个选项。 通过这个选项,可以阻止Collection在构造时从Mongoose获取默认构造的_id字段。见issue#1512中后面这样使用Schema.set(’_id’, false)将会错误。

@ccccccc2003

var schema = new Schema({ name: String }, { _id: true});
var Page = mongoose.model('Page', schema);
var p = new Page({ name: 'mongodb.org' });
console.log(p); // 不需要创建连接,这里的p中也会存在_id

@ccccccc2003 官方只是提到说_id的开关必须在构造Schema时设置,之后使用set方法是不会生效的。

但是按照我上面给的代码,确实可以关闭mongoose的_id自动创建功能,但是也意味着你构建的这个模型对象无法使用save方法~

类比一下直接在mongo shell中执行插入数据的语句:insert({name:‘kazaff’}),然后findALL,你会发现,mongodb会为文档添加_id!我就是想要这种形式,而不是交给mongoose来创建。

另外,谁知道mongoose是如何创建这个_id的呢?

@ccccccc2003 官方只是提到说_id的开关必须在构造Schema时设置,之后使用set方法是不会生效的。

但是按照我上面给的代码,确实可以关闭mongoose的_id自动创建功能,但是也意味着你构建的这个模型对象无法使用save方法~

类比一下直接在mongo shell中执行插入数据的语句:insert({name:‘kazaff’}),然后findALL,你会发现,mongodb会为文档添加_id!我就是想要这种形式,而不是交给mongoose来创建。

另外,谁知道mongoose是如何创建这个_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]
})
回到顶部