所以我正在使用NodeJS和MongoDB,我正在创建一个端点,让客户端可以使用多个可选数据字段更新其用户配置文件。因此,其中一个更新查询可能如下所示:
{
name: { givenName: 'first' },
about: 'whatever',
auth: { password: 'hashedPW' }
}
Mongoose API文档说明了有关findByIdAndUpdate
的以下信息:所有非原子操作名称的顶级更新密钥都被视为集合操作。
因此,顶级密钥about
可以正常更新。但是,嵌套密钥name
和auth
会被更新值覆盖,而不是仅设置值。
现在我可以通过手动将每个字段更改为$set
键,但是有很多不同的字段,所以这样做会非常烦人。是否有一种简单的方法将$ set规则应用于子文档?即使用Mongoose选项或其他内容将语句转换为此语句:
{
$set : { name: { givenName: 'first' } },
$set : { about: 'whatever' },
$set : { auth: { password: 'hashedPW' } }
}
答案 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
}