mongoose新增数据报错Error: 11000 duplicate key error collection (id重复)的解决方法总结

写在最前,我使用的 mongoose版本:v7.5.1MongoDB版本: v7.0.0

问题描述

最近使用express + mongoose + MongoDB 做自己的博客后端时,遇到了这样一个问题 新增文档时,集合中插入第一条文档正常,插入第二条时候报错key重复:
Error: 11000 duplicate key error collection: xxx index: keyName dup key: { yyy: "zzz" }

经过对数据库和mongoose模型、插入过程的排查,终于解决了问题。经过一系列测试,crud均正常。

修完bug后我又尝试了一番,大体上把我发现的导致问题的因素分为以下两大类:

为方便理解,本文中我们将Schema中定义的mongoose.Types.ObjectId类型称为主键,或者key、id ,在MongoDB中,默认自动生成的是_id

第一类 - 自己指定了id

  • 情况一,每次添加手动设置了id的值:

由于自己在调试过程中创建了多个名字的主键,但自己因为某种原因遗忘了(比如命名调整,比如忘了删除index),后面又重新创建了与之前重名的主键,而MongoDB中存在同名未删除的主键,导致冲突。这种情况通过重置数据库对应集合中Index消除,代价是要删除本集合中之前全部数据

解决办法:进入数据库

js 复制代码
 db.collectionName.getIndexes()  // 查看存在的索引名, 多半有之前重名的
 db.collectionName.dropIndexes()  // 删除全部索引, 删除之前的文档,重新创建
  • 情况二,插入或许正常,但是使用自己id时候发现处理到了别的文档:

自己指定了id,由于在new Schema()中,给指定的id设置了默认值,比如:

js 复制代码
 const mongoose = require("../DB")
 var Schema = mongoose.Schema
 ​
 var tagSchema = new Schema({
     tagId: {
         type: mongoose.ObjectId,
         default: mongoose.Types.ObjectId(),
         unique: true  // 这个要有
     },
     publishTime: Date,
     updateTime: Date,
     userId: String,
     tagname: String,
     tagIcon: String,
     hasNums: Number
 })
 ​
 ​
 const tagModel = mongoose.model('tags', tagSchema)
 ​
 module.exports = { tagModel }

但是,重点:插入数据时候没有手动给你设置的id赋值 ,导致了,Model使用的是设置Schema时候初始化的默认值,这就导致了写入数据库时候的值是初始化时候的值,就和之前的一样了,这时如果你的命名不是_id, 那么数据库依然会生成_id,新增或许可以成功,但是一旦编辑数据,就可能改掉了别的文档的数据,因为根据你设置的id,其他文档也有相同值的id,如果只使用id约束,那么改到别的文档在所难免。

解决方法是:新增数据时候手动赋值

js 复制代码
     const { tagModel } = require('../model/tag')
     const mongoose = require('mongoose')
     const { ObjectId } = mongoose.Types
     let flag = false
     // 操作都是是异步的,要await
     await tagModel.create({
         tagId: new ObjectId(),  // 手动赋值,生成新的key
         userId, userId,
         tagname: name, 
         tagIcon: icon, 
         hasNums: 0, 
         publishTime:birth, 
         updateTime: 0 
     }).then((data) => {
         console.log('insert success')
         flag = true
     }).catch((err) => {
         if(err.message.indexOf('duplicate key error')!==-1){
             console.log('存在重复key', err.keyPattern)
         }else {
             Object.entries(err).map(([key, value]) => {
                 console.log(`error: ${key}, ${value.message}`)
             })
         }
         flag = false
     })
     return false
  • 情况三:

自己设置了id,并且报错信息显示的是自己设置过的id,其值在数据库中也存在,那就是生成了和之前id同值的新id了,需要自己去处理,数据库删除废id或者修改,或者改进生成id的算法避免出现重复值

第二类 - 自己没有指定id

比如这样:

js 复制代码
 var tagSchema = new Schema({
     /* 没有指定_id, 那么MongoDB会自动生成_id */
     publishTime: Date,
     updateTime: Date,
     userId: String,
     tagname: String,
     tagIcon: String,
     hasNums: Number
 })

如果没有手动设置类型为mongoose.Schema.Types.ObjectId或者mongoose.ObjectId(注意版本)类型的key , MongoDB会默认自己生成一个_id作为key,这个key就像很多文章里说的,他的生成受到 时间戳+主机+进程号+序列 的影响,影响他生成的内外原因如下:

  • 内部原因:同时插入两条数据,导致数据库生成了同一个id值。
  • 外部原因:每次使用同一个变量存储不同的数据,导致数据库认为每次存储的是同一条数据,最终生成同一个id值。

所以需要自己指定id,请注意,指定之前还有个步骤是清除数据库中的索引记录

js 复制代码
 db.yourCollectionName.dropIndexes()

然后再手动设置id

js 复制代码
 var tagSchema = new Schema({
     tagId: {
         type: mongoose.ObjectId,
         unique: true  // 这个要有
     },
     publishTime: Date,
     updateTime: Date,
     userId: String,
     tagname: String,
     tagIcon: String,
     hasNums: Number
 })

最后记得插入时手动

js 复制代码
 tagId: new mongoose.Types.ObjectId()
相关推荐
掰头战士15 小时前
从LLM到Agent、Agent的6大核心。这些基础知识你还记得吗
node.js·llm·agent
FungLeo17 小时前
成为全栈·Node 后端篇·注册登录全流程实现
node.js·登录流程·成为全栈·注册流程
AI大模型-小华20 小时前
Codex CLI第一次怎么用?从安装到读取本地项目完整教程
git·node.js·ai编程·开发工具·代码分析·codex·codex cli
晴天1620 小时前
打造自己的 npm 包实战指南
前端·npm·node.js
__zRainy__20 小时前
Node系列 · Express:log4js 日志记录
log4j·express
西西小飞龙21 小时前
npm vs pnpm
前端·npm·node.js
FungLeo21 小时前
成为全栈·Node 后端篇·权限模型:从认证到 RBAC
node.js·rbac·用户认证·权限模型·成为全栈
码力斜杠哥1 天前
CodeBuddy + Node.js 后端API完整开发指南
node.js
八角丶2 天前
Node 网络编程 —— TLS 模块
javascript·后端·node.js
嘿嘿-662 天前
Cherry Studio 接入ChatGpt:GPT-5.6 Sol 文本测试与 GPT-Image-2 出图教程
人工智能·gpt·ai·chatgpt·node.js·ai编程