Nodejs Mongoose E11000 duplicate key error index 问题,google了很久也没解决,求助。

Nodejs Mongoose E11000 duplicate key error index 问题,google了很久也没解决,求助。

mongoose中save函数我的理解是如果存在则更新,否则插入。但是现在显示_id冲突,无法解决。

Schema如下:

var UserSchema = new Schema({
	username: { type: String },
	nickname: { type: String },
    	password: { type: String },
    	email: { type: String },
    	access: { type: Number, default: 0},
}, { collection: 'User' });
mongoose.model('User', UserSchema);

然后在一个更新操作中每次find, modify, save,save过程会报错

{ [MongoError: E11000 duplicate key error index: test.User.$_id_  dup key: { : ObjectId('5362235c895105241d43c46e') }]
  name: 'MongoError',
  err: 'E11000 duplicate key error index: test.User.$_id_  dup key: { : ObjectId(\'5362235c895105241d43c46e\') }',
  code: 11000,
  n: 0,
  connectionId: 1,
  ok: 1 }

更新的代码是:

	User.getByName({ username: _currentUser.username }, proxy.done(function(user) {
		if (req.body['email']) user.email = req.body['email'];
		user.save(function(err) {
			if (err) {
				console.log(err);  //这里打印的就是上边的错误
			}
		});
		proxy.emit('save');
	}));

请问是什么问题造成的,我把collection remove掉还是不行。


9 回复

针对你遇到的 E11000 duplicate key error index 错误,这通常是因为 MongoDB 在尝试插入或更新文档时遇到了重复的键值。在这种情况下,_id 字段被重复,导致错误。

问题分析

  1. _id 的唯一性:MongoDB 中的 _id 字段默认是一个唯一的字段。如果你尝试插入一个已经存在的 _id,就会触发这个错误。
  2. 手动设置 _id:在某些情况下,你可能手动设置了 _id 字段,导致了重复。

解决方案

1. 确保 _id 自动生成

确保你在保存文档时没有手动设置 _id。Mongoose 会自动为每个文档生成一个唯一的 _id,除非你明确指定了 _id

示例代码

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

// 定义用户模式
var UserSchema = new Schema({
    username: { type: String },
    nickname: { type: String },
    password: { type: String },
    email: { type: String },
    access: { type: Number, default: 0 },
}, { collection: 'User' });

// 创建模型
const User = mongoose.model('User', UserSchema);

// 更新操作
User.getByName({ username: _currentUser.username }, (user) => {
    if (req.body['email']) user.email = req.body['email'];
    user.save((err) => {
        if (err) {
            console.log(err);
        } else {
            proxy.emit('save');
        }
    });
});

2. 检查数据完整性

确保你的数据库中没有重复的 _id。你可以使用 MongoDB 的命令行工具或者 MongoDB Compass 来检查和清理重复的数据。

3. 清理重复数据

如果发现重复的 _id,可以手动删除或使用脚本清理。

User.findOne({ _id: '5362235c895105241d43c46e' })
    .then((user) => {
        if (user) {
            return user.remove();
        }
    })
    .catch((err) => {
        console.error(err);
    });

总结

  • 确保不要手动设置 _id,让它由 Mongoose 自动生成。
  • 检查并清理数据库中的重复 _id
  • 使用上述代码片段进行更新操作,并处理可能的错误。

通过这些步骤,你应该能够解决 E11000 duplicate key error index 错误。


实在是太诡异了,只有_id一个索引,默认建立的,save又不是insert为什么会duplicate呢!

求高人指点迷津啊,折腾一晚上了还是不知道哪里出了问题。

应该是你的proxy里有什么处理将此次save当成insert了 你对save的理解不太对,可以看一下mongoose的代码。

同上,先把proxy拆掉看还报错不

update吧

我也是这样的,save之前必须删掉_id很烦躁。

你用的mongoose是什么版本的? 我这边mongoose@3.8.8 测试没有问题, 对应的Model.prototype.save方法中以及根据对象是否isNew来保存或更新model.

// mongoose/lib/model.js Model.prototype.save = function save (fn) { var promise = new Promise(fn) , complete = handleSave(promise, this) , options = {}

if (this.schema.options.safe) { options.safe = this.schema.options.safe; }

if (this.isNew) { // send entire doc 新对象 var obj = this.toObject({ depopulate: 1 }); // 关联对象depopulate掉, 即 对象 --> ObjectID

if (!utils.object.hasOwnProperty(obj || {}, '_id')) {
  // documents must have an _id else mongoose won't know
  // what to update later if more changes are made. the user
  // wouldn't know what _id was generated by mongodb either
  // nor would the ObjectId generated my mongodb necessarily
  // match the schema definition.
  return complete(new Error('document must have an _id before saving'));
}

this.$version(true, obj); this.collection.insert(obj, options, complete); // 插入 this.$reset(); this.isNew = false; // 标记 this.emit(‘isNew’, false); // Make it possible to retry the insert this.$.inserting = true;

} else {
// Make sure we don’t treat it as a new object on error, // since it already exists this.$
.inserting = false;

var delta = this.$__delta();

if (delta) {
  if (delta instanceof Error) return complete(delta);
  var where = this.$__where(delta[0]);
  this.$__reset();
  this.collection.update(where, delta[1], options, complete);       // 增量更新
} else {                           // 没有脏数据
  this.$__reset();
  complete(null);
}

this.emit('isNew', false);

}

};

从你描述的情况来看,错误信息表明你在尝试保存用户时遇到了一个 _id 的重复键错误。这个错误通常发生在尝试插入一个已经存在的 _id 值时。

可能的原因:

  1. 手动设置 _id:你可能在某些地方手动设置了 _id 的值,这导致了重复键错误。
  2. 未正确删除数据:尽管你已经删除了集合中的所有数据,但可能存在残留的数据或者索引问题。
  3. 并发问题:在高并发情况下,多个请求可能会同时尝试插入相同的 _id

解决方案:

示例代码:确保 _id 自动生成

var mongoose = require('mongoose');

var UserSchema = new mongoose.Schema({
    username: { type: String, unique: true },
    nickname: { type: String },
    password: { type: String },
    email: { type: String },
    access: { type: Number, default: 0 },
});

mongoose.model('User', UserSchema);

// 更新操作
User.findOneAndUpdate(
    { username: _currentUser.username }, // 查找条件
    { $set: { email: req.body['email'] } }, // 更新字段
    { new: true, upsert: true }, // 选项,upsert: 如果没有找到匹配的文档,则插入新的文档
    function(err, doc) {
        if (err) {
            console.log(err);
        } else {
            console.log(doc);
        }
    }
);

关键点解释:

  • unique: true:确保 username 字段唯一。
  • findOneAndUpdate:使用 findOneAndUpdate 方法代替 findsave,这样可以避免手动处理 _id 的问题。
  • upsert: true:如果找不到匹配的文档,则插入一个新的文档,这可以避免 _id 冲突的问题。

通过这些调整,你应该能够避免 _id 冲突的问题,并且使你的更新操作更加健壮。

回到顶部