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()
相关推荐
caridle7 小时前
教程:使用 InterBase Express 访问数据库(五):TIBTransaction
java·数据库·express
理想不理想v8 小时前
vue种ref跟reactive的区别?
前端·javascript·vue.js·webpack·前端框架·node.js·ecmascript
暮毅13 小时前
10.Node.js连接MongoDb
数据库·mongodb·node.js
~甲壳虫18 小时前
说说webpack中常见的Plugin?解决了什么问题?
前端·webpack·node.js
~甲壳虫19 小时前
说说webpack中常见的Loader?解决了什么问题?
前端·webpack·node.js
~甲壳虫19 小时前
说说webpack proxy工作原理?为什么能解决跨域
前端·webpack·node.js
熊的猫20 小时前
JS 中的类型 & 类型判断 & 类型转换
前端·javascript·vue.js·chrome·react.js·前端框架·node.js
前端青山1 天前
Node.js-增强 API 安全性和性能优化
开发语言·前端·javascript·性能优化·前端框架·node.js
GDAL1 天前
npm入门教程1:npm简介
前端·npm·node.js
郑小憨2 天前
Node.js简介以及安装部署 (基础介绍 一)
java·javascript·node.js