Mongoose是不是可扩展的文档数组编辑和版本控制?

我正在开发与Node.js和MongoDB / Mongoose的Web应用程序。 我们最常用的Model,Record有许多子文档数组。 其中一些例如包括“评论”,“预订”和“订阅者”。

在客户端应用程序中,只要用户点击“删除”button,它就会针对该特定评论引发对删除路由的AJAX请求。 我遇到的问题是,当许多这些AJAX调用一次进来时,Mongoose在某些(但不是全部)调用上失败,出现“Document not found”错误。

这种情况发生在一次快速和多次呼叫时。 我认为这是由于Mongoose中的版本导致文档冲突。 我们目前的删除过程是:

  1. 使用Record.findById()获取文档
  2. 从适当的数组中删除子文档(使用,比如说comment.remove()
  3. 调用record.save()

我find了一个解决scheme,我可以使用Record.findByIdAndUpdate手动更新集合,然后使用$pull操作符。 但是,这意味着我们不能使用mongoose的任何中间件,而完全放松版本控制。 而我越想到它,我越发现这种情况发生,我将不得不使用findByIdAndUpdatefindAndRemove Mongoose的包装函数。 我能想到的唯一的另一个解决scheme是将删除尝试放入一个while循环,并希望它的工作,这似乎是一个非常糟糕的修复。

使用Mongoose包装器并不能真正解决我的问题,因为它不允许我使用任何types的中间件或钩子,这基本上是使用Mongoose的巨大好处之一。

这是否意味着Mongoose在快速编辑方面基本上没有用处,我可能只是使用本机MongoDB驱动程序? 我误解了mongoose的限制吗? 我怎么能解决这个问题?

Mongoose版本化的文档数组编辑不可扩展,原因很简单,它不是primefaces操作。 结果是,你有更多的数组编辑活动,两个编辑会发生冲突的可能性就越大,你就会承受代码中重试/恢复的开销。

对于可伸缩的文档数组操作,您必须使用primefaces数组更新操作符更新: $pull[All]$push[All]$pop$addToSet$ 。 当然,如果您还需要原始或生成的文档,您还可以使用findByIdAndUpdatefindOneAndUpdate基于findByIdAndUpdateprimefaces的方法使用这些运算符。

正如你所提到的,使用update而不是findOne + save一个很大的缺点就是在update过程中没有执行Mongoose中间件和validation。 但是,如果你想要一个可扩展的系统,我不认为你有任何select。 我宁愿手动复制一些中间件和validation逻辑的更新情况,而不得不承受使用Mongoose的版本化文档数组编辑的可扩展性的惩罚。 嘿,至less你仍然得到Mongoose的基于模式的types铸造更新的好处!

,从我们自己的经验来看,你的问题的答案是“是”。 Mongoose 不能对基于arrays的快速更新进行扩展。

背景

HabitRPG遇到同样的问题。 在用户增长最近激增(将我们的数据库带到6GB)之后,我们开始经历许多基于arrays的更新(版本错误的背景 )的VersionError 。 ensureIndex({_id:1,__v1:1})有一点帮助,但是还有更多的用户join。 在我看来,Mongoose对于基于arrays的更新确实不可扩展。 你可以在这里看到我们的整个调查过程 。

如果你可以从一个数组移动到一个对象,那就这样做。 例如, comments: Schema.Types.Array => comments: Schema.Types.Mixed ,并根据post.comments.{ID}.date ,甚至手动post.comments.{ID}.position

如果你坚持使用数组:

  1. db.collection.ensureIndex({_id:1,__v:1})
  2. 使用上述的方法。 你不会受益于钩子和validation,但有更糟糕的事情。

我强烈build议将这些数组放入新的集合中。 例如,一个评论集合,其中每个文档都有一个loggingID来指示它所属的位置。 这是一个更具可扩展性的解决scheme。

你是对的,Mongoose的数组操作不是primefaces的,因此不能很好地扩展。

我想到了另一个想法,我不确定,但似乎值得提供:软删除。

Mongoose非常关心数组结构的变化,因为它们使未来的变化变得模糊不清。 但是如果你只是用comment.deleted=true来标记注释子文档,那么你也许可以做更多这样的操作而不会遇到冲突。 那么你可以有一个cron任务,并通过实际删除这些评论。

噢,另一个想法是使用某种内存caching,所以如果一个logging在过去的几分钟内被访问/编辑过,那么它就可以在不需要从服务器上取出的情况下使用,这意味着两个请求进来时间将会修改同一个对象。

注意:我不确定这些是一般的好主意还是他们会解决你的问题,所以如果他们不好,那就去编辑/评论/ downvote 🙂