Mongoose: unable to populate relationship after saving a record without having to load it again

Created on 6 Sep 2014  路  7Comments  路  Source: Automattic/mongoose

Whether I'm creating or updating a record of type article, I have to use .save(). Before I hit save, I have the entire user in req.user

{ provider: 'local',
  email: '[email protected]',
  hashed_password: '49de749649b49c9bdf31073a8975bbc128b09d81',
  salt: '298408390498',
  username: 'test1',
  name: 'test1',
  _id: 540a2e06c09f8f4e922dd773,
  __v: 0 }

Then I do:

var article = new Article(req.body.article);
    article.user = req.user;

Mongoose only keeps the id part and when I try to get article.user out it just gives me the string 540a2e06c09f8f4e922dd773, the rest is scrapped. Using this id when I save, mongoose accurately saves the reference in mongo

 article.save(function(err, article) {
                res.jsonp({
                    article: article
                });
    });

But it returns the user as an id in the callback, not as an embedded record. Even if I try after the callback to attach the whole user object so I can return it to the client from the save callback, everything but id is scrapped.
If I want to make it embed, I normally call populate, but apparently I don't have that method after saving, only when I'm doing finds. Which means even though I already have the new record which I just saved or updated, I have to go _back_ to the database with a find so I can do a populate. (load is a convenience method I have for find then populate)

article.save(function(err) {

        Article.load(article._id, function(err, result) {

            if (err) {
                return res.send('users/signup', {
                    errors: err.errors,
                    article: result
                });
            } else {
                res.jsonp({
                    article: result
                });
            }
        });
    });

This is redundant and inefficient, it more than doubles the time needed to perform this operation and I was looking for a proper way to do this, but even the guides here for updating refs tell me to do a redundant find and populate...

http://mongoosejs.com/docs/populate.html

Most helpful comment

https://github.com/Automattic/mongoose/wiki/3.6-Release-Notes#syntax-support-for-population-of-multiple-fields

I guess you will find this interesting.
Model.populate(model ,opts,function(err,populdatedDoc){ })
@nasr18

All 7 comments

Why not just set article.user to the user id and then save article and user in parallel? Right now, since user is an ObjectId, mongoose casts the user object to an ObjectId

new Trending({
        location: req.body.location._id,
        tag: req.body.tag._id,
        addedBy: req.user._id,
        addedOn: Date.now()
    }).save(function(err, data) {
        if(err) console.log('add trending err:', err);
        else {
            Trending.findById(data._id).populate('location tag').exec(function(err, data) {
                if(err) console.log('add find trending err:', err);
                else res.status(200).send(data);
            });
        }
    });

Is there anyway to simplify this query? i mean save and then populate, instead of save(query) and then populate(query). @vkarpov15

@nasr18 not that I know of

its ok. why dont u make it??

Not really possible, closest thing is findOneAndUpdate but that has a few limitations. Plus I'm not sure a saveThenFind function would really provide much value

https://github.com/Automattic/mongoose/wiki/3.6-Release-Notes#syntax-support-for-population-of-multiple-fields

I guess you will find this interesting.
Model.populate(model ,opts,function(err,populdatedDoc){ })
@nasr18

ended up writing some curry-able Promise functions where you declare your schema, query_adapter, data_adapter functions and populate string in advance. For a shorter / simpler implementation easier on.
github file: curry_Promises.js
declartion
javascript 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} })
execution
````javascript
Promise.all( items.map( update_or_insert_Item ) )
.catch( console.error )
.then( console.log )
`````

Was this page helpful?
0 / 5 - 0 ratings