MongoDB重复键错误解决方案:Pinterest克隆传帖遇E11000问题
问题描述
开发Pinterest克隆项目时,同一账号上传第二篇及后续帖子时触发MongoDB E11000重复键错误,错误提示posts集合的username_1索引存在重复的null键值。
相关代码
User Schema
const mongoose = require('mongoose'); const plm = require('passport-local-mongoose'); mongoose.connect('mongodb://127.0.0.1:27017/DataVault'); const userSchema = new mongoose.Schema({ username: { type: String, unique: true }, email: { type: String, }, password: String, post: [ { type: mongoose.Schema.Types.ObjectId, ref: "Post", } ], ProfileImage: String, }); userSchema.plugin(plm); module.exports = mongoose.model('User', userSchema);
Post Schema
const mongoose = require('mongoose'); const plm = require('passport-local-mongoose'); mongoose.connect('mongodb://127.0.0.1:27017/DataVault'); const postSchema = new mongoose.Schema({ PostText: { type: String, }, image: { type: String }, user: { type: mongoose.Schema.Types.ObjectId, ref: "User", } }); postSchema.plugin(plm); module.exports = mongoose.model('Post', postSchema);
帖子创建路由
// post creaetion and uploading router.post("/postUpload", isLoggedIn, uploads.single("postImage"), async function (req, res, next) { if (!req.file) { return res.status(404).send("Something Went Wrong!!!😡"); } const user = await userModel.findOne({ username: req.session.passport.user }); const post = await postModel.create({ PostText: req.body.fileCaption, image: req.file.filename, user: user._id }); user.post.push(post._id); await user.save(); console.log("Post Successfully Saved ✌"); res.redirect("/profile"); });
报错信息
throw new error_1.MongoServerError(res.writeErrors[0]);
^
MongoServerError: E11000 duplicate key error collection: DataVault.posts index: username_1 dup key: { username: null }
at InsertOneOperation.execute (D:\backend\Codes\pinterest\node_modules\mongodb\lib\operations\insert.js:48:19)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async executeOperationAsync (D:\backend\Codes\pinterest\node_modules\mongodb\lib\operations\execute_operation.js:106:16) {
index: 0,
code: 11000,
keyPattern: { username: 1 },
keyValue: { username: null },
[Symbol(errorLabels)]: Set(0) {}
}
解决方案
核心问题
Post Schema错误地使用了passport-local-mongoose插件,该插件会自动为Schema添加username字段并创建唯一索引。但帖子模型不需要这个字段,导致每次创建帖子时username字段为null,而MongoDB中唯一索引不允许多个文档的字段值为null,因此第二次上传时触发重复键错误。
修复步骤
移除Post Schema中的passport-local-mongoose插件
编辑Post Schema文件,删除不必要的插件引入和调用,修改后的代码如下:const mongoose = require('mongoose'); // 移除不必要的plm引入 // const plm = require('passport-local-mongoose'); // 建议统一在项目入口文件中连接数据库,此处可移除 // mongoose.connect('mongodb://127.0.0.1:27017/DataVault'); const postSchema = new mongoose.Schema({ PostText: { type: String, }, image: { type: String }, user: { type: mongoose.Schema.Types.ObjectId, ref: "User", } }); // 移除插件调用 // postSchema.plugin(plm); module.exports = mongoose.model('Post', postSchema);清理MongoDB中的重复索引
进入MongoDB Shell执行以下命令:# 进入MongoDB Shell(根据版本使用mongosh或mongo) mongosh # 切换到目标数据库 use DataVault # 删除posts集合的username_1索引 db.posts.dropIndex("username_1")优化数据库连接
不要在每个Schema文件中重复调用mongoose.connect,建议在项目入口文件(如app.js或server.js)中统一配置数据库连接,避免重复连接造成资源浪费。优化User Schema(可选)
User Schema中手动定义的password字段可以移除,因为passport-local-mongoose会自动创建hash和salt字段来存储加密后的密码,手动定义可能导致冲突。
验证修复
重启后端服务,使用同一账号多次上传帖子,确认不再触发重复键错误。
内容的提问来源于stack exchange,提问作者Moiz Ali

