Mongoose删除文档后插入同唯一字段值报错问题求助
解决删除文档后插入重复唯一字段的MongoError问题
首先得指出你测试代码里的一个关键错误,这很可能是触发重复键错误的直接原因:Mongoose的Model.create()批量创建文档时,必须传入对象数组,而非多个单独的对象参数。
你当前的写法:
await Category.create( { name: 'Category 1', desc: 'This is category 1', }, { name: 'Category 2', desc: 'This is category 2', }, );
实际上只会创建第一个分类文档,第二个对象会被当作配置选项(比如会话、验证规则)而非待创建的文档。如果之前的测试残留了Category 1的记录(哪怕你调用了deleteMany,如果异步清理没完全执行),第二次执行这段代码就会触发重复键错误。
第一步:修复批量创建的写法
改成传入对象数组的正确形式:
await Category.create([ { name: 'Category 1', desc: 'This is category 1' }, { name: 'Category 2', desc: 'This is category 2' }, ]);
第二步:处理测试环境的索引异步问题
使用MongoMemoryServer时,Mongoose的索引创建是异步的,如果在索引完全生效前就插入数据,或者清理数据后索引更新未同步,也会触发这类错误。可以通过以下方式解决:
1. 等待模型索引初始化完成
在mongoose连接成功后,调用await Category.init()确保该模型的所有索引(包括唯一索引)已创建完成:
before(async () => { const mongod = new MongoMemoryServer(); mongoose.Promise = Promise; const uri = await mongod.getConnectionString(); const mongooseOpts = { useNewUrlParser: true, useUnifiedTopology: true, useCreateIndex: true, }; await mongoose.connect(uri, mongooseOpts); // 等待Category模型的索引创建完成 await Category.init(); // 清理数据 await Promise.all([ Announcement.deleteMany({}), Category.deleteMany({}), ]); });
2. 彻底清空集合而非仅删除文档
deleteMany只删除文档,索引仍保留。如果测试环境中索引状态异常,直接drop集合会让Mongoose重新创建索引,确保绝对干净的测试环境:
// 替换deleteMany为drop,catch错误防止集合不存在时抛出异常 await Promise.all([ Announcement.collection.drop().catch(() => {}), Category.collection.drop().catch(() => {}), ]);
最后确认插件使用正确性
你的mongoose-unique-validator插件注册时机是对的(schema定义后、model创建前),插件会在Mongoose层面做预验证,但底层仍依赖MongoDB的唯一索引,所以确保索引正确创建是核心前提。
内容的提问来源于stack exchange,提问作者Cristopher Namchee
相关产品推荐
相关产品推荐

