保存后填充猫鼬


96

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

这是设置:

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?
    });
});

编辑:最新的猫鼬解决了此问题并添加了填充功能,请参见新的接受的答案。

Answers:


136

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

book._creator = user;

你会做类似的事情:

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

可能对您有所帮助的答案为时已晚,但我最近对此停留不动,可能对其他人有用。


如果这可以与虚拟属性一起使用,那就太好了。喜欢creator.profile
chovy

如果用户具有某些虚拟属性,则不包括在内。
chovy

尽管我在保存之前尝试将引用设置为null时遇到了一些问题,但是这对我有用。保存后,调用Book.populate会错误地使用先前的引用值填充该字段,我无法弄清原因。数据库成功包含空值。
Daniel Flippance

2
而且这会不会重新查询数据库?
Nepoxx

9
这是重新查询数据库
bubakazouba '16

36

万一仍然有人在寻找这个。

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;

这样做book._creator = user;之后,save()是当前所有的答案中唯一正确的答案,所有其他的答案需要一个额外的查询。
Mr5o1

23

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

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

2
非常感谢@Francois,您挽救了我的生命,我正试图为此寻找解决方案。最后明白了。
Rupesh

18

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

使用文档#填充

book.populate('creator').execPopulate();

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

可能的实施

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

在此处阅读有关文档填充的信息


好东西。谢谢
Joel H

11

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

  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);
    });
  });

9

我以为我会补充这一点,以澄清像我这样的完整菜鸟的情况。

如果您不小心,将会造成极大的混乱,那就是存在三种非常不同的填充方法。它们是不同对象(模型与文档)的方法,采用不同的输入并给出不同的输出(文档与承诺)。

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

Document.prototype.populate()

查看完整的文档。

该文档处理文档并返回文档。在原始示例中,它看起来像这样:

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()

查看完整的文档。

这对文档起作用,但是它返回一个解析为文档的承诺。换句话说,您可以像这样使用它:

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

这样更好 最后,有...

Model.populate()

查看完整的文档。

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

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

希望对其他新来者有所帮助。


1

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

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时,它可能工作异常。


看来他们并不想解决此问题。
皮克勒2012年

1
3.6
mkoryak 2013年

@Pykler,您确实需要将接受的答案更改为评分最高的答案,因为该答案不再有效
Matt Fletcher 2015年

1

猫鼬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);
    });
};

0

大概…… 喜欢

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

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


0

最后编写了一些可咖喱的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 )
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.