【问题标题】:Mongoose populate after save保存后猫鼬填充
【发布时间】:2012-11-11 14:32:36
【问题描述】:

我无法手动或自动在新保存的对象上填充创建者字段...我能找到的唯一方法是重新查询我已经拥有的我不想做的对象。

这是设置:

var userSchema = new mongoose.Schema({   
  name: String,
});
var User = db.model('User', userSchema);

var bookSchema = new mongoose.Schema({
  _creator: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
  description: String,
});
var Book = db.model('Book', bookSchema);

这是我拉头发的地方

var user = new User();
user.save(function(err) {
    var book = new Book({
        _creator: user,
    });
    book.save(function(err){
        console.log(book._creator); // is just an object id
        book._creator = user; // still only attaches the object id due to Mongoose magic
        console.log(book._creator); // Again: is just an object id
        // I really want book._creator to be a user without having to go back to the db ... any suggestions?
    });
});

编辑:最新的猫鼬修复了这个问题并添加了填充功能,请参阅新接受的答案。

【问题讨论】:

    标签: node.js mongodb mongoose populate


    【解决方案1】:

    您应该能够使用模型的填充函数来执行此操作:http://mongoosejs.com/docs/api.html#model_Model.populate 在 book 的保存处理程序中,而不是:

    book._creator = user;
    

    你会做这样的事情:

    Book.populate(book, {path:"_creator"}, function(err, book) { ... });
    

    可能为时已晚,但我最近被困在这个问题上,它可能对其他人有用。

    【讨论】:

    • 如果这与虚拟属性一起使用会很好。喜欢creator.profile
    • 如果用户有一些虚拟属性不包括在内。
    • 这对我有用,尽管我在保存之前尝试将引用设置为 null 时遇到了一些问题。保存后,调用 Book.populate 错误地用以前的参考值填充了该字段,我不知道为什么。数据库成功包含空值。
    • 而这不会重新查询数据库?!
    • 这是重新查询数据库
    【解决方案2】:

    我的解决方案是使用execPopulate,就像这样

    const t = new MyModel(value)
    return t.save().then(t => t.populate('my-path').execPopulate())
    

    【讨论】:

    • 非常感谢@Francois,你救了我的命,我正试图为此寻找解决方案。终于明白了。
    【解决方案3】:

    如果有人还在寻找这个。

    Mongoose 3.6 引入了许多很酷的功能来填充:

    book.populate('_creator', function(err) {
     console.log(book._creator);
    });
    

    或:

    Book.populate(book, '_creator', function(err) {
     console.log(book._creator);
    });
    

    查看更多信息:https://github.com/LearnBoost/mongoose/wiki/3.6-Release-Notes#population

    但是这样你仍然会再次查询用户。

    在没有额外查询的情况下完成它的一个小技巧是:

    book = book.toObject();
    book._creator = user;
    

    【讨论】:

    • save()之后做book._creator = user;是当前所有答案中唯一正确的答案,所有其他答案需要额外查询。
    【解决方案4】:

    返回promise(无回调)的解决方案:

    使用 Document#populate

    book.populate('creator').execPopulate();
    
    // summary
    doc.populate(options);               // not executed
    doc.populate(options).execPopulate() // executed, returns promise
    

    可能的实施

    var populatedDoc = doc.populate(options).execPopulate();
    populatedDoc.then(doc => {
       ... 
    });
    

    阅读文档数量here

    【讨论】:

    • 好东西。谢谢
    • IDK 你知道与否,但这段代码只是一个语法糖。它将向数据库发送一个新查询。所以这不是一个好的答案。
    【解决方案5】:

    只是为了详细说明并举另一个例子,因为它帮助了我。这可能会帮助那些想要在保存后检索部分填充的对象的人。方法也略有不同。花了一两个多小时寻找正确的方法。

      post.save(function(err) {
        if (err) {
          return res.json(500, {
            error: 'Cannot save the post'
          });
        }
        post.populate('group', 'name').populate({
          path: 'wallUser',
          select: 'name picture'
        }, function(err, doc) {
          res.json(doc);
        });
      });
    

    【讨论】:

      【解决方案6】:

      我想我会补充一点,以澄清像我这样的菜鸟。

      如果您不小心的话,最令人困惑的是,存在三种截然不同的填充方法。它们是不同对象的方法(模型与文档),接受不同的输入并给出不同的输出(文档与承诺)。

      这里是为那些感到困惑的人准备的:

      Document.prototype.populate()

      See full docs.

      这适用于文档并返回一个文档。在原始示例中,它看起来像这样:

      book.save(function(err, book) {
          book.populate('_creator', function(err, book) {
              // Do something
          })
      });
      

      因为它适用于文档并返回一个文档,所以您可以像这样将它们链接在一起:

      book.save(function(err, book) {
          book
          .populate('_creator')
          .populate('/* Some other ObjectID field */', function(err, book) {
              // Do something
          })
      });
      

      但别傻了,像我一样,试着这样做:

      book.save(function(err, book) {
          book
          .populate('_creator')
          .populate('/* Some other ObjectID field */')
          .then(function(book) {
              // Do something
          })
      });
      

      记住:Document.prototype.populate() 返回一个文档,所以这是无稽之谈。如果你想要一个承诺,你需要......

      Document.prototype.execPopulate()

      See full docs.

      这个适用于文档,但它返回一个解析为文档的承诺。 换句话说,你可以这样使用它:

      book.save(function(err, book) {
          book
          .populate('_creator')
          .populate('/* Some other ObjectID field */')
          .execPopulate()
          .then(function(book) {
              // Do something
          })
      });
      

      这样更好。最后,还有……

      Model.populate()

      See full docs.

      这个适用于模型并返回一个承诺。因此它的用法有点不同:

      book.save(function(err, book) {
          Book // Book not book
          .populate(book, { path: '_creator'})
          .then(function(book) {
              // Do something
          })
      });
      

      希望这对其他一些新人有所帮助。

      【讨论】:

      • 这个例子看起来很简单,但是 {path: ':_createor'} 的目的到底是什么? _creator 是否与 OP 的示例有关?如果我的文档有几个属性,例如 _Id、Id、title 或 year - 将用于路径?相当于主键?
      【解决方案7】:

      不幸的是,这是 mongoose 长期存在的问题,我认为尚未解决:

      https://github.com/LearnBoost/mongoose/issues/570

      您可以为此编写自己的自定义 getter/setter(并在单独的属性中设置 real _customer)。例如:

      var get_creator = function(val) {
          if (this.hasOwnProperty( "__creator" )) {
              return this.__creator;
          }
          return val;
      };
      var set_creator = function(val) {
          this.__creator = val;
          return val;
      };
      var bookSchema = new mongoose.Schema({
        _creator: {
           type: mongoose.Schema.Types.ObjectId,
           ref: 'User',
           get: get_creator,
           set: set_creator
        },
        description: String,
      });
      

      注意:我没有对其进行测试,它可能会在 .populate 和设置纯 ID 时奇怪地工作。

      【讨论】:

      • 他们似乎不想解决这个问题。
      • 此问题已在 3.6 中修复
      • @Pykler 您确实需要将接受的答案更改为之前评分最高的答案,因为此答案不再有效
      【解决方案8】:

      猫鼬 5.2.7

      这对我有用(只是很头疼!)

      exports.create = (req, res, next) => {
        const author = req.userData;
        const postInfo = new Post({
          author,
          content: req.body.content,
          isDraft: req.body.isDraft,
          status: req.body.status,
          title: req.body.title
        });
        postInfo.populate('author', '_id email role display_name').execPopulate();
        postInfo.save()
          .then(post => {
            res.status(200).json(post);
          }).catch(error => {
            res.status(500).json(error);
          });
      };
      

      【讨论】:

        【解决方案9】:

        将文档保存在模型中,然后填充

           chatRoom = await chatRoom.save();
           const data = await chatRoom
          .populate("customer", "email dp")
          .populate({
            path: "admin",
            select: "name logo",
          })
          .execPopulate();
        

        【讨论】:

          【解决方案10】:

          大概……喜欢

          Book.createAsync(bookToSave).then((savedBook) => savedBook.populateAsync("creator"));
          

          这将是完成这项工作的最好且问题最少的方法(使用 Bluebird 承诺)。

          【讨论】:

            【解决方案11】:

            最终编写了一些可咖喱的 Promise 函数,您可以在其中声明您的架构、query_adapter、data_adapter 函数并提前填充字符串。对于更短/更简单的实现更容易。

            这可能不是超级高效,但我认为执行位相当优雅。

            github文件:curry_Promises.js

            声明

            const update_or_insert_Item = mDB.update_or_insert({
                schema : model.Item,
                fn_query_adapter : ({ no })=>{return { no }},
                fn_update_adapter : SQL_to_MDB.item,
                populate : "headgroup"
                // fn_err : (e)=>{return e},
                // fn_res : (o)=>{return o}
            })
            

            执行

            Promise.all( items.map( update_or_insert_Item ) )
            .catch( console.error )
            .then( console.log )
            

            【讨论】:

              【解决方案12】:

              对我有用

                  let post = await PostModel.create({
                      ...req.body, author: userId
                  })
              
                  post = await post.populate('author', 'name')
              
                  res.status(200).json({
                      status: 'success',
                      data: { post }
                  })
              

              【讨论】:

              • 正如目前所写,您的答案尚不清楚。请edit 添加其他详细信息,以帮助其他人了解这如何解决所提出的问题。你可以找到更多关于如何写好答案的信息in the help center
              • 你好。请记住,这段代码可能看起来很棒,但它正在做的是向数据库发送另一个查询。所以你的代码只是一点点糖和更短。您可以启用调试模式并查看生成的查询
              【解决方案13】:

              我没有在这里添加任何新内容。

              这只是使用 async/await 编写此代码的一种更简洁的方式:

              const newCourse = new Course(new_course_data);
              const populate_options = [
                // Here write your populate options
              ];
              const created_course = await newCourse.save();
              await created_course.populate(populate_options).execPopulate();
              

              【讨论】:

              • node:30332) UnhandledPromiseRejectionWarning: TypeError: .populate(...).execPopulate 不是函数
              猜你喜欢
              • 2016-06-10
              • 2014-09-06
              • 1970-01-01
              • 2018-05-04
              • 2020-05-09
              • 2015-04-01
              • 2015-09-30
              • 2015-07-13
              • 2016-10-10
              相关资源
              最近更新 更多