TBE
TBE

Reputation: 1133

Mongoose.js - different population than usual

I have the following simplified document from the collection "main":

{
    _id: 120,
    name: "Main1",
    prop1: "val",
    menuId: 5
}

Here is a simplified document from my "menu" collection:

{
    _id: 5,
    menuItems: [an array of elements here],
    menuProperties: {an object here}
}

Is it possible to populate with mongoose.js the "menu" document into the "main" document in such way that will result the following model object:

{
    _id: 120,
    name: "Main1",
    prop1: "val",
    menuItems: [an array of elements here],
    menuProperties: {an object here}
}

What i can achieve now is:

{
    _id: 120,
    name: "Main1",
    prop1: "val",
    menuId: {
               menuItems: [an array of elements here],
               menuProperties: {an object here}
            }
}

I haven't added the schemes as they are very simple and will contain ref for the menuId. Thanks!

Upvotes: 1

Views: 84

Answers (2)

zangw
zangw

Reputation: 48376

One way is to remove the menuId from population result, and add menuItem and menuProperties to the population result. Here are the sample codes,

Main.find()
    .populate('menuId', 'menuItem menuProperties -_id', 'Menu')
    .exec(function(err, docs){
        if (err)
            console.log(err);
        else {
            var rets = [];
            docs.forEach(function(doc) {
                // converts the mongoose document into a plain javascript object
                doc = doc.toJSON();
                // add menuItem key
                if (doc.menuId && doc.menuId.menuItem) {
                    doc.menuItem = doc.menuId.menuItem;
                }
                // add menuProperties key
                if (doc.menuId && doc.menuId.menuProperties) {
                    doc.menuProperties = doc.menuId.menuProperties;

                    // remove the menuId object
                    delete doc.menuId
                }
                rets.push(doc);
            });

            console.log(rets);
        }
    });

Test with schema

var MainSchema = new mongoose.Schema({
    name: String,
    prop1: String,
    menuId: Number
});

var MenuSchema = new mongoose.Schema({
    _id: Number,
    menuItem: [String],
    menuProperties: {k: String}
});

var Main = mongoose.model('Main', MainSchema);
var Menu = mongoose.model('Menu', MenuSchema);

And results are

[ { _id: 56fa39b4924d1254272ac3f1,
    name: 'main1',
    prop1: 'val',
    __v: 0,
    menuItem: [ 't1', 't2' ],
    menuProperties: { k: 'p1' } } ]

Upvotes: 1

user2263572
user2263572

Reputation: 5606

There are many ways to do this. You could always do something like this (note, you didn't post your schemas so I made up the names):

//get every document in menu collection
Menu.find({}).exec(function(err,all_menuItems){

    if (err) {throw err};

    //for all documents in them meny collection
    //find docuement in main collection and update

    all_menuItems.forEach(function(item){

        //wrapping in closure since doing forEach over async function
        function(){

            Main.findOne({_id:item._id}).exec(function(err,mainItem){

            if (err) {throw err};

            //set properties
            mainItem.menuItems = item.menuItems;
            menuItem.menuProperties = item.menuProperties;
            //save document
            Main.save(mainItem)

            })

        }(item)
    })

})

Also, mongoose as a built in populate method which can be used to do this. You can find that documentation here. http://mongoosejs.com/docs/populate.html.

Using populate would probably be a much better option than looping over a bunch of async calls (or re-writing the above code to process the updates synchronously). But you can handle whatever way makes the most sense for your db.

Upvotes: 2

Related Questions