当父文档可能不存在时更新MongoDB子文档
这里是我的数据,包括书籍collections,还有books.reviews子集合。
books = [{ _id: ObjectId("5558f40ad498c653748cf045"), title: "Widget XYZ", isbn: 1234567890, reviews: [ { _id: ObjectId("5558f40ad498c653748cf046"), userId: "01234", rating: 5, review: "Yay, this great!" }, { _id: ObjectId("5558f40ad498c653748cf047"), userId: "56789", rating: 3, review: "Meh, this okay." } ] }]
在Node.js(使用Mongoose)中,我需要用户能够通过表单提交评论,并将评论和书籍发送到服务器,并具有以下条件:
- 如果图书不存在已经具有特定的isbn,添加它,然后添加审查(它显然不存在)。
- 如果这本书确实存在…
- 如果此用户的评论不存在,请添加。
- 如果该用户的评论确实存在,请对其进行更新。
我可以用4个单独的查询来做到这一点,但我知道这不是最佳的。 什么是我可以使用的最less数量的查询(当然,这些查询是什么)?
你基本上有三种情况:
- 这本书和评论都存在。 这是一个简单的
$set
- 该书存在,但不是审查。 这需要
$push
- 该书不存在。 这需要
{upsert:1}
和$setOnInsert
我无法find一种方法来统一其中任何两个,而不会损害数据完整性,以防发生故障(请记住,MongoDB没有primefaces事务)。
所以我最好的想法是以下几点:
// Case 1: db.books.update({isbn:'1234567890', review: { $elemMatch: {userID: '01234'}}}, {$set: {'review.$.rating': NEW_RATING}} ) // Case 2: db.books.update({isbn:'1234567890', review: { $not: { $elemMatch: {userID: '01234'}}}}, {$push: {review: {rating: NEW_RATING, userID:'01234'}}} ) // Case 3: db.books.update({isbn:'1234567890'}, {$setOnInsert: {review: [{rating: NEW_RATING, userID:'01234'}]}}, {upsert:1} )
你可以盲目地运行这三个更新,因为它们之间没有重叠的情况。 事情的美妙之处在于所有这些操作都是幂等的 。 所以你可以申请一次或几次,总是得到相同的结果。 这在故障转移的情况下尤为重要。 另外,如果您的数据库出现故障,您的数据库不可能不一致,也不可能丢失现有的数据。 在最坏的情况下,审查不会更新。 最后,即使在并发更新的情况下,这也应该保证数据的一致性(即:在这种情况下,一个更新将会覆盖另一个更新,但是不应该最终为同一本书提供两个文档,或者同一个用户的两个评论相同书)。
由于在这里迟了,所以后面的点必须得到证实,所以我的分析可能会有些疑问。
最后,如果你想减lessMongoDB和你的应用之间的往返次数,你可以看一下update
database命令,允许你在一个命令中包装几个更新。