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'
}
}],
});
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'
}]
The following code returns this error: doc.validate is not a function
. I read that markModified should fix this...but it hasn't. What is wrong with this code?
StoreRequest.findOne({store: store._id}).populate('store').then(function(storeRequest) {
[0, 1, 2, 3, 4, 5].forEach(function(i) {
var newProductUrl = {stuff in here, including nested docs and arrays};
storeRequest.products[i] = newProductUrl;
storeRequest.markModified('products');
})
storeRequest.save().then(function(sr) {
return res.json({sr: sr});
})
}).catch(next)
Here is the StoreRequestSchema:
StoreRequest.js
var DataSchema = new mongoose.Schema({ ...
});
var ProductRequestSchema = new mongoose.Schema({
url: String,
product: { type: mongoose.Schema.Types.ObjectId, ref: 'Product'},
requests: [{type: mongoose.Schema.Types.ObjectId, ref: 'Request'}],
deal: { type: mongoose.Schema.Types.ObjectId, ref: 'Deal' },
data: DataSchema,
});
var ProductMatchSettingsSchema = new mongoose.Schema({
...
});
var SiteMapSchema = new mongoose.Schema({
...
});
var SpecialRequestSchema = new mongoose.Schema({
...
});
var StoreRequestSchema = new mongoose.Schema({
store: { type: mongoose.Schema.Types.ObjectId, ref: 'Store', unique: true},
products: [ProductRequestSchema],
siteMap: SiteMapSchema,
special: SpecialRequestSchema,
garbageUrls: [String] //Urls that have previously been thrown out
});
Im receiving a maximum call stack size exceeded error while working with mongoose and Nodejs. here is the error
RangeError: Maximum call stack size exceeded
at model.Document.$toObject (/Users/joncorrin/Desktop/workspace/MEAN/atlas-web/node_modules/mongoose/lib/document.js:2045:24)
at model.Document.toJSON (/Users/joncorrin/Desktop/workspace/MEAN/atlas-web/node_modules/mongoose/lib/document.js:2362:15)
at clone (/Users/joncorrin/Desktop/workspace/MEAN/atlas-web/node_modules/mongoose/lib/utils.js:252:18)
at cloneArray (/Users/joncorrin/Desktop/workspace/MEAN/atlas-web/node_modules/mongoose/lib/utils.js:362:14)
at clone (/Users/joncorrin/Desktop/workspace/MEAN/atlas-web/node_modules/mongoose/lib/utils.js:247:12)
at cloneObject (/Users/joncorrin/Desktop/workspace/MEAN/atlas-web/node_modules/mongoose/lib/utils.js:343:13)
at clone (/Users/joncorrin/Desktop/workspace/MEAN/atlas-web/node_modules/mongoose/lib/utils.js:260:16)
at model.Document.$toObject (/Users/joncorrin/Desktop/workspace/MEAN/atlas-web/node_modules/mongoose/lib/document.js:2092:13)
I believe this means that I'm causing an infinite loop somewhere, but I am unsure what is causing the error. The route worked correctly, then I added the mongoose-autopopulate plugin to the app. When sending a POST correctly with a token to my drive post route, I recieve this error. Nothing is logged and the server stops.
Here is an example of my route in drive.js
router.post('/', function(req, res, next){
var decoded = jwt.decode(req.query.token);
User.findById(decoded.user._id, function(err, user){
if (err) {
return res.status(500).json({
title: 'There was a server error',
error: err
});
}
var drive = new Drive({
startAddress: req.body.startAddress,
endAddress: req.body.endAddress,
tripDate: req.body.tripDate,
tripHour: req.body.tripHour,
price: req.body.price,
numberOfPassengers: req.body.numberOfPassengers,
user: user
});
drive.save(function (err, result) {
if(err) {
return res.status(500).json({
title: 'There was an error saving the drive collection',
error: err
});
}
user.drives.push(result);
user.save();
res.status(201).json({
message: 'Drive saved',
obj: result
});
});
});
});
And here is the related model
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var User = require('./user.model');
var Trip = require('./trip.model');
var autoPopulate = require('mongoose-autopopulate');
var driveSchema = new Schema({
startAddress: {type: String, required: true, lowercase: true},
endAddress: {type: String, required: true, lowercase: true},
tripDate: {type: Date, required: true},
tripHour: {type: String, required: true},
price: {type: Number, required: true},
numberOfPassengers: {type: Number, required: true},
trip: {type: Schema.Types.ObjectId, ref: 'Trip', autopopulate: true},
user: {type: Schema.Types.ObjectId, ref: 'User', autopopulate: true}
});
driveSchema.post('remove', function(drive) {
User.findById(drive.user, function(err, user) {
user.drives.pull(drive);
user.save();
});
});
driveSchema.plugin(autoPopulate);
module.exports = mongoose.model('Drive', driveSchema);
All of my models follow the same methods and queries. Is there anything in specific I am doing wrong? I looked it up and it seems that I could be calling an instance instead of JSON which breaks the code, but Im not experienced enough to identify where that instance is, or whats causing a reocurring call as .find() or .where() that I am using that is breaking it.
Here are my other models
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var autopopulate = require('mongoose-autopopulate');
var tripSchema = new Schema({
tripActivated: {type: Boolean},
tripCompleted: {type: Boolean},
driver: {type: Schema.Types.ObjectId, ref: 'Drive', autopopulate: true},
riders: [{type: Schema.Types.ObjectId, ref: 'Ride', autopopulate: true}],
comments: [{type: Schema.Types.ObjectId, ref: 'Comment', autopopulate: true}]
});
tripSchema.post('remove', function(trip) {
User.findById(trip.driver.user, function(err, user) {
user.trips.pull(trip);
user.save();
});
});
// tripSchema.post('remove', function(trip) {
// User.findById(trip.riders.user, function(err, user) {
// user.trips.pull(trip);
// user.save();
// });
// });
tripSchema.plugin(autopopulate);
module.exports = mongoose.model('Trip', tripSchema);
//// NEW MODEL
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var User = require('./user.model');
var Trip = require('./trip.model');
var autoPopulate = require('mongoose-autopopulate');
var rideSchema = new Schema({
startAddress: {type: String, required: true, lowercase: true},
endAddress: {type: String, required: true, lowercase: true},
tripDate: {type: Date, required: true},
tripHour: {type: String, required: true},
numberOfPassengers: {type: Number, required: true},
trip: {type: Schema.Types.ObjectId, ref: 'Trip', autopopulate: true},
user: {type: Schema.Types.ObjectId, ref: 'User', autopopulate: true}
});
rideSchema.post('remove', function(ride) {
User.findById(ride.user, function(err, user) {
user.rides.pull(ride);
user.save();
});
});
rideSchema.plugin(autoPopulate);
module.exports = mongoose.model('Ride', rideSchema);
////// NEW MODEL
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var User = require('./user.model');
var autoPopulate = require('mongoose-autopopulate');
var requestSchema = new Schema({
typeOfRequest: {type: String, required: true},
driver: {type: Schema.Types.ObjectId, ref: 'Drive', autopopulate: true},
rider: {type: Schema.Types.ObjectId, ref: 'Ride', autopopulate: true}
});
requestSchema.post('remove', function(request) {
User.findById(request.user, function(err, user) {
user.requests.pull(request);
user.save();
});
});
requestSchema.plugin(autoPopulate);
module.exports = mongoose.model('Request', requestSchema);
//// NEW MODEL
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var autoPopulate = require('mongoose-autopopulate');
var User = require('./user.model');
var messageSchema = new Schema ({
content: {type: String, required: true},
receiver: {type: Schema.Types.ObjectId, ref: 'User', autopopulate: true },
user: {type: Schema.Types.ObjectId, ref: 'User', autopopulate: true}
});
messageSchema.post('remove', function(message) {
User.findById(message.user, function(err, user) {
user.messages.pull(message);
user.save();
});
});
messageSchema.plugin(autoPopulate);
module.exports = mongoose.model('Message', messageSchema);
Upon diving deeper, my error seems to stem from this code that is starred in the mongoose source code in node_modules
Document.prototype.$toObject = function(options, json) {
***var defaultOptions = {
transform: true,
json: json,
retainKeyOrder: this.schema.options.retainKeyOrder,
flattenDecimals: true
};***
// _isNested will only be true if this is not the top level document, we
// should never depopulate
if (options && options.depopulate && options._isNested && this.$__.wasPopulated) {
// populated paths that we set to a document
return clone(this._id, options);
}
// When internally saving this document we always pass options,
// bypassing the custom schema options.
if (!(options && utils.getFunctionName(options.constructor) === 'Object') ||
(options && options._useSchemaOptions)) {
if (json) {
options = this.schema.options.toJSON ?
clone(this.schema.options.toJSON) :
{};
options.json = true;
options._useSchemaOptions = true;
} else {
options = this.schema.options.toObject ?
clone(this.schema.options.toObject) :
{};
options.json = false;
options._useSchemaOptions = true;
}
}
for (var key in defaultOptions) {
if (options[key] === undefined) {
options[key] = defaultOptions[key];
}
}
('minimize' in options) || (options.minimize = this.schema.options.minimize);
// remember the root transform function
// to save it from being overwritten by sub-transform functions
var originalTransform = options.transform;
options._isNested = true;
var ret = clone(this._doc, options) || {};
if (options.getters) {
applyGetters(this, ret, 'paths', options);
// applyGetters for paths will add nested empty objects;
// if minimize is set, we need to remove them.
if (options.minimize) {
ret = minimize(ret) || {};
}
}
if (options.virtuals || options.getters && options.virtuals !== false) {
applyGetters(this, ret, 'virtuals', options);
}
if (options.versionKey === false && this.schema.options.versionKey) {
delete ret[this.schema.options.versionKey];
}
var transform = options.transform;
// In the case where a subdocument has its own transform function, we need to
// check and see if the parent has a transform (options.transform) and if the
// child schema has a transform (this.schema.options.toObject) In this case,
// we need to adjust options.transform to be the child schema's transform and
// not the parent schema's
if (transform === true ||
(this.schema.options.toObject && transform)) {
var opts = options.json ? this.schema.options.toJSON : this.schema.options.toObject;
if (opts) {
transform = (typeof options.transform === 'function' ? options.transform : opts.transform);
}
} else {
options.transform = originalTransform;
}
if (typeof transform === 'function') {
var xformed = transform(this, ret, options);
if (typeof xformed !== 'undefined') {
ret = xformed;
}
}
return ret;
};
I set mongoose.set('debugger', true); to get a better error. When attempting to post on ride.js, the app registers the POST request, finds the userID, handles the new Ride (based off model), inserts the ride into the database, and then crashes immediately after.
here is the error with mongoose logs
Mongoose: users.ensureIndex({ email: 1 }, { unique: true, background: true })
Successfully connected to localhost:27017/atlas
Mongoose: users.findOne({ _id: ObjectId("59506e1629cdff044664f21c") }, { fields: {} })
(node:1219) DeprecationWarning: Mongoose: mpromise (mongoose's default promise library) is deprecated, plug in your own promise library instead: http://mongoosejs.com/docs/promises.html
Mongoose: rides.insert({ startAddress: 's', endAddress: 's', tripDate: new Date("Fri, 22 Feb 2222 00:00:00 GMT"), tripHour: '8', numberOfPassengers: 2, user: ObjectId("59506e1629cdff044664f21c"), _id: ObjectId("595078cf0b46e704c3091070"), __v: 0 })
events.js:160
throw er; // Unhandled 'error' event
^
RangeError: Maximum call stack size exceeded
at model.Document.$toObject (/Users/joncorrin/Desktop/workspace/MEAN/atlas-web/node_modules/mongoose/lib/document.js:2045:24)
at model.Document.toJSON (/Users/joncorrin/Desktop/workspace/MEAN/atlas-web/node_modules/mongoose/lib/document.js:2362:15)
at clone (/Users/joncorrin/Desktop/workspace/MEAN/atlas-web/node_modules/mongoose/lib/utils.js:253:18)
at cloneArray (/Users/joncorrin/Desktop/workspace/MEAN/atlas-web/node_modules/mongoose/lib/utils.js:363:14)
at clone (/Users/joncorrin/Desktop/workspace/MEAN/atlas-web/node_modules/mongoose/lib/utils.js:247:12)
at cloneObject (/Users/joncorrin/Desktop/workspace/MEAN/atlas-web/node_modules/mongoose/lib/utils.js:344:13)
at clone (/Users/joncorrin/Desktop/workspace/MEAN/atlas-web/node_modules/mongoose/lib/utils.js:261:16)
at model.Document.$toObject (/Users/joncorrin/Desktop/workspace/MEAN/atlas-web/node_modules/mongoose/lib/document.js:2092:13)
at model.Document.toJSON (/Users/joncorrin/Desktop/workspace/MEAN/atlas-web/node_modules/mongoose/lib/document.js:2362:15)
at clone (/Users/joncorrin/Desktop/workspace/MEAN/atlas-web/node_modules/mongoose/lib/utils.js:253:18)
at cloneObject (/Users/joncorrin/Desktop/workspace/MEAN/atlas-web/node_modules/mongoose/lib/utils.js:344:13)
at clone (/Users/joncorrin/Desktop/workspace/MEAN/atlas-web/node_modules/mongoose/lib/utils.js:261:16)
at model.Document.$toObject (/Users/joncorrin/Desktop/workspace/MEAN/atlas-web/node_modules/mongoose/lib/document.js:2092:13)
at model.Document.toJSON (/Users/joncorrin/Desktop/workspace/MEAN/atlas-web/node_modules/mongoose/lib/document.js:2362:15)
at clone (/Users/joncorrin/Desktop/workspace/MEAN/atlas-web/node_modules/mongoose/lib/utils.js:253:18)
at cloneArray (/Users/joncorrin/Desktop/workspace/MEAN/atlas-web/node_modules/mongoose/lib/utils.js:363:14)
**********UPDATE
After doing some digging, this is the code thats breaking the app
user.drives.push(result); <----------
user.save();
Its the inserting the data into mongodb and then when it tries to push to the user, it breaks. Any idea why?I added my user model for reference.
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var mongooseUniqueValidator = require('mongoose-unique-validator');
var autoPopulate = require('mongoose-autopopulate');
var userSchema = new Schema({
email: {type: String, required: true, unique: true, lowercase:true},
password: {type: String, required: true},
profileImgUrl: {type: String},
fName: {type: String},
lName: {type: String},
yearOfBirth: {type: String},
gender: {type: String},
ratings: [{type: Number}],
comments: [{type: Schema.Types.ObjectId, ref: 'Comment', autopopulate: true}],
messages: [{type: Schema.Types.ObjectId, ref: 'Message', autopopulate: true}],
rides: [{type: Schema.Types.ObjectId, ref: 'Ride', autopopulate: true}],
drives: [{type: Schema.Types.ObjectId, ref: 'Drive', autopopulate: true}],
requests: [{type: Schema.Types.ObjectId, ref: 'Request', autopopulate: true}],
trips: [{type: Schema.Types.ObjectId, ref: 'Trip', autopopulate: true}]
});
userSchema.plugin(mongooseUniqueValidator);
userSchema.plugin(autoPopulate);
module.exports = mongoose.model('User', userSchema);
The issue was in my user model. I was using the mongoose-autopopulate plugin and setting autopopulate to true objects that had a user instance in them.
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var mongooseUniqueValidator = require('mongoose-unique-validator');
var autoPopulate = require('mongoose-autopopulate');
var userSchema = new Schema({
email: {type: String, required: true, unique: true, lowercase:true},
password: {type: String, required: true},
profileImgUrl: {type: String},
fName: {type: String},
lName: {type: String},
yearOfBirth: {type: String},
gender: {type: String},
ratings: [{type: Number}],
comments: [{type: Schema.Types.ObjectId, ref: 'Comment', autopopulate: true}],
messages: [{type: Schema.Types.ObjectId, ref: 'Message', autopopulate: true}],<---------
rides: [{type: Schema.Types.ObjectId, ref: 'Ride', autopopulate: true}],<---------
drives: [{type: Schema.Types.ObjectId, ref: 'Drive', autopopulate: true}],<-----------
requests: [{type: Schema.Types.ObjectId, ref: 'Request', autopopulate: true}],
trips: [{type: Schema.Types.ObjectId, ref: 'Trip', autopopulate: true}]
});
userSchema.plugin(mongooseUniqueValidator);
userSchema.plugin(autoPopulate);
module.exports = mongoose.model('User', userSchema);
I was also setting autopopulate to true on those models. This was calling an infinite loop between populating the model given, and the user model.
If anyone is having this issue. Don't be like me and post a bunch of code on stack overflow. Figure out what you're calling that's recalling the call. In my case, I was calling autopopulate on two models that would communicate back and forth.
I have the schema which refers to another object.
model/message.js
var moogoose = require('mongoose');
var Schema = moogoose.Schema;
var schema = new Schema({
.
user: {type: Schema.Types.ObjectId, ref: 'User'}
.
});
module.exports = moogoose.model('Message', schema);
Suppose user is the object extracted from database.
Should I assign user._id or user object to user property of message model. I have tried both, they yield same result that is _id as value of user property.
var message = new Message({
user: user._id,
});
or:
var message = new Message({
user: user
});
Here is one of the schema that I use.
var mongoose=require('mongoose');
var config=require('./config'); //configuarations and APIS keys stored in this file
mongoose.connect(config.MONGO_URL, {server:{poolSize:10}});
var Schema = mongoose.Schema, ObjectId = Schema.ObjectId;
var users = new mongoose.Schema({
mobile: { type: String, unique: true },
hash: String,
location:{ type:[Number, Number], index:'2d'},
locName: String,
created_at: Number,
image: {type: String, default: 'NONE'},
role: {type: String, default: 'USER'},
name: {type: String, default: 'NONE'},
});
exports.users = mongoose.model('users', users);
var comments = new mongoose.Schema({
uid: { ObjectId, ref: 'users'},
liked_by: {type: [ObjectId], default: []},
like_count: {type: Number, default: 0},
created_at: Number
});
comments.index({spark_id:1, created_at:1});
exports.comments = mongoose.model('comments', comments);
This is one of the functions
dbase.comments.find({uid: req.body.user._id, function(err, response){
if(!err){
//do what you want
}else{
//handle error
}
}).sort({created_at: -1}).skip(count).limit(50).lean();