I'm building a REST api in node and i'm stuck on trying to include subdocuments into my GET requests. For example, I have two schema's right now, People and Locations. A location can have many people, and a person can have one location. So when you return /people I would like my location entry to contain the location information.
I believe my problem could be one of two things. I'm fairly new to node, so i'm not 100% sure if my schema's can see each other, when I try the common methods around the web, my location field get's populated with null. How I understand it, I store the location id in my people schema and then using subdocuments it will find the location with that id and fill in the info.
I also am not 100% sure how to use the populate function, and how exactly I should go about writing the response to my GET call. My code below, I'd love to hear what you have to say!
app.js
// Node Setup
var application_root = __dirname,
express = require('express'),
path = require('path'),
mongoose = require('mongoose'),
http = require('http');
var app = express();
// MongoDB Connection
var dbLocalhost = mongoose.createConnection('mongodb://localhost/lantern/');
// Configure Node
app.configure(function(){
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(app.router);
app.use(express.static(path.join(application_root, "public")));
app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
app.port = 3000;
});
// Routes
var Locations = require('./routes/locations')(app, { 'mongoose': mongoose, 'db': dbLocalhost });
var People = require('./routes/people')(app, { 'mongoose': mongoose, 'db': dbLocalhost });
// Start the server
app.listen(app.port);
routes/people.js - has all the endpoints, but we're just concerned with GET for now
module.exports = function (app, options) {
var mongoose = options.mongoose;
var Schema = options.mongoose.Schema;
var db = options.db;
var PeopleModel = require('../schemas/peopleSchema')(db);
app.get('/people', function (req, res) {
return PeopleModel.find(function (err, obj) {
if (!err) {
return res.send(obj);
} else {
return res.send(err);
}
});
});
};
schemas/peopleSchema.js (the "location" field is what i want to populate)
module.exports = function(db) {
return db.model('People', PeopleSchema());
}
function PeopleSchema () {
var Schema = require('mongoose').Schema;
return new Schema({
first_name: String,
last_name: String,
address: {
unit: Number,
address: String,
zipcode: String,
city: String,
region: String,
country: String
},
image: String,
job_title: String,
created_at: { type: Date, default: Date.now },
active_until: { type: Date, default: null },
hourly_wage: Number,
location: [{type: Schema.ObjectId , ref: 'Locations'}], // Inheirit store info
employee_number: Number
}, { collection: 'people' });
}
schemas/locationsSchema.js - and the locations schema just incase
module.exports = function(db) {
return db.model('Locations', LocationsSchema());
}
function LocationsSchema () {
var Schema = require('mongoose').Schema;
return new Schema({
title: String,
address: {
unit: Number,
address: String,
zipcode: String,
city: String,
region: String,
country: String
},
current_manager: String, // Inherit person details
alternate_contact: String, // Inherit person details
hours: {
sunday: String,
monday: String,
tuesday: String,
wednesday: String,
thursday: String,
friday: String,
saturday: String,
holidays: String
},
employees: "", // mixin employees that work at this location
created_at: { type: Date, default: Date.now },
active_until: { type: Date, default: null }
}, { collection: 'locations' });
}
Related
This may seem like a vague question, but I'm going to try to explain the best I can. As a side note, I'm quite new to using mongoose :)
I have a mongoose-schema storing different values for each user, like so...
let userSchema = mongoose.Schema({
user: { type: String, required: true, unique: true },
pass: { type: String, required: true },
files: [{ type: String, required: false }],
});
The "files"-key contains an array of values, lets say for example:
userSchema.files = [value1, value2, value3]
And I want each value to be connected to some kind of ID, so that when I call the specified ID, I get the specified value. Just for demonstrating purposes, it could look something like this:
userSchema.files = [{value:value1, id: id1},
{value:value2, id: id2},
{value:value3, id: id3}]
Then I want to find the specified id, and return it's "value"-key in a request:
router.route("/home/:id")
.get(restrict, function(req, res) {
User.findOne({ user: req.session.Auth.username }, function(error, data) {
data.files.forEach(function(file) {
if (file.id === req.params.id) {
response.render("../home", file.value)
}
}
});
});
How can I do this? Tried pushing an object to files, but that didn't work as expected. Read something about ObjectId, but couldn't quite understand it. Any tips?
I think you simply need to create a separate model for File and connect it to your User model using the 'ref' keyword :
let fileSchema = mongoose.Schema({
_id : Number,
value : String
});
let userSchema = mongoose.Schema({
user: { type: String, required: true, unique: true },
pass: { type: String, required: true },
files: [{ type: Number, ref: 'File' }]
});
let User = mongoose.model('User', userSchema);
let File = mongoose.model('File', fileSchema);
let f1 = new File({ _id: 1, value: 'File 1'});
let f2 = new File({ _id: 2, value: 'File 2'});
let f3 = new File({ _id: 3, value: 'File 3'});
let user1 = new User({user:'chuck', pass:'norris'});
user1.files.push(f1);
user1.files.push(f2);
user1.files.push(f3);
user1.save(function(err){ });
Now to get the data back:
User
.findOne({ user: 'chuck' })
.populate('files') // only works if we pushed refs to children
.exec(function (err, user) {
if (err) return handleError(err);
console.log(user);
//you can now loop through user.files and compare _id
user.files.forEach(function(file) {
if (file._id === req.params.id) {
response.render("../home", file.value)
}
}
});
You can read about mongoose reference population here: http://mongoosejs.com/docs/populate.html
I am trying to save template based on user id , How can i make sure when template save it save with user id _id ? i added reference to the templateSchema for User.
user.model.js
var UserSchema = new mongoose.Schema({
_id: { type: String, required: true, index: {unique: true}},
firstName: String,
lastName: String,
type: String,
groups:[{type: String, ref: 'Group', required: false}]
},
{
toObject: {
virtuals: true
},
toJSON: {
virtuals: true
}
});
export default mongoose.model('User', UserSchema);
template.model.js
var User = require('../user/user.model.js');
var TemplateSchema = new mongoose.Schema({
_id: { type: String, required: true},
name: String,
id: String,
appliesTo: [],
properties: [],
createdBy: { type: mongoose.Schema.Types.ObjectId, ref: 'User'}
});
export default mongoose.model('Templates', TemplateSchema);
template.controller.js
var eTemplate = require('./template.model');
export function create(req, res) {
console.log(req.body);
eTemplate.createAsync(req.body)
.then(responseWithResult(res, 201))
.catch(handleError(res));
}
Mongoose has two built-in functions that are called before (pre) and after (post) you save a document. My advice is to make use of them. Here is an example of my code in which I search for an sequence number before saving the user document. You can do the same: When you save the template, make a request for the user id to the database (Or vice-versa). You can even save one, get the id and save the other.
Bellow follows my code for the sequence and the user.
var UserSchema = new Schema({
username: { type: String, required: true, unique: true },
id: { type: String },
...
});
UserSchema.pre('save', function(next) {
let doc = this;
let id = 'userSeq'
Sequence.findByIdAndUpdate(id, { $inc : {nextSId : 1} }, function(error,data) {
if(error)
next(error)
doc.id = data.nextSId-1;
next();
})
});
I hope my answer was useful for you. Just a remark, pre and post are not called in the event of updates for the document.
EDIT [SOLVED]:
I was connecting to the wrong database...
I changed
var dbURI = 'mongodb://localhost/wifiplz'
to
var dbURI = 'mongodb://localhost/wifiPlz'
so all of this was due to a typo (uncapitalized p). Anyone with this type of problem make sure you are connecting to the right database!
Here is my schema file (location.js):
var mongoose = require('mongoose');
var openingTimeSchema = new mongoose.Schema({
days: {type: String, required: true},
opening: String,
closing: String,
closed: {type: Boolean, required: true}
});
var reviewSchema = new mongoose.Schema({
author: String,
rating: {type: Number, min: 0, max: 5, required: true},
createdOn: {type: Date, default: Date.now},
reviewText: String
});
var locationSchema = new mongoose.Schema({
name: {type: String, required: true},
address: {type: String, required: true},
rating: {type: Number, default: 0, min: 0, max: 5},
facilities: [String],
coords: {type: [Number], index: '2dsphere'},
openingTimes: [openingTimeSchema],
reviews: [reviewSchema]
});
// compiling schema as 'Location' model
mongoose.model('Location', locationSchema);
In my routes, I map the route to appropriate controller:
router.get('/locations/:locationId', locationsCtrl.locationRead);
In my controller (locationsCtrl.js) I try to find a location by id:
var mongoose = require('mongoose');
var Location = mongoose.model('Location');
module.exports.locationRead = function(req, res) {
Location
.findById(req.params.locationId)
.exec(function(err, location) {
if (err) throw err;
res.status(200);
res.json(location); // returns null
});
}
When I tested this, I am always getting null for valid ids. Would appreciate some insight as to why. Thanks.
edit:
Checking for the name of the collection on my computer using mongo and show collections, I get locations as the collection name. This is as expected. Although specifying mongoose.model('Location', locationSchema, 'locations') doesn't have any effect.
Make following changes :
var mongoose = require('mongoose');
var Location = mongoose.model('Location');
module.exports.locationRead = function(req, res) {
Location
.findOne({_id: req.params.locationId}, function (err, location){
if (err) throw err;
res.status(200);
res.json(location); // returns null
});
}
_id could be your any field so replace your db field with _id but make sure that field should be primary in nature or unique. If it's not create an index over that field.
I have the following 2 schema's
question.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var questionsSchema = new Schema({
nr: Number,
points: Number,
description: String,
isActive: {type: Boolean, required: true, default: true}
});
module.exports = mongoose.model('Question', questionsSchema);
round.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var roundSchema = new Schema({
name: String,
index: Number,
questions: {type: [Schema.Types.ObjectId], ref: 'Question'},
createdOn: {type: Date, required: true, default: Date.now()},
isActive: {type: Boolean, required: true, default: true}
});
module.exports = mongoose.model('Round', roundSchema);
There is some data that gets filled correctly, however when I try even the most simple query, it won't even work:
var Round = require('../model/round.server.model.js');
function findAll(req, res) {
Round.find().populate('questions').exec(function (err, results) {
if (err) {
console.log("An error occured when receiving all rounds!", err);
return res.sendStatus(404);
}
console.log(results);
return res.send(results);
});
}
All rounds are retrieved, but the question arrays are empty, even the _id's themselves disappeared
I think it's because you initialize in the wrong way your population. I understand you want an array of questions in each of your round. Your error seems to be here.
questions: {type: [Schema.Types.ObjectId], ref: 'Question'}
You should do as following in order to make it work:
questions: [{type: Schema.Types.ObjectId, ref: 'Question'}]
Because actually, you're making an array of type and this doesn't mean anything.
I'm trying to specify the schema of my db in mongoose. At the moment I do this:
var Schema = mongoose.Schema;
var today = new Date(2011, 11, 12, 0, 0, 0, 0);
var personSchema = new Schema({
_id : Number,
name: { type: String, required: true },
tel: { type: String, required: true },
email: { type: String, required: true },
newsitems: [{ type: Schema.Types.ObjectId, ref:'NewsItem'}]
});
var taskSchema = new Schema({
_id: Number,
description: { type: String, required: true },
startDate: { type: Date, required: true },
newsitems: [{ type: Schema.Types.ObjectId, ref:'NewsItem'}]
});
var newsSchema = new Schema({
_id: Number,
creator : { type: Schema.Types.ObjectId, ref: 'Person' },
task : { type: Schema.Types.ObjectId, ref: 'Task' },
date: { type: Date, required:true },
loc: {type: String, required: true }
});
var NewsItem = mongoose.model('NewsItem', newsSchema);
var Person = mongoose.model('Person', personSchema);
var Task = mongoose.model('Task', taskSchema);
var tony = new Person({_id:0, name: "Tony Stark", tel:"234234234", email:"tony#starkindustries.com" });
var firstTask = new Task({_id:0, description:"Get an interview with the president", startDate:today});
var newsItem1 = new NewsItem({_id:0, creator: tony.id, task: firstTask.id, date: today, loc: "NY"});
newsItem1.save(function (err) {
if (err) console.log(err);
firstTask.save(function (err) {
if (err) console.log(err);
});
tony.save(function (err) {
if (err) console.log(err);
});
});
NewsItem
.findOne({ loc: "NY" })
.populate('creator')
.populate('task')
.exec(function (err, newsitem) {
if (err) console.log(err)
console.log('The creator is %s', newsitem.creator.name);
})
I create the schemas and try to save some data.
The error:
{ message: 'Cast to ObjectId failed for value "0" at path "creator"',
name: 'CastError',
type: 'ObjectId',
value: '0',
path: 'creator' }
I wrote this code based on : http://mongoosejs.com/docs/populate.html#gsc.tab=0
The db I try to create looks like this: Specify schema in mongoose .
How can I fix this?
The example from the mongoose docs you referenced uses Number for the personSchema._id field, and ObjectId for the others.
I presume they do this in the example only to demonstrate that it's possible to use either. If you do not specify _id in the schema, ObjectId will be the default.
Here, all your records have an _id field which is an ObjectId, yet you're treating them like numbers. Furthermore, fields like personID and taskID do not exist, unless you've left out the part where you define them.
If you did want to use numbers for all your _id fields, you'd have to define that in the schemas.
var newsSchema = new Schema({
_id: Number,
_creator: {type: ObjectId, ref: "Person"},
// ...
})
var personSchema = new Schema({
_id: Number,
// ...
})
Then to create a news item with a particular ID, and assign it to a creator:
var tony = new Person({_id: 0});
var newsItem = new NewsItem({_id: 0, creator: tony.id});
However the thing to note here is that when you use something other than ObjectId as the _id field, you're taking on the responsibility of managing these values yourself. ObjectIds are autogenerated and require no extra management.
Edit: I also noticed that you're storing refs on both sides of your associations. This is totally valid and you may want to do it sometimes, but note that you'd have to take care of storing the references yourself in the pre hook.
I was receiving this error after creating a schema:
CastError: Cast to ObjectId failed for value “[object Object]” at path “_id”
Then modifying it and couldn't track it down. I deleted all the documents in the collection and I could add 1 object but not a second. I ended up deleting the collection in Mongo and that worked as Mongoose recreated the collection.