【问题标题】:Insert only new properties into embedded document仅将新属性插入嵌入文档
【发布时间】:2015-08-11 12:21:55
【问题描述】:

我想在嵌入文档中插入新值只有当嵌入文档还没有密钥时。例如,我的文档最初是

{
    name: "doc1",
    embeddedDoc: {
        a: 123,
        b: 456
    }
}

如果我要“插入”{b: 789, c: 101},我希望结果有embeddedDoc: {a: 123, b: 456, c: 101}(插入新的c,不要碰现有的b)。有没有办法做到这一点?

到目前为止,我最好的解决方案是对插入对象中的每个元素运行一个查询:

collection.update(
    {"embeddedDoc.b": {$exists: false}},
    {$set: {"embeddedDoc.b": 456}}
)

【问题讨论】:

    标签: mongodb mongodb-query


    【解决方案1】:

    就我个人而言,我的文档中不会有一个对象,而是一个数组。它本身并不能解决不覆盖数据的问题,但它确实将“键”上的“查找”变成了使用可以索引的“数据”。如果您打算定期进行此类更新,这将产生“重大”影响。

    {
        "name": "Doc1",
        "properties": [
            { "key": "a", "value": 123 },
            { "key": "b", "value": 456 }
        ]
    }
    

    因此对于“b”和“c”的新数据,您可以进行如下查询尝试:

    db.collection.update(
        { "properties.key" { "$nin": [ "b", "c" ]  } },
        { 
            "$push": { 
                "properties": { 
                    "$each": [
                        { "key": "b", "value": 789 },
                        { "key": "c", "value": 101 }
                    ]
                }
            }
        },
        { "multi": true }
    )
    

    如前所述,至少“可以”使用$exists 的“键”查找不能使用的索引,并且您至少可以“尝试”将此操作作为“第一枪”执行,如果文档确实如此满足“b”和“c”,“key”值都不存在的给定条件,那么这些文档将受到影响。

    后备案例当然是对这里的每个属性单独尝试,但整个过程真的不难用Bulk Operations解决:

    var obj = { "b": 456, "c": 101 };
    
    var keys = Object.keys(obj),
        mapped = keys.map(function(key) {
            return { "key": key, "value": obj[key] };
        });
    
    var bulk = intitializeOrderedBulkOp();
    
    bulk.find({ "properties.key": { "$nin": keys } }).update({
        "$push": { "properties": { "$each": mapped } }
    });
    
    keys.forEach(function(key) {
        bulk.find({ "properties.key": { "$ne": key } }).update({
            "$push": { "properties": { "key": key, "value": obj[key] } }
        });
    });
    
    bulk.execute();
    

    请注意,如果第一个查询确实成功了,那么其他一切都可能是“无操作”。但是,如果您像这样使用“multi”来执行此操作,那么除非您已经知道要测试多少个文档,否则您永远无法确定。所以可能一次性发送是“比抱歉更安全”

    所以它很容易编码,由于批量操作而往返于服务器一次,并且由于能够使用索引而在查找数据以匹配条件时“高效”。

    您要实现的“放弃合并”逻辑总是意味着多次更新。但是,如果您至少使流程尽可能“高效”,那么至少可以少担心一件事。

    【讨论】:

    • 由于其他原因,我不得不按照您的建议更改为键/值数组,然后尝试一下。我认为您的更新查询不起作用,因为在您的示例中,谓词 $nin: ["b", "c"] 与带有键 ["a", "b"] 的文档不匹配,如果匹配,$push 会添加重复项。需要像 $addToSet 这样的带有可指定键的东西。
    • @ZachB 请仔细阅读,因为术语“使用”是“尝试”,这意味着它只能在两者都不存在的情况下起作用,而当它不存在时,您会退回到其他操作。这实际上是关于这里的批量操作,而不是保证第一次尝试一次将所有东西都放入其中总是有效的。
    • 哦哦,我现在明白了。谢谢。
    【解决方案2】:

    我非常怀疑您是否可以对许多属性进行 2 次查询。如果您有许多属性,您可以进行第一次查询以获取所有属性,然后知道您拥有的属性,找到缺少的属性作为设置差异。然后只更新缺失的属性。

    因此,对于任意数量的属性,您最终会得到 2 个查询和一个简单的应用程序逻辑。请注意,在高度繁忙的应用程序中,query1(find) 和 query2(update) 之间可能会发生一些事情。

    【讨论】:

    • 谢谢。我从我的问题中省略了我可能会更新多个文档(通常是 1 到 5 个),因此必须为每个文档完成 query1(find) 和 query2(update)。插入文档中的典型属性数量为 10 到 50,因此每个文档的两个查询仍然胜出,尽管它的代码更多。
    猜你喜欢
    • 2011-06-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-26
    • 1970-01-01
    • 1970-01-01
    • 2020-11-22
    • 2012-08-29
    相关资源
    最近更新 更多