我在使用' $'更新我的收藏时看到了这个奇怪的问题。运营商。假设以下架构:
var mySchema = {
myArray : [{
name : String
}]
};
以及以下数据(推送到myArray时会自动引入_id属性)
[{name : 'foo', _id:'1'}, {name: 'bar', _id: '2'}]
以下按预期工作,它替换文档而不会弄乱_id字段:
var newFoo = {name : 'newFoo'};
mySchema.findOneAndUpdate({name : 'foo'}, {'$set' : {'myArray.0': newFoo }}},function(error, doc) {
//the resulting document looks like
//{name : 'newFoo', _id : '1'}
});
但是使用$运算符而不是数组索引会导致丢失_id字段:
mySchema.findOneAndUpdate({name : 'foo'}, {'$set' : {'myArray.$': newFoo }}},function(error, doc) {
//the resulting document looks like
//{name : 'newFoo'}
});
有没有人遇到过类似的错误?
谢谢。
答案 0 :(得分:2)
这不是设计上的“错误”,你做错了你真正想要做的事情,以及这里发生的事情并不是你想的。
在MongoDB的“更新”中,语句“更新”部分中的任何参数都被认为是“字面上”意味着它们是什么。存在$set
运算符,以便您可以指定“要更新的字段”或多个字段,并且只更新提到的字段而不是覆盖整个文档。
即使你在这里指定数组字段作为参数,实际上发生的事情是“整个”数组元素正在被“替换”,而不仅仅是“名称”字段,这可能是你的意图。
此列表演示了正在发生的事情:
var async = require('async'),
mongoose = require('mongoose'),
Schema = mongoose.Schema;
var testSchema = new Schema({
"list" : [{ "name": String }]
});
var Test = mongoose.model( 'Test', testSchema, "test" );
mongoose.connect('mongodb://localhost/test');
async.series([
function(callback) {
Test.remove({},callback);
},
function(callback) {
var test = new Test({ "list": [{ "name": "foo"},{ "name": "bar" }] });
test.save(function(err,doc) {
console.log(doc);
callback();
});
},
function(callback) {
var newFoo = { "name": "newFoo" };
Test.findOneAndUpdate(
{ "list.name": "foo" },
{
"$set": { "list.0": newFoo }
},
function(err,doc) {
console.log(doc);
callback();
}
)
},
function(callback) {
var newFoo = { "name": "moreFoo" };
Test.findOneAndUpdate(
{ "list.name": "newFoo" },
{
"$set": { "list.$": newFoo }
},
function(err,doc) {
console.log(doc);
callback();
}
);
}
]);
基本上你在做什么。现在仔细看看输出:
_id: 546419d60158d92a6f0167d2,
list:
[ { name: 'foo', _id: 546419d60158d92a6f0167d4 },
{ name: 'bar', _id: 546419d60158d92a6f0167d3 } ] }
{ _id: 546419d60158d92a6f0167d2,
__v: 0,
list:
[ { name: 'newFoo', _id: 546419d60158d92a6f0167d5 },
{ name: 'bar', _id: 546419d60158d92a6f0167d3 } ] }
{ _id: 546419d60158d92a6f0167d2,
__v: 0,
list:
[ { name: 'moreFoo' },
{ name: 'bar', _id: 546419d60158d92a6f0167d3 } ] }
以下是三个更新的结果,正如您所说,_id
在第三个语句之后确实缺失了。但请查看第二个语句中的_id
值。它与原版“不同”,因此新的“子文档”已经“替换”了元素,而不是更新“名称”。
这里肯定有一点“有趣”,但它与“mongoose”翻译你的语句而不是MongoDB的关系更为相关。 _id
值由mongoose而不是MongoDB创建。 MongoDB仅为“顶级”文档创建_id
值。 Mongoose为数组做了这个,因为它默认认为它是一件好事。
所以在某些时候,mongoose“确定”你的“更新”实际上将“替换”指定索引处的元素,并根据作为参数的模式规则创建一个新对象。
但如前所述,这是不如何使用$set
。如果您只想设置“name”属性,而不想设置子文档的其他属性,则使用"dot notation"指定该属性的“完整路径”。
以下是修改后的列表,仅修改“名称”属性,而不是其他内容:
var async = require('async'),
mongoose = require('mongoose'),
Schema = mongoose.Schema;
var testSchema = new Schema({
"list" : [{ "name": String }]
});
var Test = mongoose.model( 'Test', testSchema, "test" );
mongoose.connect('mongodb://localhost/test');
async.series([
function(callback) {
Test.remove({},callback);
},
function(callback) {
var test = new Test({ "list": [{ "name": "foo"},{ "name": "bar" }] });
test.save(function(err,doc) {
console.log(doc);
callback();
});
},
function(callback) {
var newFoo = { "name": "newFoo" };
Test.findOneAndUpdate(
{ "list.name": "foo" },
{
"$set": { "list.0.name": newFoo.name }
},
function(err,doc) {
console.log(doc);
callback();
}
)
},
function(callback) {
var newFoo = { "name": "moreFoo" };
Test.findOneAndUpdate(
{ "list.name": "newFoo" },
{
"$set": { "list.$.name": newFoo.name }
},
function(err,doc) {
console.log(doc);
callback();
}
);
}
]);
分别在"list.0.name"
和"list.$.name"
中查看两个符号。现在看看有什么区别:
{ __v: 0,
_id: 54641bcfdd89cf2c7299025e,
list:
[ { name: 'foo', _id: 54641bcfdd89cf2c72990260 },
{ name: 'bar', _id: 54641bcfdd89cf2c7299025f } ] }
{ _id: 54641bcfdd89cf2c7299025e,
__v: 0,
list:
[ { name: 'newFoo', _id: 54641bcfdd89cf2c72990260 },
{ name: 'bar', _id: 54641bcfdd89cf2c7299025f } ] }
{ _id: 54641bcfdd89cf2c7299025e,
__v: 0,
list:
[ { name: 'moreFoo', _id: 54641bcfdd89cf2c72990260 },
{ name: 'bar', _id: 54641bcfdd89cf2c7299025f } ] }
所以现在你看到_id
在每种情况下都保持不变,这是因为“我们告诉更新不要管它”。
这基本上就是你想要的。当使用positional $
运算符匹配找到的索引位置时,mongoose“不应该”假设在没有"dot notation"的情况下缺少右侧的任何参数意味着应该创建一个新对象。相反,你“应该”指定你想要改变的“只是一个或多个领域”的整个路径。