在 NodeJS 中使用 Mongoose + Mockgoose 更新时忽略唯一索引

Posted

技术标签:

【中文标题】在 NodeJS 中使用 Mongoose + Mockgoose 更新时忽略唯一索引【英文标题】:Unique index ignored when updating with Mongoose + Mockgoose in NodeJS 【发布时间】:2016-02-09 05:39:13 【问题描述】:

我目前正在使用 Mongoose ODM 在 NodeJS 应用程序中管理与 MongoDB 的数据库连接,并在 Mocha 测试中使用 Mockgoose 拦截连接。我遇到了一个问题,即在对文档执行更新时忽略了我的唯一索引。我只是用另一个名为 Mongoose-bird 的包来包装 Mongoose,它只是允许使用 Promise。

一个特别的模式如下:

// Gallery.js
'use strict';

var mongoose = require('mongoose-bird')(require("mongoose"));
var Schema = mongoose.Schema;
var ObjectId = Schema.Types.ObjectId;

var deepPopulate = require('mongoose-deep-populate')(mongoose);

var GallerySchema = new Schema(
    _id: ObjectId,
    type: String,
    title: String,
    slug: String,
    _albums: [
        type: ObjectId,
        ref: 'Albums'
    ]
);

GallerySchema.index( slug: 1 ,  unique: true );
GallerySchema.plugin(deepPopulate, );
mongoose.model('Galleries', GallerySchema);

在测试中从我的控制器调用Gallery.update(conditions, data, opts) 时,故意将slug 设置为另一个的副本,它会更新文档,然后我最终得到两个具有相同slug 路径的文档。

仅供参考,我已经通过使用 save() 函数找到了解决此问题的方法,它似乎毫无问题地遵守唯一索引。

但是,由于我更喜欢​​使用 update() 而不是 save()(即每次更新部分文档而不是整个文档),我很想知道是否有其他人遇到过同样的问题并且你是怎么克服的?

该应用程序遵循基于 MEAN.js 的标准 MVC 模式,因此它不仅仅是一个模型,但如果我遗漏了任何可能有用的内容,请告诉我。

更新 在查看了 Mockgoose NPM 模块的源代码后,我可以确认在运行 update() 时从未执行过针对模式的验证。这里记录了一个问题:http://www.github.com/mccormicka/Mockgoose/issues/58

【问题讨论】:

你能用db.galleries.getIndexes()的shell输出更新你的问题吗? 当我回到我的工作站时,我会更新它。但是,我使用的是拦截 Mongoose 连接的 Mockgoose,因此它不与实际数据库通信,而只是它创建的内存中的数据库。 好吧,那没关系;我没有发现这是一个 Mockgoose 问题。 一切顺利!我担心这可能是数据库本身的问题,但通过 Mongo CLI 更新确实会引发约束错误。我确定数据库工作正常,我认为这可能是一个 Mockgoose 错误,但我尚未确定是否是或者我做错了什么。 【参考方案1】:

您可能在测试挂钩中使用了mockgoose.reset(例如afterEach)。它会删除数据库,并且在执行期间不会再次创建索引。

解决方案是单独删除模型。

【讨论】:

请在您投反对票时写下评论并说明理由,以便我改进答案。 嗨,这对我来说也是正确的解决方案!谢谢@DiegoHaz。使用 Jest 和 Mongoose(不使用 Mockgoose) - 仅使用本地 Mongo 实例(通过 Docker)进行 e2e 测试,唯一索引被忽略,但仅在测试中。代码本身运行良好。我在beforeEach 中所做的清理是删除所有集合,这是另一个测试需要的,但它也擦除了唯一索引。解决方案是创建一个不使用有问题的清理代码的新测试文件。【参考方案2】:

添加到Diego's 帖子。在测试挂钩中调用mockgoose.helper.reset() 可能会清除临时存储中的集合也会删除索引。 您应该在使用下面的 sn-p 调用 reset 后重置索引。

await mockgoose.helper.reset()
const db = mongoose.connection
db.modelNames().map(async (model) => 
  await db.models[model].createIndexes()
)

这解决了我的问题。希望这会有所帮助。

【讨论】:

它帮助了我,伙计,谢谢一百万!我只是希望我能提前 3 小时找到你的帖子!【参考方案3】:

来自猫鼬docs

当您的应用程序启动时,Mongoose 会自动调用 确保架构中每个已定义索引的索引。猫鼬会打电话 为每个索引顺序确保索引,并在上发出一个“索引”事件 所有 ensureIndex 调用成功或存在时的模型 一个错误。

虽然很适合开发,但建议使用此行为 在生产中被禁用,因为索引创建可能会导致显着 性能影响。通过设置 autoIndex 禁用该行为 您的架构选项为 false,或在连接上全局设置为 将选项 config.autoIndex 设置为 false。

由于 mongoose 在启动时设置了索引,你必须调试具体的错误,为什么 mongoDb 不允许索引,使用以下代码

//keeping autoIndex to true to ensure mongoose creates indexes on app start
GallerySchema.set('autoIndex', true);
//enable index debugging
GallerySchema.set('emitIndexErrors', false);
GallerySchema.on('error', function(error) 
  // gets an error whenever index build fails
);
GallerySchema.index( slug: 1 ,  unique: true );

另外,请确保 autoIndex 没有像猫鼬文档中提到的那样设置为 false,或者最好像上面那样明确地将其设置为 true。

另外,

mongoose.set('debug', true);

调试日志将向您显示 ensureIndex 为您创建索引所做的调用。

【讨论】:

当我回到我的工作站时我会试试这个,但是我相信 Mongoose 和 MongoDB 工作正常 - 当我尝试直接在 Mongo CLI 中更新时,它会引发一个约束错误。只是在使用 Mockgoose 进行测试时,它被忽略了。我会试一试,让你知道我的进展。 我所说的解决方案将通知您有关 Mongoose 中的约束错误。您必须按照 MongoDB 的要求修复约束错误 不幸的是,我没有从上面得到任何额外的输出,我觉得 Mongoose 正在做它应该做的事情,但是 Mockgoose 的行为不正确。 你的意思是猫鼬没有报告索引错误?你在控制台中看到 ensureIndex 被调用了吗? 运行我的测试时没有报告错误,只是允许重复键。我遇到了这个问题,这似乎是同一个问题,但看起来这个话题已经消失了:github.com/mccormicka/Mockgoose/issues/58

以上是关于在 NodeJS 中使用 Mongoose + Mockgoose 更新时忽略唯一索引的主要内容,如果未能解决你的问题,请参考以下文章

nodejs中使用mongoose保存数据

nodejs中使用mongoose保存数据

在 NodeJS 中使用 Mongoose + Mockgoose 更新时忽略唯一索引

如何使用 Nodejs/mongodb/mongoose 在 ejs/html 中显示数据库项目?

如何使用 mongoose 和 NodeJs 在 Mongodb 的数组字段中插入数据

MongoDB / Mongoose / nodejs 中的引用 - 并行化