Mapping and getting data from two collections in mongoose - node.js

I have two collections User and Contact. User will request another user for contact and both userids will be stored in contact collection with status. I want to use the contact and user in many other places, so wrote separate models.
Now I want to get user profile with contact object, the contact object should have the contact user information.
User Schema
var UserSchema = new mongoose.Schema({
name : {
type:String,
default: '',
required:'Please fill name.',
trim: true
},
mobile : {
type:String,
required:'Please fill mobile',
unique:true,
trim:true
},
contacts:[{
type: mongoose.Schema.ObjectId,
ref: 'Contact'
}]
});
Contact Schema
var ContactSchema = new mongoose.Schema({
owner: {
type: mongoose.Schema.ObjectId,
ref: 'User'
},
user: {
type: mongoose.Schema.ObjectId,
ref: 'User'
},
status: {
type:String,
default:'pending'
}
});
Profile API
User
.findById(req.body.user_id)
.populate({
path: 'contacts',
select: 'user',
})
.exec(function(err, user){
if (err) return res.send(err);
res.status(200).json({'message':'Profile found.','user':user});
});
Here the response will be as follows:
{
"message": "Profile found.",
"user":
{
"_id": "563037f3fe2b69e40b05c451",
"mobile": "32435345",
"__v": 1,
"contacts":
[
{
"_id": "56303f04f1b8524f0d03d9a7",
"user": "563037bafe2b69e40b05c44e"
}
],
"name": "Lorem"
}
}
In the contacts array, I need the name and mobile field for the connected user along with id. How can I achieve this? Or is there any other better approach in schema design?

You should try mongoose-deep-populate module.
var UserSchema = new Schema({
name: String,
mobile: String,
contacts: [{type: mongoose.Schema.ObjectId, ref: 'Contact'}]
})
var ContactSchema = new Schema({
owner: {
type: mongoose.Schema.ObjectId,
ref: 'User'
},
user: {
type: mongoose.Schema.ObjectId,
ref: 'User'
},
status: {
type:String,
default:'pending'
}
});
Initialize the module with a mongoose instance:
var deepPopulate = require('mongoose-deep-populate')(mongoose);
UserSchema.plugin(deepPopulate, options /* more on options below */);
Now you can populate:
User
.findById(req.body.user_id)
.deepPopulate(users, 'contacts.user', function (err, _users) {
// _posts is the same instance as posts and provided for convenience
users.forEach(function (user) {
// post.contacts and user.contacts.user are fully populated
});
});

Related

What is the best approach to save a 4 level nested schema in mongoose NodeJs

I have 4 level nested schema:
Framework has Domain referenced to it, Domain has control referenced to it, and Control has SubControl referenced to it.
Now I have been searching for a while and I keep getting confused.
First question: is it possible to post all the data from the framework it self?
Second question: I have used referencing with ID approach, should I switch to using subDocuments?
Framework Schema:
const FrameworkSchema = new Schema({
name: {
type: String,
trim: true
},
description: {
type: String,
trim: true
},
domain: [{
domain: {type: Mongoose.Schema.Types.ObjectId, ref: 'Domain'}
}],
updated: Date,
created: {
type: Date,
default: Date.now
}
});
module.exports = Mongoose.model('Framework', FrameworkSchema);
Domain Schema:
const DomainSchema = new Schema({
_id: {
type: Schema.ObjectId,
auto: true
},
domainNo: {
type: String,
trim: true
},
domainName: {
type: String,
trim: true
},
domainDescription: {
type: String,
trim: true
},
framework: {
type: Mongoose.Schema.Types.ObjectId,
ref: 'Framework'
},
control: [{
control: {type: Mongoose.Schema.Types.ObjectId, ref: 'Control'}
}],
updated: Date,
created: {
type: Date,
default: Date.now
}
});
module.exports = Mongoose.model('Domain', DomainSchema);
My control Schema:
const ControlSchema = new Schema({
_id: {
type: Schema.ObjectId,
auto: true
},
mainControl: {
type: String
},
subControl: [{
subControlNo: {type: Mongoose.Schema.Types.String, ref: 'SubControl'}
}],
controlDescription: {
type: String,
trim: true
},
updated: Date,
created: {
type: Date,
default: Date.now
}
});
module.exports = Mongoose.model('Control', ControlSchema);
My SubControl Schema
const SubControlSchema = new Schema({
_id: {
type: Schema.ObjectId,
auto: true
},
subControlNo: {
type: [String]
},
updated: Date,
created: {
type: Date,
default: Date.now
}
});
module.exports = Mongoose.model('SubControl', SubControlSchema);
Now I'm trying to post this nested documents from the framework api:
router.post(
'/add',
auth,
role.checkRole(role.ROLES.Admin), async (req, res) => {
try {
const subControl = new SubControl({...req.body});
const subControlDoc = await subControl.save();
const control = new Control({...req.body}); // take the control data
control.subControl.push(subControlDoc._id); // take the subControl and push the ID into the control
const controlDoc = await control.save();
//make the subcontrol pushed into control
// make control pushed in domain
const domain = new Domain({...req.body});
domain.control.push(controlDoc._id);
const domainDoc = await domain.save();
const framework = new Framework({...req.body});
framework.domain.push(domainDoc._id);
const frameworkDoc = await framework.save(); //save the framework + domain's ID
res.status(200).json({
success: true,
message: `Framework has been added successfully!`,
framework: frameworkDoc
});
} catch (error) {
res.status(400).json({
error
// error: 'Your request could not be processed. Please try again.'
});
}
}
);
Now I'm using push to push the data as an array, not sure if this the right approach, and Is it possible to post the all the data from the framework api?
Tried to post this from postman:
{
"name": "ISO780001",
"description": "frameworkDescription",
"domain":
[
{
"domainNo": "11",
"domainName": "domainName00",
"domainDescription": "domaindescu0",
"control": [{
"mainControl": "1-4",
"subControl": [{
"subControlNo": "1-4-1"
},
{
"subControlNo": "1-4-2"
}],
"controlDescription": "controlDescriptionTest"
},
{
"mainControl": "1-4",
"subControl": [{
"subControlNo": "1-4-1"
},
{
"subControlNo": "1-4-2"
}],
"controlDescription": "controlDescriptionTest"
}
]
},
{
"domainNo": "1-2",
"name": "domainName00",
"description": "domaindescu0",
"control": {
"mainControl": "1-4",
"subControl": [{
"subControlNo": "1-4-1"
},
{
"subControlNo": "1-4-2"
}],
"controlDescription": "controlDescriptionTest"
}
}
]
}
Only the id's of the domain, control, and subControl get saved in mongodb, is this the how it works can I post all the data from one model in this case the framework? or should I use embedded approach ?
What I will do in scenario where i have alot of references (mongoose name it ref by the way, which allows you to populate).
Example of a frameWork schema with domain reference.
const frameworkSchema = mongoose.Schema({
domains: [{type: mongoose.Schema.Types.ObjectId, ref: 'Domain'}],
})
const FrameworkModel = mongoose.model('Framework', frameworkSchema)
Domain above refers to a Domain model. We can create a domain model now.
const domainSchema = mongoose.Schema({
_id: { type: mongoose.Schema.Types.ObjectId } //this is the default
})
const DomainModel = mongoose.model('Domain', domainSchema);
Example Usage - We want to get all the domain information related to a specific schema.
const results = FrameworkModel.findOne({ _id: 'some framework id'}).populate('domains').lean({ virtuals: true });
The results will be something like
{
_id: 'some framework id',
name: 'name of framework',
domains: [
{
_id: 'id of domain 1',
name: 'example.com'
},
{
_id: 'id of domain 2',
name: 'example2.com'
}
]
}
You can also explore virtuals to see how you can maintain your framework, domains and other controls as separate collections, in order to easily reference to them. This is a better design than nesting multiple levels in a single document.
Unless where necessary, using separate collections has more benefits than using subdocuments. (easier to find documents and easier to update documents, and of course, more performant)

POPULATION ISSUE: Mongoose/Express

I'm trying to have each record attached to a user who created it,
and every user have their records attached.
Here are my schemas:
1.The Records schema:
const mongoose = require('mongoose')
const RecordsSchema = new mongoose.Schema(
{
Title: { type: String, required: true },
postedby:[{
type: mongoose.Schema.Types.ObjectId,
ref: 'user'
}],
Author: { type: String, required: true},
ISBN: { type: String, required: true },
Review: { type: String },
SelectedFile: { type: String },
Likes: { type: Number, default:0},
Date: { type: Date, default: Date.now()}
});
module.exports = Records = mongoose.model('record', RecordsSchema', 'record');`
Here is the The user Schema:
const mongoose = require('mongoose')
const userSchema = new mongoose.Schema(
{
username: { type: String},
email: { type: String, required: true ,unique:true},
records:[{
type: [mongoose.Schema.Types.ObjectId],
ref: 'record' }],
password: { type: String, required: true},
Date: { type: Date, default: Date.now(), immutable: true }
});
module.exports = User = mongoose.model('user', userSchema,'user');
The express route for getting a record:
router.get('/postedby/', (req, res) => {
Records.find(req.params.id)
.populate('postedby')
.exec()
.then(post =>{
if (!post) {
return res.status(400).json({ msg: 'Add Posts' });
}
else return res.json(post);
}).catch (err => console.error(err))
});
Result of the route:
{
"postedby": [],
"Likes": 0,
"_id": "5fed8c12a4fb2c1e98ef09f6",
"Title": "New Age",
"Author": "Situma Prisco",
"ISBN": "23422",
"SelectedFile": "",
"Review": "",
"Date": "2020-12-31T08:30:10.321Z",
"__v": 0
},
I'm getting a blank Array on the populated user field(posteddby) .
Please help, What am I doing wrong? And yes, i do have a User Logged in
You are too close.
In your schema definition, postedby field itself is an array. Hence you can simply define it like :
postedby:[{
type: mongoose.Schema.Types.ObjectId,
ref: 'user'
}]
Make sure that the ObjectID is stored properly in the postedby array.
Also, you are trying to find a single record based on the ID, hence you can use findById(req.params.id) or findOne({_id:req.params.id}) which would return a single document.

Mongoose .populate() does not populate

I have two schema's Role and User. When I do a Role.find() I want to populate the users with the users that have that specific role.
let roleSchema = new Schema({
name: {
type: String,
required: true,
},
...,
users: [{
type: mongoose.Schema.ObjectId,
ref: 'User'
}]
}, {
timestamps: true,
});
const Role = mongoose.model('Role', roleSchema);
let userSchema = new Schema({
username: {
type: String,
required: true,
},
...,
role: {
type: mongoose.Schema.ObjectId,
ref: 'Role'
},
}, {
timestamps: true,
}
);
const User = mongoose.model('User', userSchema);
When I get the Role with the following code:
Role.findById(req.params.roleId)
.populate('users')
.then(role => {
res.send(role);
}).catch(err => {
res.send(err)
});
It returns the following:
{
"users": [
],
"_id":"5c78006df35ca926534ad865",
"name":"Role",
"createdAt":"2019-02-28T15:38:21.741Z",
"updatedAt":"2019-02-28T15:38:21.741Z",
"__v":0
}
User.find().populate('role') works just fine, but Role.find().populate('users') doesn't.
Am I doing something wrong or don't I need .populate() in this case?
Nevermind. I think I had to use aggregate().lookup() for that all along... since there weren't any ids in Role.users...
Role.aggregate()
.lookup({
from: 'users',
localField: '_id',
foreignField: 'role',
as: 'users'
}).then( role => {
res.send(role);
});
This gave me the response I wanted.

insert mongodb user id inside another users schema

Having trouble with the following schema
const FriendRequestSchema = new Schema({
requester: {
_id: {
type: Schema.Types.ObjectId
}
},
profilePicture: String,
pending: {
type: Boolean,
default: true
}
})
the way i create a new friend request
const friendRequest = new FriendRequest({
requester: {
_id: req.user._id
},
profilePicture: req.user.profilePicture
})
results object
{
"pending": true,
"_id": {
"$oid": "5ab4fdb07525fd6880d1a6b9"
},
"profilePicture": "/assets/profile_icons/glasses.png"
}
now the problem is:
im missing the requester field
the id field is always different and dosent save the requester id
how can i fix this ?
Try change you schema like this:
requester: {
type: Schema.Types.ObjectId,
required: true,
ref: 'User', // depends on how you called user schema
},
Usage:
const friendRequest = new FriendRequest({
requester: req.user._id,
profilePicture: req.user.profilePicture
})

Is it possible to populate a field in mongoose which has other information on it too?

This is the relevant portion of my Schema:
var classroomSchema = mongoose.Schema({
students: [{
_id: mongoose.Schema.Types.ObjectId,
ref: 'User',
rate: {
type: Number,
default: 200,
},
referrals: [mongoose.Schema.Types.ObjectId],
}],
)};
Here rate and referrals are properties of the students which are valid in the context of that classroom, and cannot be populated from the Students model.
Is there any way to define my schema such that I can keep those fields(rate and referrals) and also use populate to link other fields such as name, age, etc of the student?
Change your schema to this:
var classroomSchema = mongoose.Schema({
students: [{
rate: { type: Number, default: 200 },
user: { ref: 'User', type: Schema.Types.ObjectId },
referrals: [mongoose.Schema.Types.ObjectId],
}],
});
Usage:
classroom
.findOne(...)
.populate('user')
.exec((err, classroom) => {
let student0 = classroom.students[0];
// student0.rate
// student0.user.name
});

Resources