通过 mongoose 将项目推送到 mongo 数组中
Posted
技术标签:
【中文标题】通过 mongoose 将项目推送到 mongo 数组中【英文标题】:Push items into mongo array via mongoose 【发布时间】:2018-10-10 12:09:58 【问题描述】:基本上我有一个名为“people”的 mongodb 集合 其架构如下:
people:
name: String,
friends: [firstName: String, lastName: String]
现在,我有一个非常基本的 express 应用程序,它连接到数据库并成功地创建了带有空朋友数组的“人”。
在应用程序的次要位置,有一个表单用于添加朋友。该表单接受 firstName 和 lastName,然后使用 name 字段进行 POST,以引用适当的人员对象。
我很难做的是创建一个新朋友对象,然后将其“推送”到朋友数组中。
我知道,当我通过 mongo 控制台执行此操作时,我使用更新函数和 $push
作为查找条件后的第二个参数,但我似乎无法找到让 mongoose 执行此操作的适当方法。
db.people.update(name: "John", $push: friends: firstName: "Harry", lastName: "Potter");
【问题讨论】:
我看到我可能会使用 collections.findOneAndUpdate(),但我不确定我会在哪里实现它?在我的模型中? 【参考方案1】:假设,var friend = firstName: 'Harry', lastName: 'Potter' ;
您有两种选择:
更新内存中的模型,并保存(纯javascript array.push):
person.friends.push(friend);
person.save(done);
或
PersonModel.update(
_id: person._id ,
$push: friends: friend ,
done
);
我总是尽可能地尝试第一个选项,因为它会尊重 mongoose 为您提供的更多好处(钩子、验证等)。
但是,如果您正在执行大量并发写入,您将遇到竞争条件,最终会出现严重的版本错误,从而阻止您每次替换整个模型并失去您之前添加的朋友。所以只有在绝对必要时才使用前者。
【讨论】:
我认为就并发写保护而言,第二种选择实际上更安全?当你想说前者的时候,你不小心说后者了吗? 是的,我刚刚检查并(在 2017 年 11 月),IS 安全可以使用 mongooseupdate
函数,而 它不安全找到一个文档,在内存中修改它,然后在文档上调用.save()
方法。当我说一个操作是“安全的”时,这意味着即使一个、两个或 50 个更改被应用到一个文档,它们都将被成功应用,并且更新的行为将如您所愿。本质上,内存中的操作是不安全的,因为您可能正在处理一个过时的文档,因此当您保存时,在获取步骤之后发生的更改会丢失。
@Will Brickner 一个常见的工作流程是将您的逻辑包装在一个重试循环中:(可重试包含获取、修改、保存)。如果您返回 VersionError(乐观锁异常),您可以重试该操作几次并每次获取一个新副本。不过,我仍然更喜欢原子更新!
@ZackOfAllTrades 也让我感到困惑,但我相信完成的是回调函数。 let done = function(err, result) // this runs after the mongoose operation
在这种情况下您不必使用markUpdated
吗?【参考方案2】:
$push 运算符将指定的值附加到数组。
$push: <field1>: <value1>, ...
$push 以值作为元素添加数组字段。
以上答案满足所有要求,但我通过执行以下操作使其正常工作
var objFriends = fname:"fname",lname:"lname",surname:"surname" ;
Friend.findOneAndUpdate(
_id: req.body.id ,
$push: friends: objFriends ,
function (error, success)
if (error)
console.log(error);
else
console.log(success);
);
)
【讨论】:
这是我发现工作的一个,所以我认为这是截至 2019 年最新的一个。我认为最好的答案在某些方面缺乏并且可能不完整/过时了。 我可以指出您的代码中可能有一个小错误。我让它工作,因为我为我的项目输入了正确的变量。你有 Friend.findOneAndUpdate() 我认为它应该是 People.findOneAndUpdate()。这将更符合原始问题。我可以为你编辑它,但我宁愿你仔细检查一下,以防我错了(我对 node/express 有点陌生)。 @CheesusToast 我想,如果我错了,您可以更改答案。我已经提出了这个对我来说很好的答案。但是如果您发现这个答案有误,您可以更改此答案,如果您纠正我先生,我会很高兴:-)。 好的,没问题,这只是一个小编辑。我有点挑剔,因为观众(像我一样)无论如何都会弄清楚阵列被推到哪里。我仍然认为这应该是最好的答案:) 只是想让其他人知道这非常有效,现在是 2020 年,仍然非常有效。我只是使用 findById 而不是 findByOne,无论如何感谢兄弟的帮助。【参考方案3】:使用$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
【讨论】:
【参考方案4】:使用 Mongoose 将项目推入数组的另一种方法是 - $addToSet,如果您只想将唯一项目推入数组。 $push 运算符只是将对象添加到数组中,无论对象是否已经存在,而 $addToSet 仅当对象不存在于数组中时才会这样做合并重复。
PersonModel.update(
_id: person._id ,
$addToSet: friends: friend
);
这将查找您要添加到数组的对象。如果找到,什么也不做。如果没有,则将其添加到数组中。
参考资料:
$addToSet MongooseArray.prototype.addToSet()【讨论】:
【参考方案5】:首先我尝试了这段代码
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);
);
但我注意到只有第一个朋友(即 Johhny 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 ”解决了我的问题,一旦保存代码并运行它,我看到“朋友”数组现在有 2 个元素! upsert = true 选项会在对象不存在时创建它。默认设置为 false。
如果它不起作用,请使用以下 sn-p
People.findOneAndUpdate(
name: "Yash Salvi" ,
$push: friends: friendNew ,
).exec();
【讨论】:
谢谢 Raxy! .exec() 一切正常 很高兴,我可以帮忙:D【参考方案6】:一种简单的方法是使用以下方法:
var John = people.findOne(name: "John");
John.friends.push(firstName: "Harry", lastName: "Potter");
John.save();
【讨论】:
容易出现竞争条件(如果您并行实例化多个 John,您将丢失其中一个推送的值)。【参考方案7】:就我而言,我这样做了
const eventId = event.id;
User.findByIdAndUpdate(id, $push: createdEvents: eventId ).exec();
【讨论】:
【参考方案8】:$push 运算符将指定的值附加到数组。
$push 操作符的形式为:
$push: <field1>: <value1>, ...
例子
People.update(
_id: 1 ,
$push: scores: 89
)
【讨论】:
【参考方案9】:这就是你可以推送项目的方式 - official docs
const schema = Schema( nums: [Number] );
const Model = mongoose.model('Test', schema);
const doc = await Model.create( nums: [3, 4] );
doc.nums.push(5); // Add 5 to the end of the array
await doc.save();
// You can also pass an object with `$each` as the
// first parameter to use MongoDB's `$position`
doc.nums.push(
$each: [1, 2],
$position: 0
);
doc.nums;
【讨论】:
【参考方案10】:我也遇到了这个问题。我的解决方法是创建一个子架构。请参阅下面的模型示例。
---- 人物模型
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);
【讨论】:
投反对票的原因可能是因为这似乎没有必要。 Parth Raval 的回答已经足够了。 投反对票是因为这是一个糟糕的 hacky 解决方案,与其他答案的干净、原生解决方案相反以上是关于通过 mongoose 将项目推送到 mongo 数组中的主要内容,如果未能解决你的问题,请参考以下文章