【问题标题】:Push items into mongo array via mongoose通过 mongoose 将项目推送到 mongo 数组中
【发布时间】:2020-04-02 16:03:26
【问题描述】:

基本上我有一个名为“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(),但我不确定我会在哪里实现它?在我的模型中?

标签: node.js mongodb express mongoose


【解决方案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】:

使用$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 } }
    );
    

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

    参考资料:

    【讨论】:

      【解决方案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 解决方案,与其他答案的干净、原生解决方案相反
            猜你喜欢
            • 2022-01-07
            • 2020-12-30
            • 2019-01-06
            相关资源
            最近更新 更多