0

I am new to programming in NodeJS and was looking for solutions around how to populate response attributes in json Object which are of type Object Array.

Below is the sample response from my service -

{
        "Roles": [
            "5b7fd72537651320a03494f8",
            "5b7fdc06e3fcb037d016e26a"
        ],
        "_id": "5b8e530be2d4630fd4f4b34a",
        "Username": "ctucker",
        "FirstName": "Chris",
        "LastName": "Tucker",
        "Email": "[email protected]",
        "createdAt": "2018-09-04T09:40:27.091Z",
        "updatedAt": "2018-09-04T09:40:27.091Z",
        "__v": 0
    }

I would like to populate the ObjectId's in the Roles array attribute with the actual name instead of the Ids.

How should I be doing this.

Following is the way, I have defined the User and Roles schema

User Model

const userSchema = new mongoose.Schema({

    Username: {
        type: String,
        required: true,
        minlength: 5,
        maxlength: 255,
        unique: true
    },
    FirstName: {
        type: String
    },
    LastName: {
        type: String
    },
    Email: {
        type: String,
        required: true,
        minlength: 5,
        maxlength: 255
    },
    Password: {
        type: String,
        required: true,
        minlength: 5,
        maxlength: 1024
    },
    Roles: [{type: mongoose.Schema.Types.ObjectId, ref: Roles}],
    Active: {type: Boolean, default: true},
    SuperUser: {type: Boolean, default: false}
},{
    timestamps: true
});

Roles Model

const rolesSchema = new mongoose.Schema({
    RoleName: {
        type: String,
        required: true,
        minlength: 5,
        maxlength: 255,
        unique: true
    },
    Description: {
        type: String
    },
    Active: {type: Boolean, default: true}
}, {
    timestamps: true
});

Create User

router.post('/', [auth, admin], async (req, res) => {

  const { error } = validate(req.body); 
  if (error) return res.status(400).send(error.details[0].message);

  let user = await User.findOne({ Username: req.body.Username });
  if (user) return res.status(400).send('User with username: ', req.body.Username, 'already exists. Please try with any other username');

  let roleInput = [];

  if(req.body.Roles !== null) {
    req.body.Roles.forEach( async (element) => {
      objectId = mongoose.Types.ObjectId(element);
      roleInput.push(objectId);
    });
  }

  console.log('Role info ', roleInput);

  user = new User(_.pick(req.body, ['Username', 'FirstName', 'LastName' ,'Email', 'Password', 'Active','SuperUser']));

  console.log('User Input => ', user);

  const salt = await bcrypt.genSalt(10);
  user.Password = await bcrypt.hash(user.Password, salt);

  roleInput.forEach( async (objectId) => {
    user.Roles.push(objectId);
  });  

  console.log('User Input after role addition => ', user);

  await user.save();

  const token = user.generateAuthToken();
  res.header('x-auth-token', token).send(_.pick(user, ['_id', 'FirstName', 'LastName' ,'Email', 'Roles']));

});

I would like to get the RoleName instead of the ObjectId in response. Any help in this regard would be appreciated.

Forgot to mention that I already tried using the populate method, but I see the following exception.

How I am using the populate method -

router.get('/', auth, (req, res, next) => {
  var request_params = url.parse(req.url,true).query;
  var searchQuery = (request_params.mode === 'active') ? {'Active': true} : {};
  User.find(searchQuery)
    .populate('Roles')
    .select(['-Password', '-Active', '-SuperUser'])
    .then((users) => {
        res.send(users);
    }, (error) => {
      next(error);
    });
});

Exception -

<!DOCTYPE html>
<html>
    <head>
        <title></title>
        <link rel="stylesheet" href="/stylesheets/style.css">
    </head>
    <body>
        <h1>Schema hasn't been registered for model &quot;[object Object]&quot;.
Use mongoose.model(name, schema)</h1>
        <h2></h2>
        <pre>MissingSchemaError: Schema hasn't been registered for model &quot;[object Object]&quot;.
Use mongoose.model(name, schema)
    at new MissingSchemaError (C:\Vijay-Docs\Personal\vuejs-ws\AgileCenter\agilecenterservices\node_modules\mongoose\lib\error\missingSchema.js:20:11)
    at NativeConnection.Connection.model (C:\Vijay-Docs\Personal\vuejs-ws\AgileCenter\agilecenterservices\node_modules\mongoose\lib\connection.js:791:11)
    at getModelsMapForPopulate (C:\Vijay-Docs\Personal\vuejs-ws\AgileCenter\agilecenterservices\node_modules\mongoose\lib\model.js:4016:20)
    at populate (C:\Vijay-Docs\Personal\vuejs-ws\AgileCenter\agilecenterservices\node_modules\mongoose\lib\model.js:3505:21)
    at _populate (C:\Vijay-Docs\Personal\vuejs-ws\AgileCenter\agilecenterservices\node_modules\mongoose\lib\model.js:3475:5)
    at utils.promiseOrCallback.cb (C:\Vijay-Docs\Personal\vuejs-ws\AgileCenter\agilecenterservices\node_modules\mongoose\lib\model.js:3448:5)
    at Object.promiseOrCallback (C:\Vijay-Docs\Personal\vuejs-ws\AgileCenter\agilecenterservices\node_modules\mongoose\lib\utils.js:232:14)
    at Function.Model.populate (C:\Vijay-Docs\Personal\vuejs-ws\AgileCenter\agilecenterservices\node_modules\mongoose\lib\model.js:3447:16)
    at cb (C:\Vijay-Docs\Personal\vuejs-ws\AgileCenter\agilecenterservices\node_modules\mongoose\lib\query.js:1678:17)
    at result (C:\Vijay-Docs\Personal\vuejs-ws\AgileCenter\agilecenterservices\node_modules\mongoose\node_modules\mongodb\lib\utils.js:414:17)
    at executeCallback (C:\Vijay-Docs\Personal\vuejs-ws\AgileCenter\agilecenterservices\node_modules\mongoose\node_modules\mongodb\lib\utils.js:406:9)
    at handleCallback (C:\Vijay-Docs\Personal\vuejs-ws\AgileCenter\agilecenterservices\node_modules\mongoose\node_modules\mongodb\lib\utils.js:128:55)
    at cursor.close (C:\Vijay-Docs\Personal\vuejs-ws\AgileCenter\agilecenterservices\node_modules\mongoose\node_modules\mongodb\lib\operations\cursor_ops.js:211:62)
    at handleCallback (C:\Vijay-Docs\Personal\vuejs-ws\AgileCenter\agilecenterservices\node_modules\mongoose\node_modules\mongodb\lib\utils.js:128:55)
    at completeClose (C:\Vijay-Docs\Personal\vuejs-ws\AgileCenter\agilecenterservices\node_modules\mongoose\node_modules\mongodb\lib\cursor.js:887:14)
    at Cursor.close (C:\Vijay-Docs\Personal\vuejs-ws\AgileCenter\agilecenterservices\node_modules\mongoose\node_modules\mongodb\lib\cursor.js:906:10)</pre>
    </body>
</html>

3 Answers 3

1

Understood the problem.

Within the User schema definition, I had to define the Roles Schema as well.

const rolesSchema = new mongoose.Schema({
    RoleName: {
        type: String,
        required: true,
        minlength: 5,
        maxlength: 255,
        unique: true
    },
    Description: {
        type: String
    },
    Active: {type: Boolean, default: true}
}, {
    timestamps: true
});

const Roles = mongoose.model('Roles', rolesSchema);

const userSchema = new mongoose.Schema({

    Username: {
        type: String,
        required: true,
        minlength: 5,
        maxlength: 255,
        unique: true
    },
    FirstName: {
        type: String
    },
    LastName: {
        type: String
    },
    Email: {
        type: String,
        required: true,
        minlength: 5,
        maxlength: 255
    },
    Password: {
        type: String,
        required: true,
        minlength: 5,
        maxlength: 1024
    },
    Roles: [{type: mongoose.Schema.Types.ObjectId, ref: 'Roles'}],
    Active: {type: Boolean, default: true},
    SuperUser: {type: Boolean, default: false}
},{
    timestamps: true
});

After adding the roles schema definition within the user schema, the response from postman does populate the RoleName in response.

{
        "Roles": [
            {
                "RoleName": "AC-User"
            },
            {
                "RoleName": "AC-PMLead"
            }
        ],
        "_id": "5b8e48e2f3372a098c6e5346",
        "Username": "mcmillan",
        "FirstName": "McMillan",
        "LastName": "McMillan",
        "Email": "[email protected]",
        "createdAt": "2018-09-04T08:57:07.029Z",
        "updatedAt": "2018-09-04T08:57:07.029Z",
        "__v": 0
    }
Sign up to request clarification or add additional context in comments.

1 Comment

This is correct. We need import shema which we wanna use in ref.
0

Simple using mongoose populate() function

const user = await User.findOne().populate('Roles');
// output => user.Roles // will be array of mongo documents

Cheers

1 Comment

Can u add the exception to the question
0

You can use Mongoose's populate to populate RoleName instead of ObjectId refs

Mongoose has a more powerful alternative called populate(), which lets you reference documents in other collections.

'use strict';

let user = await User.findOne({
  Username: req.body.Username
}).populate('Roles');

if (user && user.Roles.length > 0) {
  for (let role of user.Roles) {
    console.log(role.RoleName);// logs `RoleName`
  }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.