MongoDB: How to populate an embedded reference - javascript

I'm unsure of how would I populate the questions field in the examBoard collection in the following example (I have made my example reasonably complicated on purpose so I can properly understand how it works).
examBoard schema:
var topicSchema = new mongoose.Schema({
name: String,
questions:[
{
type:mongoose.Schema.Types.ObjectId,
ref:"question"
}
],
});
var moduleSchema = new mongoose.Schema({
name: String,
topics: [topicSchema]
});
var examBoardSchema = new mongoose.Schema({
name: String,
modules: [moduleSchema]
});
module.exports = mongoose.model("examBoard", examBoardSchema);
question schema:
var partSchema = new mongoose.Schema({
mark: Number,
content: String
});
var questionSchema = new mongoose.Schema({
content: String,
mark:Number,
methods:[[partSchema]]
});
module.exports = mongoose.model("question", questionSchema);
How I thought I should do it:
examBoard.find()
.populate
({
path:"modules.topics.questions",
model:"question"
})
.exec(function(err,exam)
{
if(err)
{
console.log("Failed to populate");
}
else
{
console.log("exam[0].modules[0].topcis[0].questions\n"+exam.modules[0].topcis[0].questions);
}
});

Try this:
Exam
.find()
.exec()
.then((exams) => {
// Populate questions
Exam
.populate(exams, {
path: 'modules.topics.questions',
model: 'question'
})
.then((populatedExams) => {
// Do something with populated exams
});
});

Related

i can't populate mongoose deep subdocument

Below is the code that simplified the model and schema I'm having a hard time with
const guildSchema = new Schema<Guild>({
sheets: [sheetSchema],
crews: [crewSchema],
});
const GuildModel= getModel('Guild', guildSchema)
const sheetSchema = new Schema<Sheet>({
deales: [dealSchema]
})
const SheetModel = getModel('Guild.sheets', sheetSchema)
const dealSchema = new Schema<Deal>({
crew: [{ type: Schema.Types.ObjectId, refPath: 'Guild.crews' }],
damage: { type: Number, required: true },
})
const DealModel = getModel('Guild.sheets.deales', dealSchema)
const crewSchema = new Schema<Crew>({
name: { type: String, required: true },
})
const CrewModel= getModel('Guild.crews', crewSchema)
and this is Mocha-chai testcode what always throw exception
it("populated guild.sheets.deales.boss must have name",async () => {
const guild = await GuildModel.findOne({})
await guild.populate({
path: 'sheets.deales.crew'
}).execPopulate()
expect(guild.sheets[0].deales[0].crew).to.has.property("name") // expected [] to have property 'name'
})
None of the answers on stackoverflow solved my problem. I wasted 5 hours on just a few lines of this code. please help me
You checked this? https://github.com/Automattic/mongoose/issues/1377#issuecomment-15911192
This person changed nested code
var opts = {
path: 'author.phone',
select: 'name'
};
BlogPost.populate(docs, opts, function (err, docs) {
assert.ifError(err);
docs.forEach(function (doc) {
console.log(doc);
});
callback(null);
from this
var authors = docs.map(function(doc) {
return doc.author;
});
User.populate(authors, {
path: 'phone',
select: 'name'
}, callback);
to this.
author(User)is in BlogPost. BlogPost Schema has just User ObjectId, so can't understand author.phone
I might have already checked it, but I'm uploading it just in case.

Mongoose does not populate

I am trying to populate one of my models but it does not work.
This is my Card schema:
const CardSchema = new mongoose.Schema({
text: {
type: String,
},
wrap: {
type: String,
},
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
}
});
module.exports = mongoose.model('Card', CardSchema);
This is my controller:
exports.getCards = asyncHandler(async (req, res, next) => {
const cards = await Card.find({}).populate('user').exec();
res.status(200).json({
success: true,
count: cards.length,
data: cards,
});
});
It does return the cards but without any user field.
The user schema is exported as "User"
You have made a small mistake in defining the models while you referring the User Collections, remove the single quotes
The model definition should be as follows
const CardSchema = new mongoose.Schema({
text: {
type: String,
},
wrap: {
type: String,
},
user: {
type: mongoose.Schema.Types.ObjectId,
ref: User, // Replace 'User' with User
}
});
module.exports = mongoose.model('Card', CardSchema);

Do I have to define different mongodb models in separate files?

Recently, I have been working with mongodb with one single model. When I tried to add a second model, I noticed that I might face some issues.
First, here's the code with one single model:
riskRating.js
const mongoose = require('mongoose')
const Schema = mongoose.Schema;
let riskRatingRow = new Schema({
securitycause: {
type: String
},
operationalrisk: {
type: String
},
riskid: {
type: String
},
riskstatements: {
type: String
},
businessline: {
type: String
},
supportingasset: {
type: String
},
category: {
type: String
},
frequency: {
type: String
},
impact: {
type: String
},
inherentriskrating: {
type: String
},
controleffectiveness: {
type: String
},
residualrisk: {
type: String
}
});
module.exports = mongoose.model('riskRating', riskRatingRow);
Here's how I use it in the server code:
server.js
const RiskRatingRow= require('./models/riskRating');
router.route('/table').get((req, res) => {
RiskRatingRow.find((err, tableData) => {
if (err)
console.log(err);
else
res.json(tableData);
});
});
router.route('/table/add').post((req, res) => {
console.log('REQ.body is ', req.body);
const riskRatingRow = new RiskRatingRow(req.body);
riskRatingRow.save()
.then(issue => {
res.status(200).json({
'tableRow': 'Added successfully'
});
})
.catch(err => {
res.status(400).send('Failed to create new record');
});
});
First question: Is there anything wrong so far?
Now, when I add the second model:
twoModels.js
const mongoose = require('mongoose')
const Schema = mongoose.Schema;
let riskRatingRow = new Schema({
//1st model defintion
});
const businessLineDashboardRow = new Schema({
//2nd model defintion
});
module.exports = mongoose.model('businessLineDashboard', businessLineDashboardRow);
module.exports = mongoose.model('riskRating', riskRatingRow);
I have noticed that in server.js, when I am using the first model, I am not referencing it directly. I'm rather referecing the singleModel.js file. Particularly in these two lines:
const RiskRatingRow = require('./models/riskRating');
// Here I am using directly the file reference RiskRatingRow
const riskRatingRow = new RiskRatingRow(req.body);
// Same thing here
RiskRatingRow.find((err, tableData) => {
if (err)
console.log(err);
else
res.json(tableData);
});
So, when I was about to make use of the second model, I found myself blocked since as I explained when I used the first model, I didn't reference it directly. I just referenced the file.
Thing is, that actually works fine.
But, I don't know if the model file contains two models, how am I supposed to make use of them both in the server file.
So here's my two other questions:
1/ How come that code works even though I am just referecing the model defintion file?
2/ Should I define the second model in a separate file, and reference it in order to be able to use it?
Thank you, I hope I was clear enough.
module.exports can be an object containing multiple things as properties:
module.exports = {
RiskRatingRow: mongoose.model('businessLineDashboard', businessLineDashboardRow),
BusinessLineDashboardRow: mongoose.model('riskRating', riskRatingRow),
}
Since it's an empty object ({}) by default you can also assign the exports individually:
module.exports.RiskRatingRow = mongoose.model('businessLineDashboard', businessLineDashboardRow)
module.exports.BusinessLineDashboardRow = mongoose.model('riskRating', riskRatingRow)
You can now destructure the models out of the object inside server.js:
const { RiskRatingRow, BusinessLineDashboardRow } = require('./models/twoModels')
Or, if you want to do it the old-school way:
const models = require('./models/twoModels')
const RiskRatingRow = models.RiskRatingRow
const BusinessLineDashboardRow = models.BusinessLineDashboardRow
Niklas's answer is on point. However, I have found a more complete solution:
riskRating.js
const mongoose = require('mongoose')
const Schema = mongoose.Schema;
module.exports = function(mongoose) {
let riskRatingRow = new Schema({
securitycause: {
type: String
},
operationalrisk: {
type: String
},
riskid: {
type: String
},
riskstatements: {
type: String
},
businessline: {
type: String
},
supportingasset: {
type: String
},
category: {
type: String
},
frequency: {
type: String
},
impact: {
type: String
},
inherentriskrating: {
type: String
},
controleffectiveness: {
type: String
},
residualrisk: {
type: String
}
});
let businessLineDashboardRow = new Schema({
ref: {
type: String
},
riskstatements: {
type: String
},
maximpact: {
type: String
},
controleffectiveness: {
type: String
},
recommendedriskrating: {
type: String
},
frequency: {
type: String
},
impact: {
type: String
},
validatedreviewriskrating: {
type: String
},
rationalforriskadjustment: {
type: String
}
});
var models = {
BusinessLineDashboard : mongoose.model('BusinessLineDashboard', businessLineDashboardRow),
RiskRating : mongoose.model('RiskRating', riskRatingRow)
};
return models;
}
server.js
router.route('/riskRating2').get((req, res) => {
models.RiskRating.find((err, tableData) => {
if (err)
console.log(err);
else
res.json(tableData);
analyseDeRisqueService.determineMaxImpactForEachRisk(tableData)
});
});

Referenced Objects and Virtual getters returning undefined in pug layout file, but queried successfully from database

My problem is two-fold, but I think they are both caused by the same issue: my Schema definition and/or Model creation.
I am following this MDN Nodejs tutorial and i cannot figure out:
Why my referenced objects are returning undefined in my layout file, yet I have confirmed that the data is being correctly inserted and queried(as far as i can tell).
My virtual id getter is not working.
Schema Definition:
book.js italize
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const BookSchema = new Schema({
title: {type: String, required: true},
author: {type: Schema.Types.ObjectId, ref: 'Author', required: true},
summary: {type: String, required: true},
isbn: {type:String, required: true},
genre: [{type: Schema.Types.ObjectId, ref: 'Genre'}]
});
//this doesnt seem to be working. returns undefined on the
//object in layout file
BookSchema
.virtual('url')
.get(() => {
return '/catalog/book/' + this._id;
});
module.exports = mongoose.model('Book', BookSchema);
author.js italize
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const AuthorSchema = new Schema({
first_name: {type: String, required: true, max: 100},
family_name: {type: String, required: true, max: 100},
date_of_birth: {type: Date},
date_of_death: {type: Date}
});
//Virtuals
AuthorSchema.virtual('name')
.get(() => {
return this.family_name + ', ' + this.first_name;
});
//create virtual absolute url to obtain instance of this model
AuthorSchema.virtual('url')
.get(() => {
return '/catalog/author/' + this._id;
});
module.exports = mongoose.model('Author', AuthorSchema);
Model Creation:
function authorCreate(first_name, family_name, d_birth, d_death, cb) {
authordetail = {first_name:first_name , family_name: family_name }
if (d_birth != false) authordetail.date_of_birth = d_birth
if (d_death != false) authordetail.date_of_death = d_death
var author = new Author(authordetail);
author.save(function (err) {
if (err) {
cb(err, null)
return
}
console.log('New Author: ' + author);
authors.push(author._id)
cb(null, author)
} );
}
function bookCreate(title, summary, isbn, author, genre, cb) {
bookdetail = {
title: title,
summary: summary,
author: author,
isbn: isbn
}
if (genre != false) bookdetail.genre = genre
var book = new Book(bookdetail);
book.save(function (err) {
if (err) {
cb(err, null)
return
}
console.log('New Book: ' + book);
books.push(book._id)
cb(null, book)
} );
}
controller.js:
// Display list of all books.
exports.book_list = function(req, res) {
Book.find({})
.populate('author')//this seems to work
.exec(function (err, list_books) {
if (err) { return next(err); }
//Successful, so render
for(var b in list_books){
console.log('author id: ' + list_books[b].author._id);//as evidenced here
}
//but the author object is undefined when rendered
res.render('book_list', { title: 'Book List', book_list: list_books });
});
};
layout.pug:
extends layout
block content
h1= title
//url and author undefined here
ul
each book in book_list
li
a(href=book.url) #{book.title}
| #{book.author.name}
else
li There are no books.
Screenshot:
Tools:
Node + Express js
Pug
mLab free tier
I am completely new to Mongoose DB & Pug and know only as much as the primers in the tutorial have taught and explicitly pointed to as further reading.
Let me know if you need any more information. Thanks
I looks like my problem was usage of fat arrow functions. Changing those to regular function() syntax fixed the issue.
This answer confirmed that its a support issue in nodejs.

Updating a subdoc array in Mongoose without getting the parent

Schema:
var viewSchema = new Schema({
active: Boolean
, path: String
})
var pageSchema = new Schema({
name: String
, desc: String
, url: String
, views: [viewSchema]
})
In order to add a new view to an existing page I am doing:
Page
.findOne({ id: pageId })
.exec(function (err, page) {
page.views.push({ path: path })
page.save(function(err) {
//saved
})
})
Is there a way to do the same without actually getting the page?
Page.update({ id: pageId }
, { SOMETHING }
, function(err){
//updated
})
It works..
Page.update({ _id: page.id},{ $push: { "views": { path: "#page", active: true } } })
thanks #Alistair-Nelson

Categories