gpt4 book ai didi

javascript - mongodb/mongoose - 使用 $ 运算符更新会丢失 _id

转载 作者:行者123 更新时间:2023-12-03 11:26:31 24 4
gpt4 key购买 nike

在使用“$”运算符更新我的集合时,我遇到了这个奇怪的问题。假设以下架构:

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'}
});

有人遇到过类似的错误吗?

谢谢。

最佳答案

这不是一个“错误”,而是设计使然,你做错了你真正想做的事情,而且这里发生的事情也不是你想象的那样。

在 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本来是要被使用的。如果您只想设置子文档的“名称”属性,而不设置其他属性,则可以使用 "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"意味着应该创建一个新对象。相反,您“应该”指定您希望更改的“仅字段”的整个路径。

关于javascript - mongodb/mongoose - 使用 $ 运算符更新会丢失 _id,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26898022/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com