乐闻世界logo
搜索文章和话题

为什么 Mongodb 查询输出的唯一标识是 _id 而不是 id ?

5 个月前提问
3 个月前修改
浏览次数96

6个答案

1
2
3
4
5
6

MongoDB 使用 _id 作为默认的字段来存储文档的唯一标识符(primary key)。这个决策背后有几个原因:

  1. 唯一性: MongoDB 设计 _id 字段是为了确保集合中的每个文档都能够被唯一地标识。MongoDB 为每个新文档自动生成一个 ObjectId 类型的 _id 值,这个值在全局范围内是唯一的,这意味着即使在不同的服务器或集群上,两个文档不会有相同的 _id

  2. 一致性: 使用 _id 作为所有文档的标准字段名,MongoDB 为开发人员提供了一致的接口来引用文档的主键。这种一致性简化了数据模型的设计,并且使得开发者可以编写更加通用的代码来处理不同的文档。

  3. 索引: 在 MongoDB 中,每个集合会自动对 _id 字段建立一个唯一索引。这样的设计保证了文档的快速查找和高效的数据完整性检查。如果没有这个默认的索引,开发人员需要手动为他们选择的主键字段建立索引,这会增加开发的复杂性。

例如,假设我们有一个用户集合(collection),其中包含了许多用户文档(document)。每个文档都会自动有一个 _id 字段,如下:

json
{ "_id": ObjectId("507f191e810c19729de860ea"), "name": "John Doe", "email": "john.doe@example.com" }

即使开发者可以选择使用自定义的字段(比如 id)作为文档的标识符,但是为了保持一致性和利用 MongoDB 内建的索引,通常建议使用默认的 _id 字段。如果有特定的需要,开发者可以在应用层将 _id 映射到 id 或其他任何他们希望的字段上。这种情况下,开发者需要负责维护额外字段的唯一性和索引。

2024年6月29日 12:07 回复

Given you're using Mongoose, you can use 'virtuals', which are essentially fake fields that Mongoose creates. They're not stored in the DB, they just get populated at run time:

shell
// Duplicate the ID field. Schema.virtual('id').get(function(){ return this._id.toHexString(); }); // Ensure virtual fields are serialised. Schema.set('toJSON', { virtuals: true });

Any time toJSON is called on the Model you create from this Schema, it will include an 'id' field that matches the _id field Mongo generates. Likewise you can set the behaviour for toObject in the same way.

See:

You can abstract this into a BaseSchema all your models then extend/invoke to keep the logic in one place. I wrote the above while creating an Ember/Node/Mongoose app, since Ember really prefers to have an 'id' field to work with.

2024年6月29日 12:07 回复

As of Mongoose v4.0 part of this functionality is supported out of the box. It's no longer required to manually add a virtual id field as explained by @Pascal Zajac.

Mongoose assigns each of your schemas an id virtual getter by default which returns the documents _id field cast to a string, or in the case of ObjectIds, its hexString. If you don't want an id getter added to your schema, you may disable it passing this option at schema construction time. Source

However, to export this field to JSON, it's still required to enable serialization of virtual fields:

shell
Schema.set('toJSON', { virtuals: true });
2024年6月29日 12:07 回复

I used this :

shell
schema.set('toJSON', { virtuals: true, versionKey:false, transform: function (doc, ret) { delete ret._id } });

I think it would be great if they automatically suppress _id when virtuals is true.

2024年6月29日 12:07 回复

I create a toClient() method on my models where I do this. It's also a good place to rename/remove other attributes you don't want to send to the client:

shell
Schema.method('toClient', function() { var obj = this.toObject(); //Rename fields obj.id = obj._id; delete obj._id; return obj; });
2024年6月29日 12:07 回复

Here is an alternative version of the answer provided by @user3087827. If you find that schema.options.toJSON is undefined then you can use:

shell
schema.set('toJSON', { transform: function (doc, ret, options) { ret.id = ret._id; delete ret._id; delete ret.__v; } });
2024年6月29日 12:07 回复

你的答案