如何在 mongodb 中限制 $push?

how to restrict $push in mongodb?

我正在学习mongodb,想知道是否可以通过匹配值来限制推送。

例如:

field1 = {
 id:123,
 title:123,
 likes: [{by:1,type:'like'}, {by:2, type:'like'}]
}

我可以在 likes 中限制 id 的推送吗?

您可能已经尝试过 $addToSet 运算符,但后来发现它不适合这里的情况,因为 "id" 和 "type" 的组合可能会有所不同。例如,您不想要的是具有 "like" 和 "dislike".

这两种类型的相同 "id" 值

然而,这是一个典型的"voting"模型,目前的结构并不是最好的。一个更好的模型是这样的,例如基本字段:

{
    "_id": 123,
    "likeCount": 2,
    "dislikeCount": 0,
    "likes": [456,789]
    "dislikes": []
}

拥有单独的数组对于原子更新过程很重要,因为您不能 $pull and $push 来自一个数组。但不仅如此,因为它加强了保持 "count" 值背后的逻辑,因为这对于简单查询很有用,因为排序而不是计算数组长度。

为了 post 为您不想在数组中重复的用户提供 "like",尽管有这些值,$addToSet 运算符仍然不是最佳运算符现在是真正独一无二的。您还想限制 "count",因此将条件添加到更新中的查询:

db.collection.update(
    { "_id": 123, "likes": { "$ne": 456 } },
    { 
        "$push": { "likes": 456 },
        "$inc": { "likeCount": 1 }            
    }
)

这样一来,如果用户已经为他们的 "like" 投票,那么不仅不会添加任何内容,而且 "count" 也会保持正确的总数。基本上不满足更新的查询条件,因为数组中已经有一个元素匹配该值。所以文档不匹配,没有任何更新。

这是一个很好的方法,但我们可以做得更好。如果用户已经 post 改为 "dislike" 并且现在改变主意改为 "like" 怎么办?您真正需要的是 "two" 更新语句来涵盖可能的条件,这就是 Bulk Operations API 的用武之地,以便在单个请求中处理该逻辑:

var bulk = db.collection.initializeOrderedBulkOp();

// match and update where a dislike is present
bulk.find({ 
    "_id": 123, 
    "likes": { "$ne": 456 },
    "dislikes": 456
}).updateOne({
    "$push": { "likes": 456 },
    "$pull": { "dislikes": 456 }
    "$inc": {
        "likeCount": 1,
        "dislikeCount": -1
    }
});

// match and update where no dislike exists
bulk.find({ 
    "_id": 123, 
    "likes": { "$ne": 456 },
    "dislikes": { "$ne": 456 }
}).updateOne({
    "$push": { "likes": 456 },
    "$inc": { "likeCount": 1 }
});

// Send requests to server and respond
bulk.execute();

在这种情况下,如果第一个语句不匹配,因为没有不喜欢,则不会更新任何内容,但如果有不喜欢,则会进行正确的调整。

对于第二个请求,如果 dislikes 数组中没有任何匹配项并且 likes 数组中也没有匹配项,则将应用此请求。所以这将申请新的投票,并且与之前的声明不冲突。尽管有这两个语句,但根据状态条件,upadte 只应用一次或根本不应用。

这是正确处理此类投票的基本模式,因为您保留每种投票类型的列表并维护计数以便于访问。 "dislikes" 过程几乎与您需要检查的元素的逻辑相反,删除选票也有类似的条件。