如果MongoDB文档字段不存在,我该如何更新?

Mir*_*age 37 mongodb

我收集foo了以下文件:

{site_id: 'xxx', title: {ru: 'a', en: 'b'}, content: {ru: 'a', en: 'b'}}
{site_id: 'xxx', title: {ru: 'c', de: 'd'}, content: {ru: 'c', de: 'd'}}
Run Code Online (Sandbox Code Playgroud)

我需要更新可能存在或不存在的多个字段:

db.foo.update(
    { site_id: 'xxx'},
    { $set: {'title.de': '', 'content.de': ''}},
    {multi: true}
)
Run Code Online (Sandbox Code Playgroud)

但我需要一些$set不会覆盖价值的东西.

nut*_*ike 48

您可以向update语句添加查询:

db.foo.update({'title.de': {$exists : false}}, {$set: {'title.de': ''}})
Run Code Online (Sandbox Code Playgroud)

更新

对于您修改过的问题,我的解决方案看起来像这样 - 这对您有用吗?(如果不是,为什么?)

db.foo.update({site_id: 'xxx', 'title.de': {$exists : false}}, {$set: {'title.de': ''}, {multi: true})
db.foo.update({site_id: 'xxx', 'content.de': {$exists : false}}, {$set: {'content.de': ''}}, {multi: true})
Run Code Online (Sandbox Code Playgroud)

  • 我想在单个查询中执行此操作. (12认同)
  • @Nitin,我认为这个多语句有问题;它只会更新不存在 **all** 指定字段的文档。可以在这里使用`$or`,但是所有字段都将在其中一个不存在的任何文档中被覆盖。我也在试图找到一个“单一操作”的解决方案...... (3认同)

Xav*_*hot 15

开始Mongo 4.2db.collection.update()可以接受聚合管道,最后允许基于另一个字段更新/创建一个字段:

这样,我们可以在更新阶段而不是在匹配阶段移动字段检查,从而使其成为一次性更新:

// { site_id: "xxx", title: { ru: "a", en: "b" }, content: {} }
// { site_id: "xxx", title: { ru: "c", de: "d" }, content: { ru: "c" } }
db.collection.update(
  { site_id: "xxx" },
  [{ $set: {
    "title.de": { $cond: [ { $not: ["$title.de"] }, "", "$title.de" ] },
    "content.ru": { $cond: [ { $not: ["$content.ru"] }, "", "$content.ru" ] }
  }}],
  { multi: true }
)
// { site_id: "xxx", title: { ru: "a", en: "b", de: "" }, content: { ru: "" } }
// { site_id: "xxx", title: { ru: "c", de: "d"         }, content: { ru: "c" } }
Run Code Online (Sandbox Code Playgroud)
  • 第一部分{ site_id: "xxx" }是匹配查询,过滤要更新的文档。

  • 第二部分[{ $set: { ... } }]是更新聚合管道(注意方括号表示使用聚合管道)。$set是一个新的聚合运算符,是 的别名$addFields。此阶段的其余部分检查$cond是否title.de存在,如果存在,则保持原样,否则将其创建为''.

  • 不要忘记{ multi: true },否则只会更新第一个匹配的文档。

  • 谢谢!正是我一直在寻找的东西。 (4认同)

小智 10

有一个更新字段运算符$ setOnInsert,它符合您的要求.请阅读这里的文件:https://docs.mongodb.com/manual/reference/operator/update/setOnInsert/#up._S_setOnInsert

  • 注意:如果要为现有文档设置新字段,则不起作用.如果在upsert期间未插入新文档,setOnInsert不会执行任何操作 (10认同)

小智 7

我有一个特定案例的解决方案,但可能对某人有所帮助.

我的情况是:更新几个字段,其中一个字段必须只更新一次(让我们称之为"Date_of_first_update").

> db.test.find();
{ "_id" : ObjectId("57f298fdeb30478a033c70e4"), "a" : "1", "b" : "2" }

First update:

> db.test.updateOne({ "_id" : ObjectId("57f298fdeb30478a033c70e4")}, 
  {$set: {a: 100, b: 200 }, $min : {'Date_of_first_update' : (new Date())  }});

Result: 'a', 'b' updated, 'Date_of_first_update' is set.

{ "_id" : ObjectId("57f298fdeb30478a033c70e4"), "a" : 100, "b" : 200, "Date_of_first_update" : ISODate("2016-10-03T**17:47:43**.570Z") }

Second update:

> db.test.updateOne({ "_id" : ObjectId("57f298fdeb30478a033c70e4")}, 
  {$set: {a: 400, b: 800 }, $min : {'Date_of_first_update' : (new Date()) }});

Result: 'a', 'b' updated, 'Date_of_first_update' left unchanged, as I needed!!!

{ "_id" : ObjectId("57f298fdeb30478a033c70e4"), "a" : 400, "b" : 800, "Date_of_first_update" : ISODate("2016-10-03T**17:47:43**.570Z") } 
Run Code Online (Sandbox Code Playgroud)


dav*_*ouk 7

如果有人和我一样遇到问题: 在此输入图像描述

我的解决方案是仅当更新导致在 ( upsert: true)上插入新内容时才设置 _id

return {
    updateOne: {
        filter: {
            email: shadowUser.email,
        },
        update: {
            $set: user,
            $setOnInsert: { _id: shadowUser._id },
        },
        upsert: true,
    },
};
Run Code Online (Sandbox Code Playgroud)