在upsert上更新时,mongodb $ addToSet到非数组字段

Jam*_*ang 7 mongodb

我最近的项目遇到了与此问题相同的问题:问题

db.test.update(
    {name:"abc123", "config.a":1  }, 
    {$addToSet:{ config:{a:1,b:2} } }, 
    true 
)
Run Code Online (Sandbox Code Playgroud)

会产生这样的错误:

无法将$ addToSet应用于非数组字段

但改成后:

db.test.update(
    {name:"abc123", "config.a":{$in:[1]}  }, 
    {$addToSet:{ config:{a:1,b:2} } }, 
    true 
)
Run Code Online (Sandbox Code Playgroud)

它工作正常.

还引用了这个链接:答案

任何人都可以解释发生了什么吗?"config.a":1会将config变为对象吗?当"config.a":{$于:[1]}不会呢?

Nei*_*unn 23

您在此处尝试执行的操作是仅在项目不存在的情况下向数组添加新项目,并创建不存在该项目的新文档.您选择$addToSet是因为您希望这些项目是唯一的,但实际上您确实希望它们仅由"a"组成.

所以$addToset不会这样做,而你需要"测试"存在的元素.但这里真正的问题是不可能同时做到这一点和"upsert".逻辑无法工作,因为每当找不到数组元素时都会创建一个新文档,而不是像你想要的那样附加到数组元素.

设计中的当前操作错误$addToSet不能用于"创建"数组,而只能用于"添加"成员到现有数组.但如前所述,您在实现逻辑方面还存在其他问题.

您需要的是一系列更新操作,每个操作都"尝试"执行预期的操作.这只能通过多个语句来完成:

// attempt "upsert" where document does not exist
// do not alter the document if this is an update
db.test.update(
    { "name": "abc" },
    { "$setOnInsert": { "config": [{ "a": 1, "b": 2 }] }},
    { "upsert": true }
)

// $push the element where "a": 1 does not exist
db.test.update(
    { "name": "abc", "config.a": { "$ne": 1 } },
    { "$push": { "config": { "a": 1, "b": 2 } }}
)

// $set the element where "a": 1 does exist
db.test.update(
    { "name": "abc", "config.a": 1 },
    { "$set": { "config.$.b": 2 } }
)
Run Code Online (Sandbox Code Playgroud)

在第一次迭代时,第一个语句将"upsert"文档并使用items创建数组.第二个语句与文档不匹配,因为"a"元素具有指定的值.第三个语句将匹配文档,但它不会在写入操作中更改它,因为值未更改.

如果您现在更改输入,"b": 3则会得到不同的响应,但结果如下:

db.test.update(
    { "name": "abc" },
    { "$setOnInsert": { "config": [{ "a": 1, "b": 3 }] }},
    { "upsert": true }
)

db.test.update(
    { "name": "abc", "config.a": { "$ne": 1 } },
    { "$push": { "config": { "a": 1, "b": 3 } }}
)

db.test.update(
    { "name": "abc", "config.a": 1 },
    { "$set": { "config.$.b": 3 } }
)
Run Code Online (Sandbox Code Playgroud)

所以现在第一个语句与文档匹配"name": "abc"但没有做任何事情,因为唯一有效的操作是"插入".第二个语句不匹配,因为"a"匹配条件.第三个参数与"a"的值匹配,并将匹配元素中的"b"更改为所需的值.

随后将"a"更改为数组中不存在的另一个值允许1和3都不执行任何操作,但第二个语句将另一个成员添加到数组中,通过其"a"键保持内容唯一.

同时提交一份没有现有数据变更的声明,当然会产生一个回复,表明所有帐户都没有变化.

这就是你的运作方式.您可以使用"有序" 批量操作执行此操作,以便服务器只有一个请求和响应,并且具有修改或创建的有效响应.


Mik*_*ail 5

正如 MongoDB JIRA ( https://jira.mongodb.org/browse/SERVER-3946 ) 上的这个问题所解释的,这可以通过单个查询来解决:

以下更新失败,因为我们使用的$addToSet字段也包含在第一个参数中(接受要查询的字段和值的字段)。据我了解,upsert: true在这种情况下,您不能使用我们$addToSet查询的同一字段。

db.foo.update({x : "a"},  {$addToSet: {x: "b"}} , {upsert: true}); // FAILS
Run Code Online (Sandbox Code Playgroud)

解决方案是使用$elemMatch: {$eq: field: value}

db.foo.update({x: {$elemMatch: {$eq: "a"}}}, {$addToSet: {x: "b"}}, {upsert: true});
Run Code Online (Sandbox Code Playgroud)