3

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"       : "laxman@mailinator.com",
 "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"       : "laxman@mailinator.com",
 "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",
chridam
  • 100,957
  • 23
  • 236
  • 235
laxman
  • 1,338
  • 2
  • 10
  • 27

3 Answers3

4

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": "laxman@mailinator.com",
    "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!' });
});
chridam
  • 100,957
  • 23
  • 236
  • 235
  • 1
    Thanks for give me an idea, but in my application I have more than 15 collections related to user collection, in that situation I don't like to put all collections reference in user collection. – laxman Mar 14 '16 at 11:59
3

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

    }
})
zangw
  • 43,869
  • 19
  • 177
  • 214
1

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.

Community
  • 1
  • 1
hankchiutw
  • 1,546
  • 1
  • 12
  • 15