Node.js 通过mongoose将项目推入mongo阵列
我花了很多时间寻找答案,但我确信我找不到合适的词来描述我所追求的 基本上我有一个叫做“人”的mongodb集合 该集合的架构如下所示:Node.js 通过mongoose将项目推入mongo阵列,node.js,mongodb,express,mongoose,Node.js,Mongodb,Express,Mongoose,我花了很多时间寻找答案,但我确信我找不到合适的词来描述我所追求的 基本上我有一个叫做“人”的mongodb集合 该集合的架构如下所示: people: { name: String, friends: [{firstName: String, lastName: String}] } 现在,我有了一个非常基本的express应用程序,它可以连接到数据库,并成功地用一个空的friends数组创建“people” 在应用程序的第二个位置,有一个
people: {
name: String,
friends: [{firstName: String, lastName: String}]
}
现在,我有了一个非常基本的express应用程序,它可以连接到数据库,并成功地用一个空的friends数组创建“people”
在应用程序的第二个位置,有一个用于添加朋友的表单。表单接受firstName和lastName,然后使用name字段发布,也用于引用适当的人员对象
我很难做到的是创建一个新的friend对象,然后将其“推”到friends数组中
我知道,当我通过mongo控制台执行此操作时,我使用带有$push的更新函数作为查找条件之后的第二个参数,但我似乎找不到让mongoose执行此操作的适当方法
db.people.update({name: "John"}, {$push: {friends: {firstName: "Harry", lastName: "Potter"}}});
假设,
var-friend={firstName:'Harry',lastName:'Potter'}代码>
您有两种选择:
在内存中更新模型并保存(普通javascript数组.push):
或
我总是尽可能地选择第一个选项,因为它会尊重mongoose给您带来的更多好处(挂钩、验证等)
但是,如果您正在进行大量并发写入,您将遇到竞争条件,最终会出现严重的版本错误,从而阻止您每次替换整个模型并丢失以前添加的好友。所以只有在绝对必要的时候才去前者
运算符将指定的值附加到数组
我也遇到了这个问题。我的解决方案是创建一个子模式。请参见下面的模型示例
----人物模型
const mongoose = require('mongoose');
const SingleFriend = require('./SingleFriend');
const Schema = mongoose.Schema;
const productSchema = new Schema({
friends : [SingleFriend.schema]
});
module.exports = mongoose.model('Person', personSchema);
***重要提示:SingleFriend.schema->确保使用小写字母表示模式
---子模式
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const SingleFriendSchema = new Schema({
Name: String
});
module.exports = mongoose.model('SingleFriend', SingleFriendSchema);
使用$push
更新文档并在数组中插入新值
查找:
db.getCollection('noti').find({})
{
"_id" : ObjectId("5bc061f05a4c0511a9252e88"),
"count" : 1.0,
"color" : "green",
"icon" : "circle",
"graph" : [
{
"date" : ISODate("2018-10-24T08:55:13.331Z"),
"count" : 2.0
}
],
"name" : "online visitor",
"read" : false,
"date" : ISODate("2018-10-12T08:57:20.853Z"),
"__v" : 0.0
}
db.getCollection('noti').findOneAndUpdate(
{ _id: ObjectId("5bc061f05a4c0511a9252e88") },
{ $push: {
graph: {
"date" : ISODate("2018-10-24T08:55:13.331Z"),
"count" : 3.0
}
}
})
{
"_id" : ObjectId("5bc061f05a4c0511a9252e88"),
"count" : 1.0,
"color" : "green",
"icon" : "circle",
"graph" : [
{
"date" : ISODate("2018-10-24T08:55:13.331Z"),
"count" : 2.0
},
{
"date" : ISODate("2018-10-24T08:55:13.331Z"),
"count" : 3.0
}
],
"name" : "online visitor",
"read" : false,
"date" : ISODate("2018-10-12T08:57:20.853Z"),
"__v" : 0.0
}
查找结果:
db.getCollection('noti').find({})
{
"_id" : ObjectId("5bc061f05a4c0511a9252e88"),
"count" : 1.0,
"color" : "green",
"icon" : "circle",
"graph" : [
{
"date" : ISODate("2018-10-24T08:55:13.331Z"),
"count" : 2.0
}
],
"name" : "online visitor",
"read" : false,
"date" : ISODate("2018-10-12T08:57:20.853Z"),
"__v" : 0.0
}
db.getCollection('noti').findOneAndUpdate(
{ _id: ObjectId("5bc061f05a4c0511a9252e88") },
{ $push: {
graph: {
"date" : ISODate("2018-10-24T08:55:13.331Z"),
"count" : 3.0
}
}
})
{
"_id" : ObjectId("5bc061f05a4c0511a9252e88"),
"count" : 1.0,
"color" : "green",
"icon" : "circle",
"graph" : [
{
"date" : ISODate("2018-10-24T08:55:13.331Z"),
"count" : 2.0
},
{
"date" : ISODate("2018-10-24T08:55:13.331Z"),
"count" : 3.0
}
],
"name" : "online visitor",
"read" : false,
"date" : ISODate("2018-10-12T08:57:20.853Z"),
"__v" : 0.0
}
更新:
db.getCollection('noti').find({})
{
"_id" : ObjectId("5bc061f05a4c0511a9252e88"),
"count" : 1.0,
"color" : "green",
"icon" : "circle",
"graph" : [
{
"date" : ISODate("2018-10-24T08:55:13.331Z"),
"count" : 2.0
}
],
"name" : "online visitor",
"read" : false,
"date" : ISODate("2018-10-12T08:57:20.853Z"),
"__v" : 0.0
}
db.getCollection('noti').findOneAndUpdate(
{ _id: ObjectId("5bc061f05a4c0511a9252e88") },
{ $push: {
graph: {
"date" : ISODate("2018-10-24T08:55:13.331Z"),
"count" : 3.0
}
}
})
{
"_id" : ObjectId("5bc061f05a4c0511a9252e88"),
"count" : 1.0,
"color" : "green",
"icon" : "circle",
"graph" : [
{
"date" : ISODate("2018-10-24T08:55:13.331Z"),
"count" : 2.0
},
{
"date" : ISODate("2018-10-24T08:55:13.331Z"),
"count" : 3.0
}
],
"name" : "online visitor",
"read" : false,
"date" : ISODate("2018-10-12T08:57:20.853Z"),
"__v" : 0.0
}
更新结果:
db.getCollection('noti').find({})
{
"_id" : ObjectId("5bc061f05a4c0511a9252e88"),
"count" : 1.0,
"color" : "green",
"icon" : "circle",
"graph" : [
{
"date" : ISODate("2018-10-24T08:55:13.331Z"),
"count" : 2.0
}
],
"name" : "online visitor",
"read" : false,
"date" : ISODate("2018-10-12T08:57:20.853Z"),
"__v" : 0.0
}
db.getCollection('noti').findOneAndUpdate(
{ _id: ObjectId("5bc061f05a4c0511a9252e88") },
{ $push: {
graph: {
"date" : ISODate("2018-10-24T08:55:13.331Z"),
"count" : 3.0
}
}
})
{
"_id" : ObjectId("5bc061f05a4c0511a9252e88"),
"count" : 1.0,
"color" : "green",
"icon" : "circle",
"graph" : [
{
"date" : ISODate("2018-10-24T08:55:13.331Z"),
"count" : 2.0
},
{
"date" : ISODate("2018-10-24T08:55:13.331Z"),
"count" : 3.0
}
],
"name" : "online visitor",
"read" : false,
"date" : ISODate("2018-10-12T08:57:20.853Z"),
"__v" : 0.0
}
一种简单的方法是使用以下各项:
var John = people.findOne({name: "John"});
John.friends.push({firstName: "Harry", lastName: "Potter"});
John.save();
就我而言,我是这样做的
const eventId = event.id;
User.findByIdAndUpdate(id, { $push: { createdEvents: eventId } }).exec();
首先我尝试了这个代码
const peopleSchema = new mongoose.Schema({
name: String,
friends: [
{
firstName: String,
lastName: String,
},
],
});
const People = mongoose.model("person", peopleSchema);
const first = new Note({
name: "Yash Salvi",
notes: [
{
firstName: "Johnny",
lastName: "Johnson",
},
],
});
first.save();
const friendNew = {
firstName: "Alice",
lastName: "Parker",
};
People.findOneAndUpdate(
{ name: "Yash Salvi" },
{ $push: { friends: friendNew } },
function (error, success) {
if (error) {
console.log(error);
} else {
console.log(success);
}
}
);
但我注意到只有第一个朋友(即johny Johnson)被保存,并且在现有的“朋友”数组中推送数组元素的目标似乎不像我运行代码时那样有效,在中的数据库中只显示“第一个朋友”和“朋友”数组只有一个元素!
下面是简单的解决方案
const peopleSchema = new mongoose.Schema({
name: String,
friends: [
{
firstName: String,
lastName: String,
},
],
});
const People = mongoose.model("person", peopleSchema);
const first = new Note({
name: "Yash Salvi",
notes: [
{
firstName: "Johnny",
lastName: "Johnson",
},
],
});
first.save();
const friendNew = {
firstName: "Alice",
lastName: "Parker",
};
People.findOneAndUpdate(
{ name: "Yash Salvi" },
{ $push: { friends: friendNew } },
{ upsert: true }
);
添加“{upsert:true}”解决了我的问题,代码保存并运行后,我看到“friends”数组现在有2个元素!
如果对象不存在,upsert=true选项将创建该对象。默认设置为false
如果不起作用,请使用下面的代码段
People.findOneAndUpdate(
{ name: "Yash Salvi" },
{ $push: { friends: friendNew } },
).exec();
使用Mongoose将项目推入数组的另一种方法是-$addToSet,前提是您只希望将唯一的项目推入数组$push操作符仅将对象添加到数组中,而$addToSet仅在对象不存在于数组中时才将其添加到数组中,以免合并重复性
PersonModel.update(
{ _id: person._id },
{ $addToSet: { friends: friend } }
);
这将查找要添加到数组中的对象。如果被发现,什么也不做。如果不是,则将其添加到数组中
参考资料:
我发现我可能会使用collections.findOneAndUpdate(),但我不确定在哪里实现它?在我的模型中?我认为第二个选项在并发写保护方面实际上更安全?当你想说前者时,你是否无意中说了后者?是的,我刚刚检查了(2017年11月),使用mongooseupdate
函数是安全的,而查找文档不安全,请在内存中修改它,然后调用文档上的.save()
方法。当我说一个操作是“安全的”时,这意味着即使对一个文档应用了一个、两个或50个更改,它们都将成功应用,并且更新的行为将如您所期望的那样。实际上,内存中的操作是不安全的,因为您可能正在处理过时的文档,因此在保存时,在获取步骤后发生的更改将丢失。@Will Brickner的一个常见工作流是将您的逻辑包装在重试循环中:(retryable包含获取、修改、保存)。如果您返回VersionError(乐观锁异常),您可以重试该操作几次,每次都获取一个新副本。不过,如果可能的话,我还是更喜欢原子更新@ZackOfAllTrades也把我弄糊涂了,但我相信回调函数已经完成了let done=function(err,result){//这是在mongoose操作之后运行的}
在这个场景中你不需要使用markUpdated
吗?这是我发现有效的一个,所以我认为这是2019年最新的一个。我认为最好的答案是在某些方面有所欠缺,可能不完整/过时。请允许我指出,代码中可能有一个小错误。我让它工作是因为我为我的项目输入了正确的变量。你有朋友的地方。我想应该是人。findOneAndUpdate()。这将更符合最初的问题。我可以为您编辑它,但我希望您只是仔细检查一下,以防我错了(我对node/express有点陌生)。@cheesutostoast我想,如果我错了,您可以更改答案。我提出了这个对我来说很有效的答案。但是如果你发现这个答案错了,你可以修改这个答案&如果你能纠正我,我会很高兴的,先生:-)。好的,没问题,反正这只是一个小的编辑。我有点吹毛求疵,因为观众(像我一样)无论如何都会知道阵列被推到了哪里。我仍然认为这应该是最好的答案:)我只是想让其他人知道,这是完美的工作,它的2020年,仍然工作