MongooseJS - population path of subdocuments - node.js

I'm having real problems with population on the below schemas. It may not be the best design of models (relatively new to MEAN stacks) but I can get it to populate everything except the Spec model.
// Spec Model
var SpecSchema = new Schema({
time: {
type: Date,
default: Date.now
},
active: {
type: Boolean,
default: 'true'
},
name: String,
desc: String
});
module.exports = mongoose.model('Spec', SpecSchema);
// Thing model
var specsSchema = new Schema({
time: {
type: Date,
default: Date.now
},
spec: {
type: Schema.Types.ObjectId,
ref: 'Spec'
},
value: String,
});
var ThingSchema = new Schema({
time: {
type: Date,
default: Date.now
},
active: {
type: Boolean,
default: true
},
title: String,
specs: [specsSchema]
});
var Thing = mongoose.model('Thing', ThingSchema);
// Set model
var thingsSchema = new Schema({
time: {
type: Date,
default: Date.now
},
active: {
type: Boolean,
default: 'true'
},
thing: {
type: Schema.Types.ObjectId,
ref: 'Thing'
}
});
var SetSchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: 'User'
},
time: {
type: Date,
default: Date.now
},
active: {
type: Boolean,
default: 'true'
},
title: String,
things: [thingsSchema]
});
var Set = mongoose.model('Set', SetSchema);
The standard population is fine but i cant for the life of me get the model.populate to work and from all the examples and solutions I have looked at I'm unclear as to what the path should be.
Set.findById(req.params.id)
.populate('things.thing')
.populate('user', '_id name')
.exec(function (err, set) {
if(err) { return handleError(res, err); }
if(!set) { return res.send(404); }
Thing.populate(set,{
path:'things.thing.specs.spec',
select: 'name',
model: Spec
}, function(err, set){
if ( err ) return res.json(400, err);
});
return res.json(set);
});
any pointers in the right direction would be much appreciated.

path:'things.thing.specs.spec',
select: 'name',
model: Spec
should be
path:'things.thing.specs.spec',
select: 'name',
model: 'Spec'

Related

Is that possible, to solve a complex mongoose populate query?

I have four tables.
#First table
var mongoose=require('mongoose');
var Schema=mongoose.Schema;
#First table
var studentSchema=new Schema({
student_name: { type: String, required: true },
sendKey: { type: String },
created_by: { type:String },
updated_at: { type: Date, default: Date.now },
})
module.exports=mongoose.model('Student',studentSchema);
#Second table
var mongoose=require('mongoose');
var Schema=mongoose.Schema;
var institutionSchema=new Schema({
institution_name: { type: String, lowercase: true },
email: { type:String },
created_by: { type:String },
created_at: { type: Date, default: Date.now },
updated_at: { type: Date, default: Date.now },
})
module.exports=mongoose.model('Institution',institutionSchema);
#third table
var mongoose=require('mongoose');
// var Institution = ('./institution');
var Schema=mongoose.Schema;
var batchSchema=new Schema({
batch_name: { type: String, required: true },
institution: { type: Schema.Types.ObjectId, ref: 'Institution' },
created_by: { type:String },
created_at: { type: Date, default: Date.now },
updated_at: { type: Date, default: Date.now },
})
module.exports=mongoose.model('Batch',batchSchema);
#Fourth Table
var mongoose=require('mongoose');
var Schema=mongoose.Schema;
var batchtudentSchema=new Schema({
student_id: { type: Schema.Types.ObjectId, ref: 'Student', required : true},
batch_id: { type: Schema.Types.ObjectId, ref: 'Batch', required:true},
created_at: { type: Date, default: Date.now },
updated_at: { type: Date, default: Date.now },
})
module.exports=mongoose.model('BatchStudent',batchtudentSchema);
I have an institution Id. I want to find all students of this institution with Batch details.
Primarily I have tried to.. do that.
Student.find().populate('BatchStudent').pupulate('Batch').populate('Institution').exec((err, docs) => {
console.log(docs)
res.json(
{
data:docs
}
)
})
It is not working.. sending just all student data.. How can I get the data correctlly?
// Student Schema
var mongoose=require('mongoose');
var Schema=mongoose.Schema;
var studentSchema=new Schema({
student_name: {
type: String,
required: true
},
sendKey: {
type: String
},
batchStudent: {
type: Schema.Types.ObjectId,
ref: 'BatchStudent',
}
created_by: {
type:String
},
updated_at: {
type: Date,
default: Date.now
},
})
module.exports=mongoose.model('Student',studentSchema);
// BatchStudent Schema
var mongoose=require('mongoose');
var Schema=mongoose.Schema;
var batchtudentSchema=new Schema({
student_id: {
type: Schema.Types.ObjectId,
ref: 'Student',
required : true
},
batch_id: {
type: Schema.Types.ObjectId,
ref: 'Batch'
},
institution: {
type: Schema.Types.ObjectId,
ref: 'Institution'
}
})
module.exports=mongoose.model('BatchStudent',batchtudentSchema);
// Institution Schema
var mongoose=require('mongoose');
var Schema=mongoose.Schema;
var institutionSchema=new Schema({
institution_name: {
type: String,
lowercase: true
},
email: {
type: String
},
created_by: {
type:String
}
});
module.exports=mongoose.model('Institution',institutionSchema);
If the I removed created_at and updated_at to avoid duplication. Obey DRY principles always (Don't repeat yourself)
If you need to get the document you can simply use Deep Population/Association for that as shown below:
Student.find({}).populate({
path : 'batchStudent',
populate : {
path : 'institution'
}
}).exec(function (err, res) {
// do whatever you need to do here
})

Node.js mongoose model.findOne is not a function

Having an issue with a model. Trying to do a model.findOne(), but I keep getting the error
Error:
TypeError: User.findOne(...).than is not a function
I definitely installed mongoose, but it seems I am no longer importing mongoose or the User model appropriately? Not sure.
User.js (model)
const mongoose = require('mongoose')
const Schema = mongoose.Schema
module.exports = User => {
var UserSchema = new Schema({
name: String,
email: { type: String, unique: true },
password: String,
passwordResetToken: String,
passwordResetExpires: Date,
document: String,
profile_picture: String,
ocupation: { type: Schema.Types.ObjectId, ref: 'Ocupation' },
is_admin: { type: Boolean, default: false },
sector: { type: Schema.Types.ObjectId, ref: 'Sector' },
is_manager: { type: Boolean, default: false },
group: { type: Schema.Types.ObjectId, ref: 'Group' },
is_team_leader: { type: Boolean, default: false },
can_start: { type: Boolean, default: true },
isVerified: { type: Boolean, default: false },
created_at: { type: Date, default: Date.now },
updated_at: { type: Date, default: Date.now },
deleted_at: Date,
}, {
toJSON: {
virtuals: true
}
})
UserSchema.virtual('profile_url').get(function() {
return `http://${process.env.HOST}:${process.env.NODE_ENV ? process.env.DEV_PORT : process.env.PORT}/3e002f70cbf8805c904bf1536a22a52e/${this.profile_picture}`
})
return mongoose.model('Users', UserSchema)
}
UserController.js
const User = require('../models/User')
myfunction(req, res) {
const { name, email } = req.body
let checkEmail = await User.findOne({ email })
}
whats wrong?
You're not exporting the model, but a factory(?) function for generating a model. Just remove:
module.exports = User => {
and instead, edit your return statement to:
module.exports = mongoose.model('Users', UserSchema');
Also, side note, it's usually a good idea to define your model in a singular form: User, not Users.
You'll have to create reference to your model & then export that to be used as reference to the Schema
Like this,
const mongoose = require('mongoose')
const Schema = mongoose.Schema
var UserSchema = new Schema({
name: String,
email: { type: String, unique: true },
password: String,
passwordResetToken: String,
passwordResetExpires: Date,
document: String,
profile_picture: String,
ocupation: { type: Schema.Types.ObjectId, ref: 'Ocupation' },
is_admin: { type: Boolean, default: false },
sector: { type: Schema.Types.ObjectId, ref: 'Sector' },
is_manager: { type: Boolean, default: false },
group: { type: Schema.Types.ObjectId, ref: 'Group' },
is_team_leader: { type: Boolean, default: false },
can_start: { type: Boolean, default: true },
isVerified: { type: Boolean, default: false },
created_at: { type: Date, default: Date.now },
updated_at: { type: Date, default: Date.now },
deleted_at: Date,
}, {
toJSON: {
virtuals: true
}
})
UserSchema.virtual('profile_url').get(function() {
return `http://${process.env.HOST}:${process.env.NODE_ENV ? process.env.DEV_PORT : process.env.PORT}/3e002f70cbf8805c904bf1536a22a52e/${this.profile_picture}`
})
var User = mongoose.model('Users', UserSchema)
module.exports = User;

How to push objectId to mongodb schema field array of object using findByIdAndUpdate

Everytime I hit api I am getting same error-
I have tried sending value as parameters also but failed. Any help would be appreciated. When i use $set it updates same value everytime the web service is called but it does work but not with $push.
MongoError: The field 'songId' must be an array but is of type objectId in document
{_id: ObjectId('59709590380026118c22dd61')}
My Playlist schema code:-
var PlaylistSchema = new mongoose.Schema({
name: String,
coverphoto: { type: String, default: '' },
userId: {
type: ObjectId,
ref: 'User'
},
songId: [{ type: ObjectId, ref: 'Song' }],
updated_at: { type: Date, default: Date.now },
});
my Song Schema code
var mongoose = require('mongoose');
var Schema = mongoose.Schema,
ObjectId = Schema.ObjectId;
var SongSchema = new mongoose.Schema({
audioName:{type:String,default:''},
title: String,
// coverphoto: { type: String, default: '' },
singer: { type: String, default: '' },
movie: { type: String, default: '' },
album: { type: String, default: '' },
lyricist: { type: String, default: '' },
actors: { type: String, default: '' },
lyrics: { type: String, default: '' },
genre: { type: String, default: 'Random' },
duration: { type: String, default: '' },
size: { type: String, default: '' },
userId: {
type: ObjectId,
ref: 'User'
},
categoryId: {
type: ObjectId,
ref: 'Category'
},
updated_at: { type: Date, default: Date.now },
});
module.exports = mongoose.model('Song', SongSchema);
My Api code
/* post api to add song in playlist */
router.post('/addSongToPlaylist', function (req, res, next) {
Playlist.findOne({ '_id': req.body.playlistId }, function (err, playlist) {
if (err) return next(err);
console.log(playlist)
console.log("req.body.songId", req.body.songId);
if (playlist) {
Playlist.findByIdAndUpdate(
req.body.playlistId,
{ $push: { "songId": req.body.songId } },
{ new: true },
function (err, playlistData) {
if (err) return next(err);
res.json({ message: 'New Song added successfully', playlist: playlistData, success: true });
});
} else if (!Song) {
res.json({ message: 'Failed to add song', success: false });
}
});
});

Populate on basis of condition in mongoose, mongoDB

Here is my code to get one floorplan and populate all flats linked with this
see the code below :
var floorplan = Floorplan.find({
project: req.params.project,
tower: req.params.tower,
isDeleted: false
});
floorplan.populate('flats').exec(function(err, floorplan) {
if (err) { return res.send(err); }
if (!floorplan) { return res.status(401).json(); }
res.status(200).json(floorplan);
});
But I want to populate only those flats where isDeleted : false
How to achive this ??
Schema of floorplan
var FloorplanSchema = new Schema({
project: { type: Schema.ObjectId, ref: "Project" },
flats: [{ type: Schema.ObjectId, ref: "Flat" }],
tower: [{ type: Schema.ObjectId, ref: "Tower" }],
unitType: String,
area: Number,
floorPlan2D: String,
floorPlan3D: String,
livingRoomArea: Number,
kitchenArea: Number,
balconies: Number,
bathRooms: Number,
isDeleted: { type: Boolean, 'default': false },
createdAt: { type: Date, 'default': Date.now }
});
Schema of flat
var FlatSchema = new Schema({
tower: { type: Schema.ObjectId, ref: "Tower" },
floorplan: { type: Schema.ObjectId, ref: "Floorplan" },
project: { type: Schema.ObjectId, ref: "Project" },
status: String,
floor: Number,
size: String,
superbuiltup_area: Number,
directionFacing: String,
furnishingState: String,
flooringType: String,
createdAt: { type: Date, 'default': Date.now },
isDeleted: { type: Boolean, 'default': false },
});
The populate() method has an option which allows for filtering, you can either try this
Floorplan
.find({
project: req.params.project,
tower: req.params.tower,
isDeleted: false
})
.populate({
path: 'flats',
match: { isDeleted: false }
})
.exec(function(err, floorplan) {
if (err) { return res.send(err); }
if (!floorplan) { return res.status(401).json(); }
res.status(200).json(floorplan);
});
or
Floorplan
.find({
project: req.params.project,
tower: req.params.tower,
isDeleted: false
})
.populate('flats', null, { isDeleted: false })
.exec(function(err, floorplan) {
if (err) { return res.send(err); }
if (!floorplan) { return res.status(401).json(); }
res.status(200).json(floorplan);
});

Load specific mongoose models first

I'm attempting to use a model ("goal") in my "like" model class (Code below). However every time I startup the node instance it says that Goal.findById is not a function.
After running a console.log(number) in each model, I found that they load very oddly and out of the order I would like/need.
I was wondering how I can load models before others or set a specific load order for models?
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var User = require('../models/user.js');
var Goal = require('../models/goal.js');
var likeSchema = new Schema({
userPosted: {
type: Number,
ref: 'user',
required: true
},
goal: {
type: Number,
ref: 'goal',
required: true
}},
{
timestamps: true
});
likeSchema.post('save', function (doc, next) {
var goalID = doc.goal;
Goal.findOne({'_id': doc.goal}, function(err, goal) {
goal.likes.push(doc._id);
goal.save();
User.findById(doc.userPosted, function(err, user) {
user.likedPosts.push(goalID);
user.save();
next();
});
});
});
likeSchema.post('remove', function(doc) {
Goal.findById(doc.goal, function(err, goal) {
goal.likes.pull(doc._id);
goal.save();
User.findById(doc.userPosted, function(err, user) {
user.likedPosts.pull(goal._id);
user.save();
});
});
});
console.log("4");
module.exports = mongoose.model('like', likeSchema);
Error:
TypeError: Goal.findOne is not a function at model.<anonymous> (E:\Project\like.js:28:10)
**Edit: ** goal.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var autoIncrement = require('mongoose-auto-increment');
var User = require('../models/user.js');
var Like = require('../models/like.js');
var goal = new Schema({
user: {
type: Number,
ref: 'user',
required: true
},
user_name: {
type: String,
required: true
},
title: {
type: String,
required: true,
trim: true
},
description: {
type: String,
default: undefined,
trim: true
},
location: {
type: String,
default: undefined
},
likes: [{
type: Schema.ObjectId,
ref: 'like'
}],
comments: [{
type: Schema.ObjectId,
ref: 'comment'
}],
updates: [{
type: Schema.ObjectId,
ref: 'update'
}],
created: {
type: Date,
default: Date.now
},
cover_image: {
type: String,
default: undefined
},
complete_by: {
type: String,
default: "Death"
},
completed: {
type: Boolean,
default: false
},
completedDate: {
type: String,
default: undefined
},
url: {
type: String,
default: undefined
},
sponsor: {
type: String,
default: undefined
},
private: {
type: Boolean,
default: false
}
});
goal.plugin(autoIncrement.plugin, 'goal');
goal.pre('save', function (next) {
this.wasNew = this.isNew;
next();
});
goal.post('save', function (doc) {
if (this.wasNew) {
User.findById(doc.user, function (err, user) {
user.goals.push(doc._id);
user.save();
});
}
});
goal.post('remove', function(doc) {
//TODO: Removes like objects/user profile/anything containing the goal
User.findById(doc.user, function(err, user) {
user.goals.pull(doc._id);
user.save();
});
Like.find({
'goal': doc._id
}).remove(function(err, removed) {
if(err) {
console.log("ERROR?");
return;
}
console.log('removed likes - ' + removed);
});
});
module.exports = mongoose.model('goal', goal);
I think your likeschema would be something like this -
var likeSchema = new Schema({
userPosted: {
type: Number,
ref: 'User',
required: true
},
goal: {
type: Number,
ref: 'Goal',
required: true
}},
{
timestamps: true
});
If it doesn't work, can you please show your code for Goal model?

Resources