Mongoose and ExpressJS - modular architecture? - mongodb

I want to have each file for each model - i.e. user and country.
Currently, I solved this the following way:
person.js
/*jslint node: true */
'use strict';
var mongoose = require('./mongoose'),
Schema = mongoose.Schema;
var userSchema = new Schema({
name: String,
username: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
admin: Boolean,
location: String,
meta: {
age: Number,
website: String
},
created_at: Date,
updated_at: Date,
country: {
type: Schema.Types.ObjectId,
ref: 'Country'
}
});
userSchema.methods.toString = function() {
return `I am ${this.username} and I'm from ${this.country.name}`;
}
module.exports = mongoose.model('Person', userSchema);
country.js
/*jslint node: true */
'use strict';
var mongoose = require('./mongoose'),
Schema = mongoose.Schema;
var countrySchema = new Schema({
name: {
type: String,
required: true,
unique: true
},
created_at: Date,
updated_at: Date,
people: [{
type: Schema.Types.ObjectId,
ref: 'Person'
}]
});
var Country = mongoose.model('Country', countrySchema);
module.exports = Country;
So my question is as follows: how does the mongoose reference two tables? For example, in Person schema, I reference the country by providing ref: 'Country', but I never included the model in the Person.js file. Am I doing something wrong here?
UPDATE:
Taking the below answer into the consideration, I now have 3 files:
database.js
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/people');
mongoose.set('debug', true);
module.exports = mongoose;
person.js
/*jslint node: true */
'use strict';
var mongoose = require('./database'),
Schema = mongoose.Schema;
var userSchema = new Schema({
name: String,
username: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
admin: Boolean,
location: String,
meta: {
age: Number,
website: String
},
created_at: Date,
updated_at: Date,
country: {
type: Schema.Types.ObjectId,
ref: 'Country'
}
});
userSchema.methods.toString = function() {
return `I am ${this.username} and I'm from ${this.country.name}`;
}
mongoose.model('Person', userSchema);
country.js
/*jslint node: true */
'use strict';
var mongoose = require('./mongoose'),
Schema = mongoose.Schema;
var countrySchema = new Schema({
name: {
type: String,
required: true,
unique: true
},
created_at: Date,
updated_at: Date,
people: [{
type: Schema.Types.ObjectId,
ref: 'Person'
}]
});
mongoose.model('Country', countrySchema);
Now when I include all these 3 files into a test file, e.g.:
var mongoose = require('./database'),
User = mongoose.model('Person'),
Country = mongoose.model('Country');
I get the following error: MissingSchemaError: Schema hasn't been registered for model "Person". I get the same error for Country if I delete the User variable. It appears as if mongoose is trying to define the model, not retrieve it. What seems to be the problem?
UPDATE 2:
OK, when I include now these documents, I do the following:
require('./person');
require('./country');
var User = require('mongoose').model('Person'),
Country = require('mongoose').model('Country');

Related

Mongoose Populate with Express, not working in production (Heroku)

This is a MERN app, hosted on github, and it works perfectly on localhost. Unfortunately, it does not work on Heroku.
The issue is the API request, it must return an object and populate an array of OIDs (see Department Model). API request is working. I'm getting the data from MLab, but it doesn't populate... instead returns: "surveys":[]
API File
router.get('/department_data/:d_oid', function(req, res) {
Department.findOne({_id: req.params.d_oid}).populate("surveys").exec(function(err,doc){
if(err) throw(err)
res.send(doc)
})
});
Department Model
**Department Model**
var mongoose = require("mongoose");
var Schema = mongoose.Schema;
// Create the survey schema
var departmentSchema = new Schema({
department_name: {
type: String,
trim: true,
required: true
},
surveys: [{
type: Schema.Types.ObjectId,
ref: 'Surveys'
}],
participants: [{
type: String
}],
create_date: {
type: Date,
default: Date.now
},
created_by: {
type: Schema.Types.ObjectId,
ref: 'Created_By'
},
});
departmentSchema.index({ department_name: 1, created_by: 1}, {unique: true});
const Department = mongoose.model('Departments', departmentSchema);
module.exports = Department;
Survey Model
var mongoose = require("mongoose");
var Schema = mongoose.Schema;
// Create the survey schema
var surveySchema = new Schema({
survey_name: {
type: String,
trim: true,
required: true
},
questions: [{
type: Schema.Types.ObjectId,
ref: 'Questions'
}],
created_date: {
type: Date,
default: Date.now
}
});
const Survey = mongoose.model('Surveys', surveySchema);
module.exports = Survey;
Solved.
The problem was in the database: the ref OIDs got scrambled with a previous update, so when it was trying to populate, Mongoose couldn't find any matching OIDs.
Solution: we had to purge and re-seed. When the correct OID references exist, this code works as expected in localhost & Heroku.

Mongoose Populate a field

I have two mongoose schema
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var itemSchema = new Schema({
name: {type: String, required: true, max: 25, trim: true},
price: {type: Number, required: true, trim: true, default: 0},
tax: {
type: Schema.Types.ObjectId,
ref: "Store"
}
});
module.exports = mongoose.model('Item', itemSchema);
The second Schema
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var storeSchema = new Schema({
name: {type: String, required: true, trim: true},
taxes: [
{
name: String,
rate: Number
}
]
});
module.exports = mongoose.model('Store', storeSchema);
What I want to do is populate the itemSchema tax object with the storeSchema taxes array of object. every time I pushed a new tax object to the taxes array mongoose created an ObjectId. I stored that ObjectId in my itemSchema Tax. I want to use that _id to retrieve the store taxes that matches the itemSchema _id.
I have tried this so far, but I get no in the tax attribute.
Item.find().populate("tax", "taxes").exec(function (err, docs) {
if (err) return console.error(err);
console.log(items);
});
Try this query
Item.find().populate({
path: 'tax',
select: 'taxes'
}).exec(function (err, docs) {
if (err) {
console.error(err);
} else {
console.log(docs);
}
});
Item.find().populate(path:"tax", model: )
Mention your item model file name... don't use "" or '' for the file name, simply add the file name.
Use Item.find().populate("Store", "taxes") instead of Item.find().populate("tax", "taxes").

Define array of objects based on a mongoose schema

I have the following schema in mongoose that I would like to have as a nested array in another schema:
var NestedSchema = new Schema({
name: { type: String, required: true }
});
Some other schema that needs and array of the nested schema.
var EventSchema = new Schema({
name: { type: String, required: true, unique: true },
fields: [NestedSchema]
});
Which works just fine. However now I want to run some validation against that array.
var validators = // some validators
var EventSchema = new Schema({
name: { type: String, required: true, unique: true },
fields: [{ type: 'NestedSchema', required: true, validate: validators }]
});
Of course type: 'NestedSchema' does not work, it was a shoot in the dark. Does mongoose allow you to have an array of object based on schema
Using:
MongoDB shell version: 3.2.12,
Mongoose : 4.4.7
I managed to have an array based on schema that way:
var NestedSchema = new Schema({
name: { type: String, required: true }
});
var EventSchema = new Schema({
name: { type: String, required: true, unique: true },
fields: [{ type: [NestedSchema], required: true }]
});
Did not try validation, but I believe it will work as required works well. :)

Adding child document to existing mongodb document

Essentially I am just trying to add a new sub-document to my existing mongodb document that has the following schema
/models/server/destination.js
// this is the "destination" model for mongoose
var mongoose = require('mongoose')
var Adventure = require('../models/adventure')
// this is the schema that every entry will get when a new trip is made.
var tripSchema = mongoose.Schema({
name: { type: String, required: true },
city: { type: String, required: true },
dateStart: { type: Date, required: true },
dateFinish: { type: Date, required: true },
adventures: [Adventure]
})
// module.exports makes this model available to other file
module.exports = mongoose.model('Destination', tripSchema)
/server/models/adventure.js
var mongoose = require('mongoose')
var adventure = mongoose.Schema({
site: String,
rating: String,
photo: String,
website: String,
date: Date
})
module.exports = mongoose.model('Adventure', adventure)
REST route to post to adventures
app.post('/api/destinations/:id/addAdventures', destinationsController.addAdventures)
/server/controllers/controller.js
module.exports.addAdventures = function(req, res) {
var id = req.params.id;
Destination.findOne({ _id: id }, function(err, result) {
var adventure = new Adventure(req.body)
var destination = result
destination.adventures.push(adventure)
destination.save(function(err, advresult) {
console.log('push worked')
res.json(advresult);
})
})
}
When I take the adventure out of the destination.adventures.push() the code does not break, but when I insert adventures I get an error
/Travellog/node_modules/mongoose/lib/types/array.js:117
return this._schema.caster.cast(value, this._parent, false);
^ TypeError: undefined is not a function
at Array.MongooseArray.mixin._cast (/Travellog/node_modules/mongoose/lib/types/array.js:117:32)
at Array.MongooseArray.mixin._mapCast (/Travellog/node_modules/mongoose/lib/types/array.js:286:17)
at Object.map (native)
at Array.MongooseArray.mixin.push (/Travellog/node_modules/mongoose/lib/types/array.js:299:25)
at Query.<anonymous> (/Travellog/server/controllers/destinations-controller.js:28:28)
at /Travellog/node_modules/mongoose/node_modules/kareem/index.js:177:19
at /Travellog/node_modules/mongoose/node_modules/kareem/index.js:109:16
at process._tickCallback (node.js:355:11)
The error you are getting is as a result of embedding the Adventure model instead of the schema. You need to add the Adventure schema in the destination schema definition the Adventure model's schema property:
// this is the "destination" model for mongoose
var mongoose = require('mongoose');
var AdventureSchema = require('../models/adventure').schema; /* <- access the schema via its Model.schema property */
var tripSchema = mongoose.Schema({
name: { type: String, required: true },
city: { type: String, required: true },
dateStart: { type: Date, required: true },
dateFinish: { type: Date, required: true },
adventures: [AdventureSchema]
});

Population to sub-scheme in Mongoose

I have two schemas:
Clinic:
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var ProcedureSchema = mongoose.Schema({
name: {
type: String,
trim: true,
required: true
},
alias: {
type: String,
trim: true,
required: true
}
});
var ClinicSchema = mongoose.Schema({
name: {
type: String,
trim: true,
required: true
},
procedures: [ProcedureSchema]
});
module.exports = mongoose.model('Clinic', ClinicSchema);
and Record:
var mongoose = require('mongoose'),
Patient = require('./patient'),
User = require('./user'),
Clinic = require('./clinic'),
Schema = mongoose.Schema;
var RecordSchema = Schema({
doctor: {
type: Schema.Types.ObjectId,
ref: 'User'
},
clinic: {
type: Schema.Types.ObjectId
},
date: {
type: Date
},
patient: {
type: Schema.Types.ObjectId,
ref: 'Patient'
},
procedure: {
type: [Schema.Types.ObjectId],
ref: 'Clinic'
}
});
module.exports = mongoose.model('Record', RecordSchema);
In record schema i store all ids of procedure, which sub-scheme for Clinic
I want to get full object of procedures in record.
I try this query:
Record.find({}).
populate('procedures.procedure').
populate('doctor').
populate('patient').
exec(function(err, records) {
...
});
But get only array of ids, instead array of objects.
Where is problem?
You totally mix all schemes:
populate('procedures.procedure')
But you have not procedures in RecordSchema. Even if it is type mistake, an you mean procedure.procedures - you don't have procedures in ProcedureSchema.
read more about references in MongoDB especially http://docs.mongodb.org/manual/applications/data-models-tree-structures/
Try to make nesting path less than 2. Something like this:
var User,
Procedure,
Clinic,
Patient,
Record;
function defineModels(mongoose, fn) {
var Schema = mongoose.Schema,
ObjectId = Schema.ObjectId;
User = new Schema({
...
});
Procedure = new Schema({
name: { type: String, trim: true, required: true },
alias: { type: String, trim: true, required: true }
});
Clinic = new Schema({
name: { type: String, trim: true, required: true },
procedures: [ProcedureSchema]
});
Patient = new Schema({
...
});
Record = new Schema({
'date': {type: Date, default: Date.now},
'doctor': {type: ObjectId, ref: 'User'},
'clinic': {type: ObjectId, ref: 'Clinic'},
'patient': {type: ObjectId, ref: 'Patient'},
'procedure': {type: ObjectId, ref: 'Procedure'},
});
mongoose.model('User', User);
mongoose.model('Procedure', Procedure);
mongoose.model('Clinic', Clinic);
mongoose.model('Patient', Patient);
mongoose.model('Record', Record);
fn();
}
exports.defineModels = defineModels;
Hope this help.