mongoose js populate embedded objects in array - node.js

I am trying to display a list of subject names associated with a training event, however the object id is the only property that is populated after the query has executed. I have tried the solution in this QUESTION which seems to be similar to what I am trying to achieve, but it is not working.
My load method is at the very bottom of this code snippet.
Any help is appreciated, thanks.
var UserSchema = new Schema({
firstName: {
type: String,
trim: true,
default: '',
validate: [validateLocalStrategyProperty, 'Please fill in your first name']
},
lastName: {
type: String,
trim: true,
default: '',
validate: [validateLocalStrategyProperty, 'Please fill in your last name']
},
displayName: {
type: String,
trim: true
}
});
var SubjectSchema = new Schema({
name: {
type: String,
default: '',
required: 'Please fill subject name',
trim: true
},
description: {
type: String,
default: '',
required: 'Please fill subject description',
trim: true
}
});
var TrainingSubjectSchema = new Schema({
subject: {
type: Schema.ObjectId,
ref: 'Subject'
},
attendanceRate:[{
type: Number
}]
});
var TrainingEventSchema = new Schema({
name: {
type: String,
default: '',
required: 'Please fill training event name',
trim: true
},
created: {
type: Date,
default: Date.now
},
user: {
type: Schema.ObjectId,
ref: 'User'
},
attendees:[{
type: Schema.ObjectId,
ref: 'User'
}],
subjects:[TrainingSubjectSchema]
});
// retrieve method
Training.findById(id).populate('user', 'displayName').populate('subjects.subject','name').exec(function(err, trainingevent)

Not sure why it is not working in your side but I have tried
in my code and it is working as expected.
Here it is how I am creating the Schemas:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
mongoose.connect('mongodb://localhost:27017/mongoose_benn');
var UserSchema = new Schema({
firstName: {type: String, trim: true, default: ''},
lastName: {type: String, trim: true, default: ''},
displayName: {type: String, trim: true}
});
var SubjectSchema = new Schema({
name: {
type: String,
default: '',
required: 'Please fill the subject name.',
trim: true
},
description: {
type: String,
default: '',
required: 'Please fill subject description',
trim: true
}
});
var TrainingSubjectSchema = new Schema({
subject: {type: Schema.ObjectId, ref: 'Subject'},
attendanceRate: [{type: Number}]
});
var TrainingEventSchema = new Schema({
name: {
type: String,
default: '',
required: 'Please fill the training event name.',
trim: true
},
created: {type: Date, default: Date.now},
user: {type: Schema.ObjectId, ref: 'User'},
attendes: [{
type: Schema.ObjectId,
ref: 'User'
}],
subjects: [TrainingSubjectSchema]
});
module.exports = {
User: mongoose.model('User', UserSchema),
Subject: mongoose.model('Subject', SubjectSchema),
TrainingSubject: mongoose.model('TrainingSubject', TrainingSubjectSchema),
TrainingEvent: mongoose.model('TrainingEvent', TrainingEventSchema)
};
And here I am processing data with the models:
var async = require('async');
var models = require('./models.js');
var User = models.User;
var Subject = models.Subject;
var TrainingSubject = models.TrainingSubject;
var TrainingEvent = models.TrainingEvent;
async.waterfall(
[
clearDB.bind(null, {collections: [User, Subject, TrainingSubject, TrainingEvent], async: async}),
createUsers.bind(null, {User: User, async: async}),
createSubjects.bind(null, {async: async, Subject: Subject}),
createTrainingEvents.bind(null, {async: async, TrainingEvent: TrainingEvent}),
showTrainingEvents.bind(null, {TrainingEvent: TrainingEvent})
]
);
function createTrainingEvents(data, db, next) {
var firstSubject = db.subjects[0];
var secondSubject = db.subjects[1];
var trainingSubjects = [
{
subject: firstSubject._id,
attendanceRate: [5, 5]
},
{
subject: secondSubject._id,
attendanceRate: [4, 4, 5]
}
];
var trainingEvents = [
{
name: 'October Fest',
user: db.users[0]._id,
subjects: [trainingSubjects[0]],
attendes: [db.users[0]._id, db.users[1]._id]
},
{
name: 'August Fest',
user: db.users[1]._id,
subjects: [trainingSubjects[1]],
attendes: [db.users[0]._id, db.users[1]._id]
}
];
data.async.map(
trainingEvents,
function(trainingEvent, done) {
data.TrainingEvent.create(trainingEvent, done);
},
function(err, result) {
next(err);
}
);
}
function clearDB(data, next) {
async.map(
data.collections,
function(collection, done) {
collection.remove({}, done);
},
function(err) {
next(err);
}
);
}
function createUsers(data, next) {
var users = [
{firstName: 'Wilson', lastName: 'Balderrama', displayName: 'Wily'},
{firstName: 'Santiago', lastName: 'Balderrama', displayName: 'Santi'},
{firstName: 'Keila', lastName: 'Balderrama', displayName: 'Kei'}
];
data.async.map(
users,
function(user, done) {
data.User.create(user, done);
},
function(err, results) {
next(err, {users: results});
}
);
}
function showUsers(data, next) {
data.User.find({}, function(err, users) {
next();
});
}
function createSubjects(data, db, next) {
var subjects = [
{
name: 'JavaScript for Beginners',
description: 'JS fundamentals'
},
{
name: 'Node.JS for Beginners',
description: 'Node.JS streams'
}
];
data.async.map(
subjects,
function(subject, done) {
data.Subject.create(subject, done);
},
function(err, result) {
db.subjects = result;
next(err, db);
}
);
}
function createTrainingSubjects(data, db, next) {
var firstSubject = db.subjects[0];
var secondSubject = db.subjects[1];
var trainingSubjects = [
{
subject: firstSubject._id,
attendanceRate: [5, 5]
},
{
subject: secondSubject._id,
attendanceRate: [4, 4, 5]
}
];
data.async.map(
trainingSubjects,
function(trainingSubject, done) {
data.TrainingSubject.create(trainingSubject, done);
},
function(err, result) {
db.trainingSubjects = result;
next(err, db);
}
);
}
function showTrainingSubjects(data, next) {
data.TrainingSubject
.find({})
.populate('subject')
.exec(function(err, data) {
next();
});
}
function showSubjects(data, next) {
data.Subject.find({}, function(err, data) {
next();
});
}
function showTrainingEvents(data, next) {
data.TrainingEvent
.find({name: 'August Fest'})
.populate('user', 'displayName')
.populate('subjects.subject', 'name')
.exec(function(err, data) {
console.log(JSON.stringify(data));
next();
});
}
And the output is;
[
{
"_id":"55981d58cfd48c905c3a5fde",
"user":{
"_id":"55981d58cfd48c905c3a5fd8",
"displayName":"Santi"
},
"__v":0,
"subjects":[
{
"subject":{
"_id":"55981d58cfd48c905c3a5fdb",
"name":"Node.JS for Beginners"
},
"_id":"55981d58cfd48c905c3a5fdf",
"attendanceRate":[
4,
4,
5
]
}
],
"attendes":[
"55981d58cfd48c905c3a5fd7",
"55981d58cfd48c905c3a5fd8"
],
"created":"2015-0704T17:52:24.181Z",
"name":"August Fest"
}
]

Related

Mongoose - Get and Delete a subrecord

I have a model defined as so:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const feedbackSchema = new Schema({
Name: {
type: String,
required: true,
},
Email: {
type: String,
required: true,
},
Project: {
type: String,
required: true,
},
Wonder: {
type: String,
required: true,
},
Share: {
type: String,
required: true,
},
Delight: {
type: String,
required: true,
},
Suggestions: {
type: String,
required: true,
},
Rating: {
type: String,
required: true,
},
dateCreated: {
type: Date,
default: Date.now(),
},
user: {
type: Schema.Types.ObjectId,
ref: 'User'
}
});
const UserSchema = new Schema({
googleId: {
type: String
},
displayName: {
type: String
},
firstName: {
type: String
},
lastName: {
type: String
},
image: {
type: String
},
createdAt: {
type: Date,
default: Date.now(),
},
feedback: [feedbackSchema],
})
module.exports = mongoose.model("User", UserSchema);
An example document:
{
_id: ObjectId('60b9dc728a516a4669b40dbc'),
createdAt: ISODate('2021-06-04T07:42:01.992Z'),
googleId: '2342987239823908423492837',
displayName: 'User Name',
firstName: 'User',
lastName: 'Name',
image: 'https://lh3.googleusercontent.com/a-/89wf323wefiuhh3f9hwerfiu23f29h34f',
feedback: [
{
dateCreated: ISODate('2021-06-04T07:42:01.988Z'),
_id: ObjectId('60b9dc858a516a4669b40dbd'),
Name: 'Joe Bloggs',
Email: 'joe#bloggs.com',
Project: 'Some Project',
Suggestions: 'Here are some suggestions',
Rating: '10'
},
{
dateCreated: ISODate('2021-06-04T08:06:44.625Z'),
_id: ObjectId('60b9df29641ab05db7aa2264'),
Name: 'Mr Bungle',
Email: 'mr#bungle',
Project: 'The Bungle Project',
Suggestions: 'Wharghable',
Rating: '8'
},
{
dateCreated: ISODate('2021-06-04T08:08:30.958Z'),
_id: ObjectId('60b9df917e85eb6066049eed'),
Name: 'Mike Patton',
Email: 'mike#patton.com',
Project: 'No More Faith',
Suggestions: 'Find the faith',
Rating: '10'
},
],
__v: 0
}
I have two routes defined, the first one is called when the user clicked a button on a feedback item on the UI which takes the user to a "are you sure you want to delete this record"-type page displaying some of the information from the selected feedback record.
A second route which, when the user clicks 'confirm' the subrecord is deleted from the document.
The problem I'm having is I can't seem to pull the feedback from the user in order to select the document by id, here's what I have so far for the confirmation route:
router.get('/delete', ensureAuth, async (req, res) => {
try {
var url = require('url');
var url_parts = url.parse(req.url, true);
var feedbackId = url_parts.query.id;
const allFeedback = await User.feedback;
const feedbackToDelete = await allFeedback.find({ _id: feedbackId });
console.log(feedbackToDelete);
res.render('delete', {
imgSrc: user.image,
displayName: user.firstName,
feedbackToDelete
});
} catch (error) {
console.log(error);
}
})
Help much appreciated
Update
You should be able to do just this:
const feedbackToDelete = await User.feedback.find({ _id: feedbackId });
Or if feedbackId is just a string, which is appears to be, you may have to do something like:
// Create an actual _id object
// That is why in your sample doc you see ObjectId('foobarbaz')
const feedbackId = new mongoose.Types.ObjectId(url_parts.query.id);
const feedbackToDelete = await User.feedback.find({ _id: feedbackId });
Original
Shouldn't this:
const allFeedback = await User.feedback; (a field)
be this:
const allFeedback = await User.feedback(); (a method/function)
?

Delete the associated blog posts with user before deleting the respective user in mongodb and nodejs

When I am deleting a user, I also want to delete all the associated blog posts with that user. I have used MongoDB's pre() middleware. when it is fired it only sets the postedBy property to null in the post and then MongoDB compass the postedBy is still there along with userId
here is User schema.
const crypto = require("crypto");
const mongoose = require("mongoose");
const Post = require("./post");
const userSchema = mongoose.Schema(
{
username: {
type: String,
trim: true,
required: true,
unique: true,
index: true,
lowercase: true,
},
name: {
type: String,
index: true,
required: true,
max: 32,
},
email: {
type: String,
index: true,
required: true,
trim: true,
unique: true,
},
hashed_password: {
type: String,
required: true,
},
role: {
type: Number,
default: 0,
},
profile: {
type: String,
required: true,
},
photo: {
data: Buffer,
contentType: String,
},
salt: String,
resetPassword: {
data: String,
default: "",
},
},
{ timestamp: true }
);
userSchema
.virtual("password")
.set(function (password) {
this._password = password;
this.salt = this.makeSalt();
this.hashed_password = this.encryptPassword(password);
})
.get(function () {
return this._password;
});
userSchema.methods = {
authenticate: function (plainText) {
return this.encryptPassword(plainText) === this.hashed_password;
},
encryptPassword: function (password) {
if (!password) return "";
try {
return crypto
.createHmac("sha1", this.salt)
.update(password)
.digest("hex");
} catch (err) {
return "";
}
},
makeSalt: function () {
return Math.round(new Date().valueOf() * Math.random()) + "";
},
};
userSchema.pre("findByIdAndRemove", function (next) {
Post.deleteMany({ postedBy: this._id }, function (err, result) {
if (err) {
console.log("error");
} else {
console.log(result);
}
});
next();
});
module.exports = mongoose.model("User", userSchema);
here is Post schema
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const postSchema = new Schema({
postedBy: {
type: Schema.Types.ObjectId,
ref: "User",
},
title: {
type: String,
},
body: {
type: String,
},
name: {
type: String,
},
photo: {
data: Buffer,
contentType: String,
},
updated: Date,
avatar: {
type: String,
},
created: {
type: Date,
default: Date.now,
},
comments: [
{
postedBy: {
type: Schema.Types.ObjectId,
refPath: "onModel",
},
text: String,
created: {
type: Date,
default: Date.now,
},
},
],
likes: [
{
type: Schema.Types.ObjectId,
refPath: "onModel",
},
],
onModel: {
type: String,
enum: ["User", "Imam"],
},
});
module.exports = mongoose.model("Post", postSchema);
this is delete route function
exports.userdelete = (req, res) => {
User.findByIdAndRemove(req.params.id).exec((err, doc) => {
if (err) {
return res.status(400).json({
error: "Something went wrong",
});
}
return res.json({
message: "User deleted",
});
});
};
You Can follow this code
You can use cascade delete in mongoose
User.findOne({...}, function(err, customer) {
Post.remove();
});

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?

Mongoosejs : Populate doesn't return any data

Hi I'm new in mongoose and I have a problem with populate collection it doesn't return any data, I don't know why here's my code:
Company Model
'use strict';
/**
* Module dependencies.
*/
var mongoose = require('mongoose'),
Schema = mongoose.Schema,
Agent = mongoose.model('Agent'),
DBRef = mongoose.SchemaTypes.DBRef;
/**
* Company Schema
*/
var CompanySchema = new Schema({
name: {
type: String,
default: '',
//required: 'Please fill Company name',
trim: true
},
created: {
type: Date,
default: Date.now
},
updated: {
type: Date,
default: Date.now
},
address: {
type: String,
default: '',
//required: 'Please fill Company address',
trim: true
},
locked: {
type: Boolean,
default: true,
},
deleted: {
type: Boolean,
default: false,
},
logo: {
type: String,
default: '',
},
email: {
type: String,
default: '',
index: { unique: true }
//required: 'Please fill Company email',
},
tel: {
type: String,
default: '',
//required: 'Please fill Company tel',
},
fax: {
type: String,
default: '',
//required: 'Please fill Company fax',
},
type: {
type: String,
//required: 'Please fill Company type',
trim: true
},
description: {
type: String,
default: '',
trim: true
},
validator: {
type: Schema.ObjectId,
ref: 'User'
},
admins: [Agent]
});
module.exports = mongoose.model('Company', CompanySchema);
Agent Model
'use strict';
/**
* Module dependencies.
*/
var mongoose = require('mongoose'),
Schema = mongoose.Schema,
//Company = mongoose.model('Company'),
DBRef = mongoose.SchemaTypes.DBRef;
/**
* Agent Schema
*/
var AgentSchema = new Schema({
// Agent model fields
// ...
firstname: {
type: String,
default: ''
},
lastname: {
type: String,
default: ''
},
email: {
type: String,
default: '',
index: { unique: true }
},
password: {
type: String,
default: ''
},
roles: {
type: Array,
},
created: {
type: Date,
default: Date.now
},
updated: {
type: Date,
default: Date.now
},
deleted: {
type: Boolean,
default: false
},
locked: {
type: Boolean,
default: false
},
workFor: {
type: Schema.ObjectId,
ref: 'Company'
}
});
module.exports = mongoose.model('Agent', AgentSchema);
Populate Code
Company.findById(id).populate('admins').exec(function(err, company) {
if (err) return next(err);
if (!company) return next(new Error('Failed to load Company ' + id));
console.log(company.admins);
req.company = company ;
next();
});
thanks :) .
your schema definition of company actually defines that the agents are embedded into the company document, not referenced. If you want references that you then could populate you should use something like this:
var CompanySchema = new Schema({
....
admins: [{type: Schema.Types.ObjectId, ref: 'Agent'}],
....
thnks reto, here is the saving code of company :
/**
* Create a Company
*/
exports.create = function(req, res) {
var company = new Company(req.body);
User.findById('54e22d19aae0cff01a47df96',function(err,user){
if (err) {
return res.status(400).send({
messages: errorHandler.getErrorMessage(err)
});
}
company.user = user;
req.checkBody('name', 'please fill the name').notEmpty();
req.checkBody('address', 'please fill the address').notEmpty();
req.checkBody('tel', 'please fill a correct phone').notEmpty().isNumeric();
req.checkBody('type', 'please fill the type').notEmpty();
req.checkBody('email', 'please fill a correct email').isEmail();
req.checkBody('admin.firstname', 'please fill the admin firstname').notEmpty();
req.checkBody('admin.lastname', 'please fill the admin lastname').notEmpty();
req.checkBody('admin.email', 'please fill a correct admin email').isEmail();
req.checkBody('admin.password', 'please fill the admin password').notEmpty();
var errors = req.validationErrors();
if (errors) {
return res.status(400).send({
messages: errors
});
}else{
company.save(function(err) {
if (err) {
return res.status(400).send({
messages: errorHandler.getErrorMessage(err)
});
} else {
var agents = preapareAgents(req,company);
Agent.create(agents,function(err){
if(err){
return res.status(400).send({
messages: errorHandler.getErrorMessage(err)
});
}else{
res.jsonp({company: company, agents: agents});
}
});
}
});
}
});
};

How to save multilanguage data in moongoose?

i try to learn Mean.js and i need to use multilanguage support in my app.
I chose this schema to save data
"_id": {
"name": {
en: "English",
fr: "Français"
}
}
in my server model:
var KnowledgeSchema = new Schema({
name: {
en: {
type: String,
default: '',
required: 'Please fill Knowledge name',
trim: true
},
fr : {
type: String,
default: '',
required: 'Please fill Knowledge name',
trim: true
}
},
created: {
type: Date,
default: Date.now
},
user: {
type: Schema.ObjectId,
ref: 'User'
}
});
mongoose.model('Knowledge', KnowledgeSchema);
and controller
exports.create = function(req, res) {
var knowledge = new Knowledge(req.body);
knowledge.user = req.user;
knowledge.save(function(err) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
res.jsonp(knowledge);
}
});
};
What i need for save "en" and "fr" into "name" ?

Resources