通过 mongoose 将项目推送到 mongo 数组中

Posted

技术标签:

【中文标题】通过 mongoose 将项目推送到 mongo 数组中【英文标题】:Push items into mongo array via mongoose 【发布时间】:2018-10-17 06:11:15 【问题描述】:

基本上我有一个名为“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 安全可以使用 mongoose update 函数,而 安全找到一个文档,在内存中修改它,然后在文档上调用.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】:

我也遇到了这个问题。我的解决方法是创建一个子架构。请参阅下面的模型示例。

---- 人物模型

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 解决方案,与其他答案的干净、原生解决方案相反【参考方案4】:

使用$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

【讨论】:

【参考方案5】:

一种简单的方法是使用以下方法:

var John = people.findOne(name: "John");
John.friends.push(firstName: "Harry", lastName: "Potter");
John.save();

【讨论】:

容易出现竞争条件(如果您并行实例化多个 John,您将丢失其中一个推送的值)。【参考方案6】:

就我而言,我这样做了

  const eventId = event.id;
  User.findByIdAndUpdate(id,  $push:  createdEvents: eventId  ).exec();

【讨论】:

【参考方案7】:

首先我尝试了这段代码

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【参考方案8】:

使用 Mongoose 将项目推入数组的另一种方法是 - $addToSet,如果您只想将唯一项目推入数组。 $push 运算符只是将对象添加到数组中,无论对象是否已经存在,而 $addToSet 仅当对象不存在于数组中时才会这样做合并重复。

PersonModel.update(
   _id: person._id , 
   $addToSet:  friends: friend  
);

这将查找您要添加到数组的对象。如果找到,则不执行任何操作。如果没有,则将其添加到数组中。

参考资料:

$addToSet MongooseArray.prototype.addToSet()

【讨论】:

【参考方案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】:

$push 运算符将指定的值附加到数组。

$push 操作符的形式为:

 $push:  <field1>: <value1>, ...  

例子

People.update(
    _id: 1 ,
    $push:  scores: 89  
)

【讨论】:

以上是关于通过 mongoose 将项目推送到 mongo 数组中的主要内容,如果未能解决你的问题,请参考以下文章

通过 mongoose 将项目推送到 mongo 数组中

通过 mongoose 将项目推送到 mongo 数组中

通过 mongoose 将项目推送到 mongo 数组中

通过 mongoose 将项目推送到 mongo 数组中

通过 mongoose 将项目推送到 mongo 数组中

通过 mongoose 将项目推送到 mongo 数组中