laxman
laxman

Reputation: 1348

Merge two objects in mongoose

I have two models in user.js and userProfile.js of mongoose, where I want to get documents using a join query but I have not take ref in user schema so I have code as below:

user.js

var userSchema = new Schema({
     nick_name:{type:String},
     email: {type: String},
     password: {type: String},
    is_active:{type:String,enum:['1','0'],default:"1"},
},{ collection: 'user'});

userProfile.js

var userProfileSchema = new Schema({
    user_id:{type:Schema.Types.ObjectId, ref:'User'},
    first_name:{type:String},
    last_name:{type:String},
    address:{type:String},
    country:{type:String},
    state:{type:String},
    city:{type:String},
    gender:{type:String,enum:['m','f','o'],default:"m"},
    is_active:{type:String,enum:['1','0'],default:"1"},
},{ collection: 'userProfile'});

server.js

app.get('/api/userLists', function(req, res) {
    User.find({},"nick_name email",function(err, user) {
        if(err) {
            res.json(err);
        } else {
            var userIds = user.map(function(obj){
               return obj._id;
            });

            UserProfile.find({user_id:{$in:userIds}},function(err,userProfiles){
                if(err){
                    res.json(userProfiles);
                    console.log(userProfiles);
                }else{

                    ---------------------------------
                    -What to do here, I have no idea.-
                    ---------------------------------

                    res.json(user);
                }
            });

        }
    });

});

expected output as follows

{
 "nick_name"   : "laxman",
 "email"       : "[email protected]",
 "first_name"  : "my first name",
 "last_name"   : "my last name",
 "address"     : "my address",
 "country"     : "my country",
 "state"       : "my state",
 "city"        : "my city",
 "gender"      : "m",
}

**OR**

{
 "nick_name"   : "laxman",
 "email"       : "[email protected]",
 "profile" :{
        "first_name"  : "my first name",
        "last_name"   : "my last name",
        "address"     : "my address",
        "country"     : "my country",
        "state"       : "my state",
        "city"        : "my city",
        "gender"      : "m",
   }
}

dependencies

"express"  => "version": "4.7.4",
"mongoose" => "version": "4.4.5",
"mongodb"  => "version": "2.4.9",
"OS"  => "ubuntu 14.04 lts 32bit",

Upvotes: 3

Views: 13351

Answers (3)

hankchiutw
hankchiutw

Reputation: 1662

You can use aggreate which implements mongodb aggregation.

Sample code may be looked like:

UserProfile.aggregate()
    .match({user_id: {$in: userIds}})
    .group({_id: "$user_id"})  // '$' is must
    .project(...)  // fields you want from UserProfile
    .exec(function(err, result){
        // do populate or something 
    })

You may also like to know how to populate the aggregate result.

Upvotes: 1

zangw
zangw

Reputation: 48396

Please try this one, or you could use async or promise to make codes more concisely.

MyUser.find({}, 'nick_name email', function(err, users){
    if (err)
        console.log(err);
    else {
        var len = users.length;
        var curIdx = 0;
        var newUsers = [];

        users.forEach(function(user) {
            UserProfile.findOne({user_id: user._id}, function(err, ret) {
                if (err)
                    console.log(err);
                else{
                    // combine those two objects here...
                    user.set('profile', ret.toJSON(), {strict: false})
                    newUsers.push(user);
                    ++curIdx;

                    if (curIdx == len) {
                        //console.log(newUsers);
                        res.json(newUsers);
                    }
                }
            });
        });

    }
})

Upvotes: 3

chridam
chridam

Reputation: 103365

Population would be ideal in your case. From the docs:

Population is the process of automatically replacing the specified paths in the document with document(s) from other collection(s).

Population will seamlessly help you bring data from the Profile collection into your User model. For example, to get the second output in the json form

{
    "nick_name": "laxman",
    "email": "[email protected]",
    "profile": {
        "first_name"  : "my first name",
        "last_name"   : "my last name",
        "address"     : "my address",
        "country"     : "my country",
        "state"       : "my state",
        "city"        : "my city",
        "gender"      : "m",
    }
}

consider the following example with the approach you can take. Change your User schema to

var mongoose = require('mongoose');
var Schema = mongoose.Schema;

var userSchema = new Schema({
    nick_name: { type: String },
    email: { type: String },
    password: { type: String },
    profile: { type: Schema.Types.ObjectId, ref: 'UserProfile' }
},{ collection: 'user'});

module.exports = mongoose.model('User', userSchema);

In your user schema definition, you add the primary surrogate key to a user profile, called ObjectId, referenced as _id in the data to get the populate functionality. This will be the key used to refer to documents in the UserProfile collection.

Reading the data

This is where Mongoose population displayes how very straightforward, easy and fast it is to makes reads of documents through its populate() function. So for instance, to show the referenced profile on a user, call the populate() method with the name of that field in a string as a parameter e.g.

app.get('/api/userLists', function(req, res) {
    User.find({}, "nick_name email")
        .populate("profile")
        .exec(function(err, users) {
            if(err) {
                res.json(err);
            } else {
                res.json(users)
            }
        });

Writing data

When you save data for the user model, you will also need to save the references to the profile. For instance, when a new User is created, you'll need the UserProfile _id reference saved as the profile field:

var user = new User();
var profileId = "54b5659536cd5250a3a93bd3"; // <-- user profile id, for example.
user.nick_name = req.body.name;
user.email = req.body.email;
user.password = encrypt(req.body.password, user.nick_name);
user.profile = profileId; 

user.save(function(err) {
    if (err)
        res.send(err);
    res.json({ message: 'user created!' });
});

Upvotes: 4

Related Questions