Referencing a schema, not a model - node.js

I want to reference to a subdocument which is defined as a schema. Here's the example:
exam.model.js:
const answerSchema = new mongoose.Schema({
text: String,
isCorrect: Boolean,
});
const questionSchema = new mongoose.Schema({
text: String,
answers: [answerSchema],
});
const examSchema = new mongoose.Schema({
title: String,
questions: [questionSchema],
})
const ExamModel = mongoose.model("Exam", examSchema);
//...export the schemas and ExamModel...
solvedExam.model.js:
const solvedExamSchema = new mongoose.Schema({
exam: {
type: mongoose.Schema.Types.ObjectId,
ref: "Exam",
}
answers: [{
question: {
type: mongoose.Schema.Types.ObjectId,
ref: //What do I put here? "question" is not a model, it's only a sub-document schema
}
answer: {
type: mongoose.Schema.Types.ObjectId,
ref: //Same problem
}
}],
});
So as it's obvious, I want to reference to the questions and answers which are only subdocuments as a schema and NOT models. How can I reference them? Thanks.

You should declare the respective Answer and Question Schema and refence those:
const answerSchema = new mongoose.Schema({
text: String,
isCorrect: Boolean,
});
const questionSchema = new mongoose.Schema({
text: String,
answers: [answerSchema],
});
const examSchema = new mongoose.Schema({
title: String,
questions: [questionSchema],
})
const AnswerModel = mongoose.model("Answer", examSchema);
const QuestionModel = mongoose.model("Question", examSchema);
const ExamModel = mongoose.model("Exam", examSchema);
...
const solvedExamSchema = new mongoose.Schema({
exam: {
type: mongoose.Schema.Types.ObjectId,
ref: "Exam",
}
answers: [{
question: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Question'
}
answer: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Answer'
}
}],
});

Related

how to populate nested array using mongoose

assuming i have this 2 schemas
company schema with an array of categories
//category
export const CategorySchema = new mongoose.Schema({
name: { type: String },
}, { timestamps: true });
//company
export const CompanySchema = new mongoose.Schema({
user: { type: Schema.Types.ObjectId, ref: 'User' },
name:String,
email:String,
categories:{type: [CategorySchema], default: [] },
}, { timestamps: true });
product schema with category as a ref to the category from company
export const ProductSchema =new mongoose.Schema({
name:String,
category:{ type: Schema.Types.ObjectId, ref: 'Category' },
}, { timestamps: true })
is it possible to populate category from the product ?
i tried this code and it's not working
const products=await this.productModel.find({}).populate({'path':"category","model":"Category"}) ``
what you need is just
productModel.find({}).populate("category")
try this code for populate
const products=await this.productModel.find({}).populate({'path':"category","model":"company.categories"})

How to populate a data for a field in mongoose, where reference is a nested array in model?

How can I populate category in courses model?
I have courses, courses will have category from categories->subcategories model.
I don't know how to populate from nested objects.
Reference model is category, I have to populate from array subcategories!
**courses:**
const CourseSchema = new mongoose.Schema({
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'users'
},
name: {
type: String
},
description: {
type: String
},
category: {
type: mongoose.Schema.Types.ObjectId,
ref: 'category'
}
});
**category:**
const mongoose = require("mongoose");
const CategorySchema = new mongoose.Schema({
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'users'
},
name: {
type: String,
required: true
},
subcategories: [{
name: {
type: String
}
}]
});
const Category = mongoose.model('category', CategorySchema);
module.exports = Category;
use seprate collection for subCetagory like:
**courses**
const CourseSchema = new mongoose.Schema({
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'users'
},
name: {
type: String
},
description: {
type: String
},
category: {
type: mongoose.Schema.Types.ObjectId,
ref: 'SubCategory'
}
});
**SubCategory:**
const mongoose = require("mongoose");
const SubCategorySchema = new mongoose.Schema({
name: {
type: String,
required: true
},
categorie: {
type: String,
ref: "category"
}
});
const SubCategory = mongoose.model('category', SubCategorySchema);
module.exports = SubCategory;
**Category**
const mongoose = require("mongoose");
const CategorySchema = new mongoose.Schema({
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'users'
},
name: {
type: String,
required: true
},
subcategories: [
{
type: Schema.types.objectId,
ref: "SubCategory"
}
]
});
then just add subcetagory id to course when insert new course or update a course and when you want to get course along with subcetagory and category use populate query of mongoose

mongoose populate makes the field empty

I have two schemas:
const categorySchema = Schema({
slug: {
type: String,
index: true
},
tags: {
type: [Schema.Types.ObjectId],
ref: 'Tag'
}
});
and:
const tagSchema = Schema({
title: String,
type: {
type: String,
enum: ['bool', 'selectable', 'int']
},
categories: [{
type: Schema.Types.ObjectId,
ref: 'Category'
}],
possibleValues: [String]
});
Now here is the problem. When I try to populate my category instance with tags, the whole field, becomes an empty array, while when there are no .populate() statements, there are some ObjectIds there. What is the problem?
Update: here is my query:
models.Category
.findOne({_id: req.params.categoryId})
.populate('tags')
.then(category => {
console.log(category);
res.send(category.tags);
});
Category Schema :
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var categorySchema = Schema({
slug: {type: String,index: true},
tags: { type: [Schema.Types.ObjectId],ref: 'Tag'}
},{
collection:'categories'
});
var Category = mongoose.model('Category', categorySchema);
Tag Schema :
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var tagSchema = Schema({
title: String,
type: {
type: String,
enum: ['bool', 'selectable', 'int']
},
categories: [{
type: Schema.Types.ObjectId,
ref: 'Category'
}],
possibleValues: [String]
},{
collection:'tags'
});
var Tag = mongoose.model('Tag', tagSchema);
Find Query :
Category.
findOne({_id: req.params.categoryId}).
populate('tags').
exec(function (err, categories) {
return res.json(categories);
});
I think you need to change the schema definition of category.tags to this:
tags: [{
type: Schema.Types.ObjectId,
ref: 'Tag'
}]

How to implement partial document embedding in Mongoose?

I have a simple relation between topics and categories when topic belongs to a category.
So schema looks like this:
const CategorySchema = new mongoose.Schema({
name: String,
slug: String,
description: String
});
And topic
const TopicSchema = new mongoose.Schema({
category: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Category'
},
title: String,
slug: String,
body: String,
created: {type: Date, default: Date.now}
});
I want to implement particular embedding of category into topic
{
category: {
_id: ObjectId('abc'),
slug: 'catslug'
},
title: "Title",
slug: "topictitle",
...
}
It will help me avoid unnecessary population and obtain performance bonuses.
I don't want to embed whole document because I want to changes categories sometimes (it is a rare operation) and maintain references.
Hope this helps, done it in my own project to save some RTTs in common use cases. Make sure you're taking care of both copies on update.
parent.model.js:
const mongoose = require('mongoose');
const childEmbeddedSchema = new mongoose.Schema({
_id: {type: mongoose.Schema.Types.ObjectId, ref: 'Child', auto: false, required: true, index: true},
someFieldIWantEmbedded: {type: String}
});
const parentSchema = new mongoose.Schema({
child: { type: childEmbeddedSchema },
moreChildren: { type: [{type: childEmbeddedSchema }] }
});
module.exports = mongoose.model('Parent', parentSchema);
child.model.js:
const mongoose = require('mongoose');
const childSchema = new mongoose.Schema({
someFieldIWantEmbedded: {type: String},
someFieldIDontWantEmbedded: {type: Number},
anotherFieldIDontWantEmbedded: {type: Date}
});
module.exports = mongoose.model('Child', childSchema);
parent.controller.js:
const mongoose = require('mongoose');
const Parent = require('path/to/parent.model');
exports.getAll = (req, res, next) => {
const query = Parent.find();
// only populate if requested! if true, will replace entire sub-document with fetched one.
if (req.headers.populate === 'true') {
query.populate({
path: 'child._id',
select: `someFieldIWantEmbedded ${req.headers.select}`
});
query.populate({
path: 'moreChildren._id',
select: `someFieldIWantEmbedded ${req.headers.select}`
});
}
query.exec((err, results) => {
if (err) {
next(err);
} else {
res.status(200).json(results);
}
});
};

Mongoose populate

Here is my test code which I can not figure out why it isn't working, as it is very similar to test 'populating multiple children of a sub-array at a time'.
var mongoose = require('mongoose'),
Schema = mongoose.Schema,
ObjectId = Schema.ObjectId;
mongoose.connect('mongodb://localhost/testy');
var UserSchema = new Schema({
name: String
});
var MovieSchema = new Schema({
title: String,
tags: [OwnedTagSchema]
});
var TagSchema = new Schema({
name: String
});
var OwnedTagSchema = new Schema({
_name: {type: Schema.ObjectId, ref: 'Tag'},
_owner: {type: Schema.ObjectId, ref: 'User'}
});
var Tag = mongoose.model('Tag', TagSchema),
User = mongoose.model('User', UserSchema),
Movie = mongoose.model('Movie', MovieSchema);
OwnedTag = mongoose.model('OwnedTag', OwnedTagSchema);
User.create({name: 'Johnny'}, function(err, johnny) {
Tag.create({name: 'drama'}, function(err, drama) {
Movie.create({'title': 'Dracula', tags:[{_name: drama._id, _owner: johnny._id}]}, function(movie) {
// runs fine without 'populate'
Movie.find({}).populate('tags._owner').run(function(err, movies) {
console.log(movies);
});
});
})
});
Produced error is
node.js:201
throw e; // process.nextTick error, or 'error' event on first tick
^
TypeError: Cannot call method 'path' of undefined
at /Users/tema/nok/node_modules/mongoose/lib/model.js:234:44
Update
Got rid from OwnedTag and rewrote MovieSchema like this
var MovieSchema = new Schema({
title: String,
tags: [new Schema({
_name: {type: Schema.ObjectId, ref: 'Tag'},
_owner: {type: Schema.ObjectId, ref: 'User'}
})]
});
Working code https://gist.github.com/1541219
Your variable OwnedTagSchema must be defined before you use it or you'll end up doing basically this:
var MovieSchema = new Schema({
title: String,
tags: [undefined]
});
Move it above MovieSchema definition.
I would expect your code to work, too. Does it work if you put the OwnedTag right in MovieSchema, like so?
var MovieSchema = new Schema({
title: String,
tags: [{
_name: {type: Schema.ObjectId, ref: 'Tag'},
_owner: {type: Schema.ObjectId, ref: 'User'}
}]
});
edit:
var MovieSchema = new Schema({
title: String,
tags: [{ type: Schema.ObjectId, ref: 'OwnedTag' }]
});

Resources