Node.js 通过mongoose将项目推入mongo阵列

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” 在应用程序的第二个位置,有一个

我花了很多时间寻找答案,但我确信我找不到合适的词来描述我所追求的

基本上我有一个叫做“人”的mongodb集合 该集合的架构如下所示:

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月),使用mongoose
update
函数是安全的,而查找文档不安全,请在内存中修改它,然后调用文档上的
.save()
方法。当我说一个操作是“安全的”时,这意味着即使对一个文档应用了一个、两个或50个更改,它们都将成功应用,并且更新的行为将如您所期望的那样。实际上,内存中的操作是不安全的,因为您可能正在处理过时的文档,因此在保存时,在获取步骤后发生的更改将丢失。@Will Brickner的一个常见工作流是将您的逻辑包装在重试循环中:(retryable包含获取、修改、保存)。如果您返回VersionError(乐观锁异常),您可以重试该操作几次,每次都获取一个新副本。不过,如果可能的话,我还是更喜欢原子更新@ZackOfAllTrades也把我弄糊涂了,但我相信回调函数已经完成了
let done=function(err,result){//这是在mongoose操作之后运行的}
在这个场景中你不需要使用
markUpdated
吗?这是我发现有效的一个,所以我认为这是2019年最新的一个。我认为最好的答案是在某些方面有所欠缺,可能不完整/过时。请允许我指出,代码中可能有一个小错误。我让它工作是因为我为我的项目输入了正确的变量。你有朋友的地方。我想应该是人。findOneAndUpdate()。这将更符合最初的问题。我可以为您编辑它,但我希望您只是仔细检查一下,以防我错了(我对node/express有点陌生)。@cheesutostoast我想,如果我错了,您可以更改答案。我提出了这个对我来说很有效的答案。但是如果你发现这个答案错了,你可以修改这个答案&如果你能纠正我,我会很高兴的,先生:-)。好的,没问题,反正这只是一个小的编辑。我有点吹毛求疵,因为观众(像我一样)无论如何都会知道阵列被推到了哪里。我仍然认为这应该是最好的答案:)我只是想让其他人知道,这是完美的工作,它的2020年,仍然工作