Mongoose.js:嵌套属性的原子更新?
使用猫鼬版本3.6.4Mongoose.js:嵌套属性的原子更新?
说我有一个像MongoDB的文档,以便:
{
"_id" : "5187b74e66ee9af96c39d3d6",
"profile" : {
"name" : {
"first" : "Joe",
"last" : "Pesci",
"middle" : "Frank"
}
}
}
而且我有用户以下模式:
var UserSchema = new mongoose.Schema({
_id: { type: String },
email: { type: String, required: true, index: { unique: true }},
active: { type: Boolean, required: true, 'default': false },
profile: {
name: {
first: { type: String, required: true },
last: { type: String, required: true },
middle: { type: String }
}
}
created: { type: Date, required: true, 'default': Date.now},
updated: { type: Date, required: true, 'default': Date.now}
);
我提交表单传递一个名为:profile[name][first]
的值为Joseph
因此我想只更新用户的fi第一个名字,但保留他最后的和中间的孤独,我以为我会只是做:
User.update({email: "[email protected]"}, req.body, function(err, result){});
但是,当我做到这一点,“删除”的profile.name.last
和profile.name.middle
属性和我结束了一个文档,看起来像:
{
"_id" : "5187b74e66ee9af96c39d3d6",
"profile" : {
"name" : {
"first" : "Joseph"
}
}
}
所以基本上覆盖所有的profile
与req.body.profile
,我的猜测是有道理的。有没有办法解决这个问题,而不必通过在更新查询中指定我的字段来代替req.body
?
你是正确的,猫鼬转换为你更新$set
。但是这并不能解决你的问题。在mongodb shell中尝试一下,你会看到相同的行为。
相反,要更新单个深度嵌套属性,您需要指定$set
中deep属性的完整路径。
User.update({ email: '[email protected]' }, { 'profile.name.first': 'Joseph' }, callback)
没错,但我希望能传递'req.body',所以我不必指定所有可能的字段。所以我最终做了一个'findById()',从找到的文档中删除'__v'和'_id',使用_.deepExtend()'合并到新的更新属性中,然后将我的frankenstein对象传递给Mongoose 'update'。 – k00k 2013-05-08 18:29:09
@aaronheckmann,无论如何告诉猫鼬或mongo“这里是我的最新对象,只是更新它”? – 2013-09-17 23:27:34
@ k00k - 这种方法是原子吗?它不会再写整个文档吗?为什么你必须删除__v和_id? – 2015-02-15 21:54:27
我认为你正在寻找$ SET
http://docs.mongodb.org/manual/reference/operator/set/
User.update({email: "[email protected]"}, { $set : req.body}, function(err, result){});
试一下
也许这是一个很好的解决方案 - 添加选项Model.update,即替换像嵌套对象:
{FIELD1:1,fields2:{A:1,B:2}} => { 'field1':1,'field2.a':1,'field2.b':2}
nestedToDotNotation: function(obj, keyPrefix) {
var result;
if (keyPrefix == null) {
keyPrefix = '';
}
result = {};
_.each(obj, function(value, key) {
var nestedObj, result_key;
result_key = keyPrefix + key;
if (!_.isArray(value) && _.isObject(value)) {
result_key += '.';
nestedObj = module.exports.nestedToDotNotation(value, result_key);
return _.extend(result, nestedObj);
} else {
return result[result_key] = value;
}
});
return result;
}
});
需要改进的循环引用的处理,但这种嵌套对象
我使用下划线工作时是非常有用的。js的这里,但是这些功能可以轻松地与其它类似物来代替
一个非常容易的方法与Moongose 4.1
来解决这个和flat
包:
var flat = require('flat'),
Schema = mongoose.Schema,
schema = new Schema(
{
name: {
first: {
type: String,
trim: true
},
last: {
type: String,
trim: true
}
}
}
);
schema.pre('findOneAndUpdate', function() {
this._update = flat(this._update);
});
mongoose.model('User', schema);
req.body
(举例来说)现在可以:
{
name: {
first: 'updatedFirstName'
}
}
该对象将被展平被执行的实际查询之前,从而$set
将更新仅预期性质而不是整个name
对象。
在嵌套ObjectId时要小心。 flat会将这些变成'something._bsontype':'ObjectID','something.id':
考虑接受Aichholzer的答案,因为它提供了一个真正的解决方案。 – zbr 2015-12-10 19:06:33