53

我有一个像这样的 mongo 文件。

{
    "_id" : ObjectId("50b429ba0e27b508d854483e"),
    "array" : [
        {
            "id" : "1",
            "letter" : "a"
        },
        {
            "id" : "2",
            "letter" : "b"
        }
    ],
    "tester" : "tom"
}

我希望能够array使用单个 mongo 命令插入和更新,而不是在find()then 运行中使用条件insert()update()具体取决于对象的存在。

id是我想成为选择器的项目。所以如果我用这个更新数组:

{
    "id" : "2",
    "letter" : "c"
}

我必须使用$set声明

db.soup.update({
    "tester":"tom",
    'array.id': '2'
}, {
    $set: {
        'array.$.letter': 'c'
    }
})

如果我想在数组中插入一个新对象

{
    "id" : "3",
    "letter" : "d"
}

我必须使用$push声明

db.soup.update({
    "tester":"tom"
}, {
    $push: {
        'array': {
            "id": "3",
            "letter": "d"
        }
    }
})

我需要一种数组项的 upsert。

我必须以编程方式执行此操作,还是可以通过单个 mongo 调用执行此操作?

4

5 回答 5

22

我不知道有一个选项会像 MongoDB 2.2 那样插入到嵌入式数组中,因此您可能必须在应用程序代码中处理这个问题。

鉴于您希望将嵌入式数组视为一种虚拟集合,您可能需要考虑将数组建模为单独的集合。

您不能基于嵌入数组中的字段值执行 upsert,但$addToSet如果嵌入文档不存在,您可以使用它来插入:

db.soup.update({
    "tester":"tom"
}, {
    $addToSet: {
        'array': {
            "id": "3",
            "letter": "d"
        }
    }
})

这不适合您匹配id数组元素的确切用例,但如果您知道预期的当前值,可能会很有用。

于 2012-11-27T23:04:58.660 回答
21

我自己也遇到了这个问题。我无法找到一次调用解决方案,但我找到了一个两次调用解决方案,当您在数组元素中具有唯一值时该解决方案有效。首先使用该$pull命令,从数组中删除元素,然后使用$push.

db.soup.update({
    "tester":"tom"
}, {
    $pull: {
        'array': {
            "id": "3"
        }
    }
})
db.soup.update({
    "tester":"tom"
}, {
    $push: {
        'array': {
            "id": "3",
            "letter": "d"
        }
    }
})

这应该在文档不存在、文档存在但数组中的条目不存在以及条目存在时起作用。

同样,这仅在您拥有某些东西(例如id本例中的字段)时才有效,该字段在数组的元素中应该是唯一的。

于 2013-09-09T22:34:11.503 回答
5

如果您可以重组文档以使用对象而不是数组,则可以在某些情况下实现嵌入式文档集的 upsert。我刚刚在另一个问题上回答了这个问题,但为了方便起见,我会在此处对其进行调整。


一个示例文档是

{
    tester: 'tom',
    items: {
        '1': 'a',
        '2': 'b'
    }
}

要使用id3 和值 'c' 插入一个项目,你会这样做

db.coll.update(
    { tester: 'tom' }, 
    { $set: { 'items.3': 'c' } }
)

一个缺点是查询字段名称(使用$exists查询运算符)需要扫描。您可以通过添加一个存储属性名称的额外数组字段来解决此问题。该字段可以正常被索引和查询。Upserts 变成

db.coll.update(
    { tester: 'tom' }, 
    { 
        $set: { 'items.3': 'c' },
        $addToSet: { itemNames: 3 }
    }
)

(请记住$addToSetO(n)。)删除属性时,您还必须将其从数组中拉出。

db.widgets.update(
    { tester: 'tom' }, 
    { 
        $unset: { 'items.3': true },
        $pull: { itemNames: 3 }
    }
)
于 2014-01-25T05:45:46.693 回答
2

问题来自 2012 年。我查看了 mongo 文档:https ://docs.mongodb.com/manual/reference/method/Bulk.find.upsert Rob Watts 在评论中提到的,但它非常稀疏。

I found an answer here: https://stackoverflow.com/a/42952359/1374488

于 2020-01-15T09:08:35.700 回答
1

嘿,您可以使用数组过滤器选项来完成。您可以参考这里 https://docs.mongodb.com/manual/reference/method/db.collection.update/#update-arrayfilters

   db.getCollection('soup').update({'tester':tom}, { $set: { "collection.$[element].letter" : 'c' } },
   {
     arrayFilters: [ { "element.id": 2 } ]
   })
于 2019-08-28T05:08:04.237 回答