问题 嵌入式文档中的“upsert”


我目前有以下数据集:

{  
    'component_id':1,  
    '_locales':[   
        {  
            'url': 'dutch',  
            'locale': 'nl_NL'  
        } 
    ] (etc)
}

如果我想用语言环境更新行,我会运行类似于:

db.components.update(
    {'component_id': 1, '_locales.locale': 'nl_NL'},
    {$set: {'_locales.$': {'url': 'new url','locale':'nl_NL'}}, 
    true
);

这很好,直到语言环境不存在:

db.components.update(
    {'component_id': 1, '_locales.locale': 'en_US'},
    {$set: {'_locales.$': {'url': 'new url','locale':'en_US'}}, 
    true
);

因为component_id上有一个唯一索引,所以会抛出一个抱怨重复键的异常。

有没有办法自动添加具有不同语言环境的新“文档”并更新它(如果它已经存在)?根据使用位置运算符的文档将无法使用'upserting'。


5255
2018-04-23 08:28


起源



答案:


您可以使用 $ addToSet 添加到集合确保没有重复的数组元素,但这不适用于您的“更新”情况。

为了做你想做的事,你需要将你的数据结构改为:

{
    "_id" : ObjectId("4f9519d6684c8b1c9e72e367"),
    "component_id" : 1,
    "_locales" : {
        "nl_NL" : {
            "url" : "dutch"
        }
    }
}

现在,您可以使用以下命令对nl_NL语言环境进行更新:

db.components.update( { component_id: 1 }, { $set: { '_locales.nl_NL.url' : 'new url' } }, true );

一个新的语言环境也可以使用,例如:

db.components.update( { component_id: 1 }, { $set: { '_locales.en_US.url' : 'American' } }, true );

您可能还想考虑将语言环境作为嵌套对象的一部分,例如:

{
    "_id" : ObjectId("4f9519d6684c8b1c9e72e367"),
    "component_id" : 1,
    "_locales" : {
        "nl_NL" : {
            "url" : "dutch"
            "locale" : "nl_NL"                 
        }
    }
}

这使得在某些情况下更容易检索数据。


10
2018-04-23 09:11



嗨Derick,谢谢你的回复。您的建议实际上是我的初始数据结构,我将其更改为上述内容。一个原因是创建索引:_locales.url而不是每个语言环境:_locales.nl_NL.url,_locales.en_US.url等。目前我通过获取所有_locales数据并修改/添加我正在工作的语言环境来解决这个问题用'手动'。当我完成后,我用新的'_locales'替换当前的'_locales'。现在这已足够,性能明智这可能不是一个好主意。 - Wim Wisselink
我同意,由于索引,使用“未定义”键通常不是一件好事。有时,如果在其他情况下由于索引/其他原因导致性能提高,则最好执行两次查询来执行更新。大多数时候没有真正的“正确方法”。使用它,如果它没有执行更改它并运行两个查询进行更新。 - Derick
@Derick:我正面临类似的问题。 stackoverflow.com/questions/32038606/... - dark_shadow


答案:


您可以使用 $ addToSet 添加到集合确保没有重复的数组元素,但这不适用于您的“更新”情况。

为了做你想做的事,你需要将你的数据结构改为:

{
    "_id" : ObjectId("4f9519d6684c8b1c9e72e367"),
    "component_id" : 1,
    "_locales" : {
        "nl_NL" : {
            "url" : "dutch"
        }
    }
}

现在,您可以使用以下命令对nl_NL语言环境进行更新:

db.components.update( { component_id: 1 }, { $set: { '_locales.nl_NL.url' : 'new url' } }, true );

一个新的语言环境也可以使用,例如:

db.components.update( { component_id: 1 }, { $set: { '_locales.en_US.url' : 'American' } }, true );

您可能还想考虑将语言环境作为嵌套对象的一部分,例如:

{
    "_id" : ObjectId("4f9519d6684c8b1c9e72e367"),
    "component_id" : 1,
    "_locales" : {
        "nl_NL" : {
            "url" : "dutch"
            "locale" : "nl_NL"                 
        }
    }
}

这使得在某些情况下更容易检索数据。


10
2018-04-23 09:11



嗨Derick,谢谢你的回复。您的建议实际上是我的初始数据结构,我将其更改为上述内容。一个原因是创建索引:_locales.url而不是每个语言环境:_locales.nl_NL.url,_locales.en_US.url等。目前我通过获取所有_locales数据并修改/添加我正在工作的语言环境来解决这个问题用'手动'。当我完成后,我用新的'_locales'替换当前的'_locales'。现在这已足够,性能明智这可能不是一个好主意。 - Wim Wisselink
我同意,由于索引,使用“未定义”键通常不是一件好事。有时,如果在其他情况下由于索引/其他原因导致性能提高,则最好执行两次查询来执行更新。大多数时候没有真正的“正确方法”。使用它,如果它没有执行更改它并运行两个查询进行更新。 - Derick
@Derick:我正面临类似的问题。 stackoverflow.com/questions/32038606/... - dark_shadow