findByIdAndUpdate包含多个子文档

时间:2015-08-16 19:23:54

标签: node.js mongodb mongoose mongodb-query

所以我正在使用NodeJS和MongoDB,我正在创建一个端点,让客户端可以使用多个可选数据字段更新其用户配置文件。因此,其中一个更新查询可能如下所示:

{ 
    name: { givenName: 'first' },
    about: 'whatever',
    auth: { password: 'hashedPW' } 
}

Mongoose API文档说明了有关findByIdAndUpdate的以下信息:所有非原子操作名称的顶级更新密钥都被视为集合操作。

因此,顶级密钥about可以正常更新。但是,嵌套密钥nameauth会被更新值覆盖,而不是仅设置值。

现在我可以通过手动将每个字段更改为$set键,但是有很多不同的字段,所以这样做会非常烦人。是否有一种简单的方法将$ set规则应用于子文档?即使用Mongoose选项或其他内容将语句转换为此语句:

{ 
    $set : { name: { givenName: 'first' } },
    $set : { about: 'whatever' },
    $set : { auth: { password: 'hashedPW' } } 
}

1 个答案:

答案 0 :(得分:1)

您基本上需要将输入对象转换为"dot notation"表单,以避免在更新中覆盖其他可能的子键。这真的很简单:

var obj = { 
    name: { givenName: 'first' },
    about: 'whatever',
    auth: { password: 'hashedPW' } 
};

var target = {};

function dotNotate(obj,prefix) {
  prefix = (typeof(prefix) === 'undefined') ? "" : prefix;
  Object.keys(obj).forEach(function(key) {
    if ( typeof(obj[key]) === "object" ) {
      dotNotate(obj[key],key + ".")
    } else {
      target[prefix + key] = obj[key];
    }
  });
}

dotNotate(obj);

现在target对象如下所示:

{
    "name.givenName" : "first",
    "about" : "whatever",
    "auth.password" : "hashedPW"
}

因此,语句的更新块仅写为:

{ "$set": target }

作为参考,dotNotate()函数可以更精确和自包含。还包括较短的默认分配作为有效输入通常被认为是" truthy"。还有"前缀"应该在每次通话时都预先设定好以使其在任意深度上工作:

function dotNotate(obj,target,prefix) {
  target = target || {},
  prefix = prefix || "";

  Object.keys(obj).forEach(function(key) {
    if ( typeof(obj[key]) === "object" ) {
      dotNotate(obj[key],target,prefix + key + ".");
    } else {
      return target[prefix + key] = obj[key];
    }
  });

  return target;
}

然后你可以使用内联:

var update = { "$set": dotNotate(obj) };

如果您愿意,也可以传递这样的已定义对象:

var update = { "$set": {} };
dotNotate(obj,update["$set"]);

结果相同。

对于数组和嵌套深度也很好:

{
    "things" : [
        {
            "a" : 1,
            "b" : 2
        },
        {
            "a" : 3,
            "b" : 4
        }
    ],
    "bool" : false
}

输出:

{
    "things.0.a" : 1,
    "things.0.b" : 2,
    "things.1.a" : 3,
    "things.1.b" : 4,
    "bool" : false
}