MongoDB-在数组中更新或插入对象
我有以下收藏MongoDB-在数组中更新或插入对象,mongodb,Mongodb,我有以下收藏 { "_id" : ObjectId("57315ba4846dd82425ca2408"), "myarray" : [ { userId : ObjectId("570ca5e48dbe673802c2d035"), point : 5 }, { userId :
{
"_id" : ObjectId("57315ba4846dd82425ca2408"),
"myarray" : [
{
userId : ObjectId("570ca5e48dbe673802c2d035"),
point : 5
},
{
userId : ObjectId("613ca5e48dbe673802c2d521"),
point : 2
},
]
}
这些是我的问题
我想进入myarray
如果userId
不存在,应该将它附加到myarray
中。如果存在userId
,则应将其更新到点
我找到了这个
但如果userId
不存在,则不会发生任何事情
和
但如果userId
对象已经存在,它将再次推送
在MongoDB中执行此操作的最佳方法是什么?不幸的是,在嵌入式阵列上无法执行“upsert”操作。运算符根本不存在,因此在一条语句中不可能这样做。因此,必须执行两个更新操作才能执行所需操作。另外,这两个更新的应用顺序对于获得期望的结果也很重要。试试这个
db.collection.update(
{ _id : ObjectId("57315ba4846dd82425ca2408")},
{ $pull: {"myarray.userId": ObjectId("570ca5e48dbe673802c2d035")}}
)
db.collection.update(
{ _id : ObjectId("57315ba4846dd82425ca2408")},
{ $push: {"myarray": {
userId:ObjectId("570ca5e48dbe673802c2d035"),
point: 10
}}
)
解释:
在第一个语句中,$pull
从文档上的数组中删除具有userId=ObjectId(“570ca5e48dbe673802c2d035”)
的元素,其中\u id=ObjectId(“57315ba4846dd82425ca2408”)
在第二个$push
插入
这个对象
{userId:ObjectId(“570ca5e48dbe673802c2d035”),point:10}
在同一个数组中。Flying Fisher接受的答案是先删除现有记录,然后再推送它
更安全的方法(常识)是首先尝试更新记录,如果没有找到匹配项,则插入它,如下所示:
// first try to overwrite existing value
var result = db.collection.update(
{
_id : ObjectId("57315ba4846dd82425ca2408"),
"myarray.userId": ObjectId("570ca5e48dbe673802c2d035")
},
{
$set: {"myarray.$.point": {point: 10}}
}
);
// you probably need to modify the following if-statement to some async callback
// checking depending on your server-side code and mongodb-driver
if(!result.nMatched)
{
// record not found, so create a new entry
// this can be done using $addToSet:
db.collection.update(
{
_id: ObjectId("57315ba4846dd82425ca2408")
},
{
$addToSet: {
myarray: {
userId: ObjectId("570ca5e48dbe673802c2d035"),
point: 10
}
}
}
);
// OR (the equivalent) using $push:
db.collection.update(
{
_id: ObjectId("57315ba4846dd82425ca2408"),
"myarray.userId": {$ne: ObjectId("570ca5e48dbe673802c2d035"}}
},
{
$push: {
myarray: {
userId: ObjectId("570ca5e48dbe673802c2d035"),
point: 10
}
}
}
);
}
这也会提高(常识,未测试)性能,如果在大多数情况下记录已经存在,则只会执行第一个查询。当您想要更新或在数组中插入值时,请尝试执行它 数据库中的对象
key:name,
key1:name1,
arr:[
{
val:1,
val2:1
}
]
质疑
在MongoDB 3.6中,现在可以。我还没有找到任何基于单原子查询的解决方案。取而代之的是基于两个查询序列的3种方法:
$pull
(从阵列中删除项目),然后$push
(将更新的项目添加到阵列)
$set
(如果存在,则更新数组中的项),然后获取结果并检查更新操作是否成功,或者是否需要$push
(插入项)
$addToSet
(如果不存在项,则添加该项),然后始终$set
更新数组中的项
db.collection.update({_id: ObjectId("57315ba4846dd82425ca2408")},
myarray: { $not: { $elemMatch: {userId: ObjectId("570ca5e48dbe673802c2d035")} } } },
{
$addToSet : {
myarray: {
userId: ObjectId("570ca5e48dbe673802c2d035"),
point: 10
}
}
},
{ multi: false, upsert: false});
db.collection.update({
_id: ObjectId("57315ba4846dd82425ca2408"),
"myArray.userId": ObjectId("570ca5e48dbe673802c2d035")
},
{ $set : { myArray.$.point: 10 } },
{ multi: false, upsert: false});
第三条路更安全。
$addToSet
仅在项目不存在时添加,否则不会发生任何事情。在两个并发请求的情况下,只有一个请求将缺少的项添加到数组中 数组更新和创建不要在一个查询下混合使用,如果您非常关心原子性,那么有以下解决方案:
将您的模式标准化为
{
"_id" : ObjectId("57315ba4846dd82425ca2408"),
userId : ObjectId("570ca5e48dbe673802c2d035"),
point : 5
}
有一个名为“从MongoDB v4.2开始”的选项
- 检查
中是否存在myarray.userId
的条件userId
$cond
- 如果是,则
迭代$map
数组的循环,并检查条件myarray
是否匹配,然后使用userId
与新文档合并$mergeObjects
- 如果否,则
到concat新对象和$concatarray
myarray
作为参考,我在下面的链接中添加了详细的解决方案,该链接涵盖了相同的场景,即
如何将新对象添加到对象数组中,除非存在特定的对象值(例如userId),在这种情况下,更新对象
:如果能为将来的读者明确说明代码的作用,那就太好了OK,这很好,但在MongoDb中,id应该自动插入,而您手动将其添加到两个不同的CAL中,这是一个很好的想法,但代价很高。如果两个并发请求同时进入if语句,会发生什么情况?两个$addToSet
中只有一个有效,不是吗?@FabioFormosa是的,但你是对的,这仍然是不安全的。如果同时,不仅执行了$addToSet
,而且更新了点。。。此代码肯定应该在事务中。在一个查询中可以创建或更新此架构文档。幸运的是,upsert选项并不能真正帮助解决此问题,因为它需要一个(即所有数组元素都在查询中显式定义)。如果未找到匹配项,则upsert将创建一个完整的新文档,但它不会将新元素“upsert”到数组中。方法#3如何安全?如果并发请求修改了数组元素,使得点
不再是10
,那么第一个调用将为相同的用户ID
向数组添加第二个元素。唯一的竞争条件是:元素不在数组中,第一个请求完成$addToSet
,现在,第二个请求运行更新
,最后第一个请求运行$set
。因为只有一个$addToSet
命中,所以总是只有一个元素(从来没有两个!)!点字段的值将是执行$set
的最后一个请求的值。您不应该对执行$addToSet
的请求感兴趣。因此,这是一种更安全的方法,因为它不属于双插入。如果相同的userId
存在一个数组元素,但具有不同的点
值,则相同的userId
的第二个元素将添加到数组中。$addToSet
方法只有在10
永远是点
唯一可能的值时才有效,这是不可能的。啊,现在我明白你的意思了。我错过了$elemMatch
,因为我没有滚动足够远。在这种情况下,你是对的——它应该会起作用。您甚至可以使用$push
而不是$addToSet
,因为
var query = {
$inc:{
"arr.0.val": 2,
"arr.0.val2": 2
}
}
.updateOne( { "key": name }, query, { upsert: true }
key:name,
key1:name1,
arr:[
{
val:3,
val2:3
}
]
db.collection.update(
{ _id : ObjectId("57315ba4846dd82425ca2408")},
{ $pull: {"myarray.userId": ObjectId("570ca5e48dbe673802c2d035")}}
)
db.collection.update(
{ _id : ObjectId("57315ba4846dd82425ca2408")},
{
$push: {
"myarray": {
userId:ObjectId("570ca5e48dbe673802c2d035"),
point: 10
}
}
}
)
var result = db.collection.update(
{
_id : ObjectId("57315ba4846dd82425ca2408"),
"myarray.userId": ObjectId("570ca5e48dbe673802c2d035")
},
{
$set: {"myarray.$.point": {point: 10}}
}
);
if(!result.nMatched){
db.collection.update({_id: ObjectId("57315ba4846dd82425ca2408")},
{
$addToSet: {
myarray: {
userId: ObjectId("570ca5e48dbe673802c2d035"),
point: 10
}
}
);
db.collection.update({_id: ObjectId("57315ba4846dd82425ca2408")},
myarray: { $not: { $elemMatch: {userId: ObjectId("570ca5e48dbe673802c2d035")} } } },
{
$addToSet : {
myarray: {
userId: ObjectId("570ca5e48dbe673802c2d035"),
point: 10
}
}
},
{ multi: false, upsert: false});
db.collection.update({
_id: ObjectId("57315ba4846dd82425ca2408"),
"myArray.userId": ObjectId("570ca5e48dbe673802c2d035")
},
{ $set : { myArray.$.point: 10 } },
{ multi: false, upsert: false});
{
"_id" : ObjectId("57315ba4846dd82425ca2408"),
userId : ObjectId("570ca5e48dbe673802c2d035"),
point : 5
}
let _id = ObjectId("57315ba4846dd82425ca2408");
let uodateDoc = {
userId: ObjectId("570ca5e48dbe673802c2d035"),
point: 10
};
db.collection.update(
{ _id: _id },
[{
$set: {
myarray: {
$cond: [
{ $in: [updateDoc.userId, "$myarray.userId"] },
{
$map: {
input: "$myarray",
in: {
$mergeObjects: [
"$$this",
{
$cond: [
{ $eq: ["$$this.userId", updateDoc.userId] },
uodateDoc,
{}
]
}
]
}
}
},
{ $concatArrays: ["$myarray", [uodateDoc]] }
]
}
}
}]
)