Mongo Populate
Im trying to populate some user info onto the articles query
exports.articleByID = function(req, res, next, id) {
Article.findById(id).populate('user', 'displayName', 'email').exec(function(err, article) {
if (err) return next(err);
if (!article) return next(new Error('Failed to load article ' + id));
req.article = article;
next();
});
};
Im getting the error
MissingSchemaError: Schema hasn't been registered for model "email".
Any ideas??
Here is the schema
'use strict';
/**
* Module dependencies.
*/
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
/**
* Article Schema
*/
var ArticleSchema = new Schema({
created: {
type: Date,
default: Date.now
},
title: {
type: String,
default: '',
trim: true,
required: 'Title cannot be blank'
},
content: {
type: String,
default: '',
trim: true
},
user: {
type: Schema.ObjectId,
ref: 'User'
}
});
mongoose.model('Article', ArticleSchema);
The third parameter to populate is the name of the model you wish to use for population, overriding what's specified in the schema.
Assuming email is a field you want from the user doc, include that in the second parameter instead:
exports.articleByID = function(req, res, next, id) {
Article.findById(id).populate('user', 'displayName email').exec(...
Related
I'm trying to build an ecommerce shop using express and mongodb. I'm trying to make categories for the shop. (e.g when someone clicks a category it should only display the items corresponding to that category) I've tried multiple ways to tackle this issue but haven't found a solution.
My current code is this one:
var NFTitem = require("../models/NFTitem")
var Item = require('../models/item');
var async = require('async')
exports.item_list = function(req, res, next) {
async.parallel({
item: function(callback) {
Item.find({}).exec(callback);
},
collection_list: function (callback) {
NFTitem.find({}).exec(callback);
},
collection: function(callback) {
NFTitem.find({'_id' : req.params.id }).exec(callback)
},
thisCategory: function (callback) {
Item.find({'collectionItem' : req.params.id }).exec(callback);
},
}, function(err, results) {
res.render('item_list.jade', { title: "NFT Marketplace", collection_list: results.item});
})
}
Now the logic behind this is to find the items corresponding to the items database which is (Item) and extract it, finding only the id matching the categories database. Then we would find the id for the categories database which is (NFTitem). Then we would do and if else command so that it only shows if both ids are matching. But this method doesn't seem to work.
I've also tried filtering the thing but it displays nothing when i filter it out. I would like to know what would be the best solution for this and if there's any way I could solve this issue.
rendered website:
block content
h1= title
ul
each collection in collection_list
li
a(href=collection.url) #{collection.name}
models for items:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var ItemSchema = new Schema(
{
name: {type: String, required: true},
description: {type: String, required: true},
collectionItem: {type: Schema.Types.ObjectId, ref: 'nftitem', required: true},
price: {type: Number, required: true},
instock: {type: Number, required: true},
}
);
// Virtual for book's URL
ItemSchema
.virtual('url')
.get(function () {
return '/catalog/item/' + this._id;
});
//Export model
module.exports = mongoose.model('Item', ItemSchema);
models for collections:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var NFTItemSchema = new Schema(
{
name: {type: String, required: true},
description: {type: String, required: true},
}
);
// Virtual for book's URL
NFTItemSchema
.virtual('url')
.get(function () {
return '/catalog/nftitem/' + this.name;
});
//Export model
module.exports = mongoose.model('nftitem', NFTItemSchema);
Since the collectionItem field in Item Schema is a reference to NFTItemSchema Schema, you can query Item Collection directly. You can refactor and simplify your code like this:
const Item = require('../models/item');
exports.item_list = async (req, res, next) => {
try{
let all_items = await Item.find({collectionItem: req.params.id});
res.render('item_list.jade', { title: "NFT Marketplace", collection_list: all_items });
} catch (error) {
res.status(400).json(error: error);
}
}
Hi I'm learning MongoDB and how to use Mongoose,
I'm trying to build a simple Workout manager app, where workouts have a name (Eg: "Upper Body"), an email account (which is used to identify who created the workout) and array of exercise consisting of references to exercises.
I want to be able to have a query where I can access the exercise title(Eg: "Push Ups") by using just the workout name.
My workout.js Model:
var mongoose = require('mongoose');
const Schema = mongoose.Schema;
var Exercise = require('./exercise');
const workoutSchema = new Schema({
title: {type: 'String', required: true},
email: {type: 'String', required: true},
exercises: [{ type: Schema.Types.ObjectId, ref: 'Exercise' }],
});
module.exports = mongoose.model('Workout', workoutSchema);
My exercise.js Model:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var exerciseSchema = new mongoose.Schema({
title: {type: String, required: true}
}, {
timestamps: true
});
module.exports = mongoose.model('Exercise', exerciseSchema);
My Workout Controller looks like this :
var Workout = require('../models/workout');
var Exercises = require('../models/exercise');
exports.getWorkoutExercises = function(req, res) {
Workout.findOne({ title: req.params.workout_name})
.populate('exercises')
.exec((err, exercises) => {
if (err){
res.send(err);
}
/* I want to be able to return or access the exercises title */
res.json({exercises.title});
});
}
In My routes.js i have this :
workoutRoutes.get('/:workout_name', WorkoutController.getWorkoutExercises);
Any Help or tips is much appreciated !
I am not sure what you did here res.json({exercises.title}); Can you try this? Hope it works
Workout.findOne({ title: req.params.workout_name})
.populate('exercises')
.exec((err, workout) => {
if (err){
res.send(err);
} else {
res.send({ exercises: workout.exercises });
}
});
And then, on your client side, you can iterate the exercises array and access to the title of exercise
I am creating a webapp using the following stack:
Node
Express
MongoDB
Mongoose
I have structured the app into a MVC structure.
There are Customer, OrderReceived and OrderSent schemas. OrderReceived and OrderSent schema references Customer schema. Abridge schema structures are following:
Customer
const mongoose = require('mongoose');
const customerSchema = mongoose.Schema({
companyName: String,
firstName: { type: String, required: true},
lastName: { type: String, required: true}
});
module.exports = mongoose.model('Customer', customerSchema);
OrderReceived
const mongoose = require('mongoose');
const orderReceivedSchema = mongoose.Schema({
receivedDate: { type: Date, required: true},
customer: {type: mongoose.Schema.Types.ObjectId, ref: 'Customer', required: true}
});
module.exports = mongoose.model('OrderReceived', orderReceivedSchema);
OrderSent
const mongoose = require('mongoose');
const orderSentSchema = mongoose.Schema({
sentDate: { type: Date, required: true},
customer: {type: mongoose.Schema.Types.ObjectId, ref: 'Customer', required: true}
});
module.exports = mongoose.model('OrderSent', orderSentSchema);
When a Customer document is asked for delete, I want to check if it the document is referenced by either OrderReceived or OrderSent documents. And if there is a presence I want to prevent the deletion of the Customer document.
The solution I came up with is to do the check in the controller of Customer, as following:
CustomerController#destroy this handles the delete request:
destroy(req, res){
OrderReceived.count({customer: req.params.id}, (error, orderLength)=>{
if (error) res.send(error);
if (orderLength<1){
OrderSent.count({'customer.customer': req.params.id}, (error, orderLength)=>{
if (error) res.send(error);
if (orderLength<1){
Customer.remove({_id: req.params.id}, error => {
if (error) res.send(error);
res.json({message: 'Customer successfully deleted.'});
});
} else {
res.status(409).json({message: 'There are orders sent using the Customer. Datum could not be deleted'});
}
});
} else {
res.status(409).json({message: 'There are orders received using the Customer. Datum could not be deleted.'});
}
});
}
Is there a better way to do this? I have other models that also depends upon the Customer document and this code is only going to get messier.
Please help.
When you are creating OrderReceived or OrderSent save reference of it in Customer too.
So on this way before you delete it, you can simply check if they are empty or not.
Your Customer schema would be like:
const customerSchema = mongoose.Schema({
companyName: String,
firstName: { type: String, required: true},
lastName: { type: String, required: true},
ordersSent: [{type: mongoose.Schema.Types.ObjectId, ref: 'OrderSent'}],
ordersReceived: [{type: mongoose.Schema.Types.ObjectId, ref: 'OrderReceived'}],
});
and your delete function should contain something like:
Customer.findById(req.params.id)
.then(customer => {
if(customer.ordersSent.length== 0&& customer.ordersReceived.length== 0)
return true
//if there was more than 0
return false
}).then(result => {
if(result)
return Customer.findByIdAndRemove(req.params.id)
res.status(409).json({message: 'There are orders received or sent using the Customer. Customer could not be deleted.'})
}).then(customerDataJustInCase =>{
res.status(200).json({message: 'Customer deleted.'})
}).catch(err => {
//your error handler
})
or you can use it via try-catch.
You can use Promise.all method to perform all DB queries at once, like below:
Promise.all([
OrderReceived.count({customer: req.params.id}),
OrderSent.count({'customer.customer': req.params.id})
])
.then(([orderReceivedCount, orderSendCount]) => {
if (orderReceivedCount < 1 && orderSendCount<1) {
...delete doc
}
}).catch(error => ...handleError)
I have a question about best practice and how to add user authorization functionality. Should it be in model, controller or elsewhere.
Currently,
I have been building validation functions within my Mongoose Models
I have been building authentication/authorization checks using middleware and called from my routes.
My current challenge is when an an authenticated and authorized user attempts to update a model for which they are NOT the owner.
My authenticated user has been attached to my request, but that data is not going to be available from within the Mongoose Model so I am thinking that I should probably create some sort of validation function on the model that can be called from my controller, so that my logic lives nicely with the model but can be called from the controller.
Controller
exports.create = function (req, res) {
try {
if (!_.isEmpty(req.body.entity.ordererAccountId) && !_.isEqual(req.user.accountId.toString(), req.body.entity.ordererAccountId)) {
var err = mong.formatError({ message: 'Invalid Account Access' });
return res.status(403).json(err);
}
OrderSchema.create(req.body.entity, function (err, entity) {
if (err) {
return mong.handleError(res, err);
}
return res.status(201).json(mong.formatSuccess(entity));
});
} catch (e) {
console.log(e);
}
};
Model
'use strict';
// ------------------------------------------------------------
// Order Model
// ------------------------------------------------------------
var mongoose = require('mongoose');
var findOneOrCreate = require('mongoose-find-one-or-create');
var Schema = mongoose.Schema;
var OrderSchema = new Schema({
created_at: { type: Date },
updated_at: { type: Date },
ordererAccountId:
{
type: Schema.ObjectId, ref: 'Account',
required: true
},
supplierAccountId:
{
type: Schema.ObjectId, ref: 'Account'
},
userId:
{
type: Schema.ObjectId, ref: 'User',
required: true
},
status: {
type: String,
enum: ['Open', 'Sent'],
default: 'Open'
},
notes: String,
supplierCompanyName: String,
supplierEmail: String,
supplierContactName: String,
supplierPhone1: String,
supplierPhone2: String,
deliveryCompanyName: String,
deliveryEmail: String,
deliveryFirstName: String,
deliveryLastName: String,
deliveryAddress1: String,
deliveryAddress2: String,
deliveryCity: String,
deliveryState: String,
deliveryPostCode: String,
deliveryCountry: String,
deliveryPhone1: String,
deliveryPhone2: String,
});
OrderSchema.plugin(findOneOrCreate);
// ------------------------------------------------------------
// Validations
// ------------------------------------------------------------
// Validate only one open order at a time per user
OrderSchema
.path('status')
.validate(function (status, respond) {
var Order = mongoose.model('Order');
// Excluding this Order, make sure there are NO other orders for this user with the status of 'Open'
var condition = {
userId: this.userId,
status: 'Open',
_id: { $ne: this._id }
};
Order.count(condition, function (err, count) {
if (err) {
console.log(err);
}
else {
respond(count === 0);
}
});
}, 'There can be only one open order at a time.');
// ------------------------------------------------------------
// Pre-Save Hook
// ------------------------------------------------------------
OrderSchema.pre('save', function (next) {
var now = new Date().getTime();
this.updated_at = now;
if (!this.created_at) {
this.created_at = now;
}
next();
});
module.exports = mongoose.model('Order', OrderSchema);
you can use your "create" function as a validation middleware in your router,by doing something like this:
app.post('/yourRoute', create, function(req, res) {
//if validation success
//do somthing
});
not forgetting to pass the "next" function as a third argument to your create function
I'm new to node.js and I am having problem accessing to the when multiple mongoose schema were declare.
//schema.js in model
var mongoose = require('mongoose');
var Schema = mongoose.Schema
, ObjectId = Schema.ObjectId;
//User Schema
var userSchema = new Schema({
id: ObjectId,
firstname: {type: String, require: true},
lastname: {type: String, require: true},
username: {type: String, unique: true, require: true},
password: {type: String, require: true},
role: {type: [String], require: true}
})
var User = mongoose.model('User', userSchema);
module.exports = User;
//Question Schema
var qnSchema = new Schema({
id: ObjectId,
question: {type: String, require: true},
module_id: {type: ObjectId, ref: 'Module'}
})
var Question = mongoose.model('Question', qnSchema);
module.exports = Question;
//Answer Schema
var ansSchema = new Schema({
id: ObjectId,
answer: String,
question: {type: ObjectId, ref: 'Question'}
})
var Answer = mongoose.model('Answer', ansSchema);
module.exports = Answer;
//Module Schema
var modSchema = new Schema({
id: ObjectId,
name: {type: String, require: true}
})
var Module = mongoose.model('Module', modSchema);
module.exports = Module;
//Role Schema
var roleSchema = new Schema({
id: ObjectId,
role: {type: String, require: true}
})
var Role = mongoose.model('Role', roleSchema);
module.exports = Role;
//index.js in controller
var mongoose = require('mongoose');
var User = require('../models/schema');
var db = mongoose.connect('mongodb://localhost/damai');
module.exports = function(app) {
app.get('/', function(req, res) {
if (typeof req.session.userid == 'undefined') {
res.render('login', { title: app.get('title') });
} else {
res.render('index', { title: app.get('title') });
}
});
app.post('/login', function(req, res) {
passwordVerification(req, res);
});
}
function passwordVerification(req, res)
{
var userid = req.param('userid');
var password = req.param('password');
User.findOne({'username': userid},{'password': 1}, function(err, cb)
{
console.log(cb);
if(cb!= null)
{
if (password == cb.password) {
req.session.userid = userid;
res.render('index', { title: app.get('title'), 'userid': userid });
} else {
res.render('login', { title: app.get('title'), error: 'Invalid login'});
}
}
else
{
res.render('login', { title: app.get('title'), error: 'Invalid login'});
}
});
}
When I only have the "User Schema" in my schema.js, the database call from method "passwordVerification()" from index.js will return me the relevant password that was retrieve from the database. However, when I start adding in other schema such as "Question Schema" in schema.js, the method "passwordVerification()" will always return null.
When exporting multiple models from a single file like you are in schema.js, you need to give each exported model its own exports field name.
For example, replace the multiple module.exports = ... lines in schema.js with this code at the end of the file that exports all models:
module.exports = {
User: User,
Question: Question,
Answer: Answer,
Module: Module,
Role: Role
};
And then in index.js you can access the models like so:
var models = require('./schema');
...
models.User.findOne(...