【问题标题】:Can mongo upsert array data?mongo 可以 upsert 数组数据吗?
【发布时间】:2012-11-15 07:35:52
【问题描述】:

我有一个这样的 mongo 文档。

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

我希望能够使用单个 mongo 命令插入和更新array,而不是在find() 中使用条件,然后根据对象的存在运行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 调用执行此操作?

【问题讨论】:

  • 令我惊讶的是,mongo 仍然没有我在使用 mongo 的每个项目中多次遇到的这个基本功能:/

标签: node.js mongodb


【解决方案1】:

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

我在这里找到了答案:https://stackoverflow.com/a/42952359/1374488

【讨论】:

    【解决方案2】:

    嘿,您可以使用数组过滤器选项来做到这一点。 你可以参考这里 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 } ]
       })
    

    【讨论】:

    • 这将非常有用,但在我手中它只有在更新对象和数组过滤器中的标识符相同时才有效,例如:db.collection.update({'tester': 'tom'}, { $set: { "array.$[element].letter": "c"}}, { arrayFilters: [ { "element.letter": "b"} ]}) - 这不太有用。此外,文档中提到的upsert 选项允许您在不匹配的情况下插入新文档,而不是向数组中添加新元素,因此这并不能解决 OPs 问题。
    【解决方案3】:

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


    一个示例文档是

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

    要使用 id 3 和值“c”插入项目,你会这样做

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

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

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

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

    db.widgets.update(
        { tester: 'tom' }, 
        { 
            $unset: { 'items.3': true },
            $pull: { itemNames: 3 }
        }
    )
    

    【讨论】:

    • 这可行,但请注意,您将失去按 ID 索引项目的能力,因为 mongo 无法在给定子对象的所有键上创建索引。跨度>
    【解决方案4】:

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

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

    当文档不存在、文档存在但数组中的条目不存在以及条目存在时,这应该有效。

    同样,这仅在您有某些内容时才有效,例如本示例中的 id 字段,该字段在数组的元素中应该是唯一的。

    【讨论】:

    • 谢谢,使用文档中的 2 个字段进行搜索时效果很好
    • $push 查询中添加一个$exists 签入,以便在两次调用之间发生问题时它会失败。
    • 我也在寻找解决这个问题的方法——几年后。这有什么更新吗?
    • 你认为bulk-find-upsert 可以一次性解决这个问题吗?
    【解决方案5】:

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

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

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

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

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

    【讨论】:

    • 请注意,对嵌入式文档的某种形式的“虚拟集合”支持是一种流行的请求。您可以在 MongoDB 问题跟踪器中投票/观看SERVER-142
    猜你喜欢
    • 2014-12-06
    • 2018-06-12
    • 1970-01-01
    • 1970-01-01
    • 2019-01-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多