Related
I'm trying a to make a post request to save new data to one of my subdocuments, but I'm getting an error when trying to access the subdocument in the function. It keeps coming back as undefined. How can I get a specific user by id and create and add new data the one it's subdocuments?
model
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const ClassworkSchema = new Schema({
name: String,
time: Date,
todo: String,
isDone: false
});
const OutcomesSchema = new Schema({
name: String,
time: Date,
todo: String,
isDone: false,
isApproved: false
})
const MeetupSchema = new Schema({
name: String,
time: Date,
location: String,
attended: false
})
const UserSchema = new Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true
},
password: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now
},
classwork:{type: [ClassworkSchema], default: []},
outcomes: [OutcomesSchema],
meetups: [MeetupSchema],
});
module.exports = User = mongoose.model('users', UserSchema);
controller
classworkRouter.post("/:userId/", (req, res) => {
User.findById(req.params.user_id, (err, user) => {
if (err) return err;
new_classwork = new classwork();
(new_classwork.name = req.body.name),
(new_classwork.date = req.body.date),
(new_classwork.todo = req.body.todo),
(new_classwork.isDone = req.body.isDone);
console.log(new_classwork);
user.classwork = {};
user.classwork.name = req.body.classwork.name;
user.classwork.todo = user.classwork.todo;
if (user.classwork === undefined) {
user.classwork.push(new_classwork);
} else {
user.classwork = [new_classwork];
}
user.save(function (err, data) {
if (err) res.send(err);
res.json({ message: "work added", data: data });
});
});
});
you can see the error in the terminal in the following phto:
in this part of code
new_classwork = new classwork()
you shoud defined the new_classwrok like this :
let new_classwork = new classwork()
and new classwork() is not defined, you must to require Model of classwork in controller..
in schema file export schemas like this :
const User = mongoose.model('users', UserSchema);
const Classwork = mongoose.model('Classwork', ClassworkSchema );
module.exports = {
User : User ,
Classwork : Classwork
}
in controller.js
const {User} = require('../models/certification');
const {Classwork } = require('../models/certification');
after require models you can use new Crosswork like this :
note: Classwork with uppercase character
let new_classwork = new Classwork()
So I've been working on a project and I finished most of it, but then this error popped up, saying there is something that is undefined, here is the error:
E11000 duplicate key error index: build-a-voting-app.polls.$votedIp_1 dup key: { : undefined }
Here is my code for my create new mongo schema file (polls.model.js)
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const pollSchema = new Schema({
title: { type: String, unique: true, required: true },
choices: [
{
title: { type: String, required: true },
count: { type: Number, default: 0 }
}
],
votedIp: [{ type: String, unique: true }],
createdAt: {type:Date, default:Date.now()},
createdBy: String
});
const Poll = mongoose.model('polls', pollSchema);
module.exports = Poll;
Here is the function where I add the inputs
function submitVote(field, res, ip) {
Poll.findOneAndUpdate(
{ choices: { $elemMatch: { title: field } } },
{ $inc: { 'choices.$.count': 1 }, $addToSet: { 'votedIp': ip } },
{ new: true },
function (err, poll) {
if (err) throw err;
res.json({ updated: poll });
}
);
}
Here is how I first created it
var newPoll = new Poll({
title: req.body.title,
choices: choicesArr,
createdBy: req.session.user.username || req.session.user
}).save(function (err, poll) {
if (err) throw err
res.redirect('/mypolls')
});
If you want to see the full code please go to https://github.com/ElisaLuo/Freecodecamp-Build-A-Voting-App
I'm using the ip addresses for checking if the user has voted or not (I'm building a voting app), but right now, I cannot even create a new schema / poll. Does anyone know why the error happens and how I can solve it?
#Elisa l - you may want to read this - mongoose enforce unique attribute on subdocument property
However, I did manage to test with mongoose-mock and the behavior is as expected - test results below (please do check the two versions of votedIp in the test code snippets)
and as described in the MongoDb document referenced in the above link. Mongoose does not enforce the unique integrity, MongoDb does.
With the mocha test below (inserted as snippets, not to run the code but just for better readability, please ignore the messy look of the comments in the code but the permutation and combination had to be worked out!), I did manage to create the mongoose schema by adding a create method in "Poll". please note the change in the schema - votedIp: { type: String, unique: true }, you can change it to array in the test code.
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var pollSchema = new Schema({
title: { type: String, unique: true, required: true },
choices: [
{
title: { type: String, required: true },
count: { type: Number, default: 0 }
}
],
votedIp: { type: String, unique: true },
createdAt: {type:Date, default:Date.now()},
createdBy: String
});
// Below code added by RJv (ie. me :)
var NewPoll = new mongoose.Schema(pollSchema);
NewPoll.statics.create = function (params, callback) {
var newUpdate = new NewPoll(params);
newUpdate.save(function(err, result) {
callback(err, result);
});
return newUpdate;
};
var Poll = mongoose.model('Model', NewPoll);
module.exports = Poll;
var expect = require('chai').expect,mongooseMock = require('mongoose-mock'),proxyquire=require('proxyquire'),
sinon = require('sinon'), chai=require('chai'),sinonChai = require("sinon-chai");chai.use(sinonChai);
var Poll;
before(function(done){
Poll = proxyquire('./Poll', {'mongoose': mongooseMock});
done();
})
describe('model', function() {
/* beforeEach(function (done) {
Poll = proxyquire('./Poll', {'mongoose': mongooseMock});
done();
});*/
it("should be called once",function(done){
setTimeout(done, 15000);
var callback = sinon.spy();
var poll1 = Poll.create({ "title": 'jv', "choices": [{"title":"jv#gmail.com","count":"1"}],
"votedIp":"9.1.2.1","createdAt":"23/07/2017","createdBy":"Lisa"}, callback);
// Below to pass data for votedIp as an array as described in the original schema by Elisa
//"votedIp":[{"add1":"9.","add2":"1.","add3":"2.","add4":"1"}],"createdAt":"23/07/2017","createdBy":"Lisa"}, callback);
//expect(poll1.votedIp[0].add1+poll1.votedIp[0].add2+poll1.votedIp[0].add3+poll1.votedIp[0].add4).equals("9.1.2.1");
expect(poll1.save).calledOnce;
console.log(JSON.stringify(poll1));
expect(poll1.votedIp).equals("9.1.2.1");
done();
});
it('should expect same ip to get added', function(done) {
this.timeout(5000);
setTimeout(done, 15000);
var callback = sinon.spy();//mock(new Poll({ title: 'jv', choices: [{title:"jv#gmail.com","count":"1"}], votedIp:[{ad1:"9.",add2:"1.",add3:"2.",add4:"1"}],createdAt:"25/07/2017",createdBy:"Lisa"}));
var poll = Poll.create({ "title": 'jv', "choices": [{"title":"jv#gmail.com","count":"1"}],
"votedIp":"9.1.2.1","createdAt":"23/07/2017","createdBy":"Lisa"}, callback);
// var poll = Poll.create({ "title": 'jv', "choices": [{"title":"jv#gmail.com","count":"1"}],
// Below to pass data for votedIp as an array as described in the original schema by Elisa
// "votedIp":[{"add1":"9.","add2":"1.","add3":"2.","add4":"1"}],"createdAt":"25/07/2017","createdBy":"Lisa"}, callback);
// expect(poll.votedIp[0].add1+poll.votedIp[0].add2+poll.votedIp[0].add3+poll.votedIp[0].add4).equals("9.1.2.1");
expect(poll.save).calledOnce;
expect(poll.votedIp).equals("9.1.2.1");
//assert(spy.calledOnce);
done();
});
});
Are you calling submitVote multiple times in quick succession? You might be running into https://jira.mongodb.org/browse/SERVER-14322.
The suggested fix for this is to check the error and if one of the calls fails retry it.
https://docs.mongodb.com/manual/reference/method/db.collection.update/#use-unique-indexes
I want to make the key email unique across that collection but i cant getting this working, here is my server code.
// Create a schema
var userSchema = new mongoose.Schema({
email: { type: String, required: true},
password: String
});
var userModel = mongoose.model("user", userSchema);
router.post('/postuser', (req, res) => {
console.log('Requested data to server: ' + JSON.stringify(req.body._user));
var user = new userModel({
email: req.body._user.email,
password: req.body._user.password
});
// user.isNew = false;
user.save((err, data) => {
console.log('Analyzing Data...');
if(data) {
console.log('Your data has been successfully saved.');
res.json(data);
}
else {
console.log('Something went wrong while saving data.');
console.log(err);
res.send(err);
}
})
});
Note: I also try email: { type: String, required: true, unique: true} but its not working and show below error.
name: 'MongoError',
message: 'E11000 duplicate key error collection: hutreservationsystem.users
index: _Email_1 dup key: { : null }',
driver: true,
code: 11000,
index: 0,
errmsg: 'E11000 duplicate key error collection: hutreservationsystem.users index: _Email_1 dup key: { : null }',
getOperation: [Function],
toJSON: [Function],
toString: [Function] }
A short answer using this tool mongoose-unique-validator
npm install --save mongoose-unique-validator
and in your model
var mongoose = require('mongoose')
var uniqueValidator = require('mongoose-unique-validator')
var userSchema = new mongoose.Schema({
email: { type: String, required: true, unique: true},
password: String
});
userSchema.plugin(uniqueValidator)
var userModel = mongoose.model("user", userSchema);
That's it! (Notice unique: true)
Now, there is no email duplication in your collection.
Bonus! : you can access err
.catch(err => console.log(err))
so in your example
// user.isNew = false;
user.save((err, data) => {
console.log('Analyzing Data...');
if(data) {
console.log('Your data has been successfully saved.');
res.json(data);
}
else {
console.log('Something went wrong while saving data.');
console.log(err);
res.send(err);
}
accessing err >>
so you can res.send(err.message) >> 'Validation failed'
{
message: 'Validation failed',
name: 'ValidationError',
errors: {
email: {
message: 'Error, expected `email` to be unique. Value: `example#gmail.com`',
name: 'ValidatorError',
kind: 'unique',
path: 'email',
value: 'example#gmail.com'
}
}
}
Async Custom Validator
var userSchema = new mongoose.Schema({
password: String,
email: {
type: String,
lowercase: true,
required: true,
validate: {
isAsync: true,
validator: function(value, isValid) {
const self = this;
return self.constructor.findOne({ email: value })
.exec(function(err, user){
if(err){
throw err;
}
else if(user) {
if(self.id === user.id) { // if finding and saving then it's valid even for existing email
return isValid(true);
}
return isValid(false);
}
else{
return isValid(true);
}
})
},
message: 'The email address is already taken!'
},
}
});
You may like to change the validator code to es6.
email: {
type: String,
trim: true,
unique: true, // note - this is a unqiue index - not a validation
validate: {
validator: function(value) {
const self = this;
const errorMsg = 'Email already in use!';
return new Promise((resolve, reject) => {
self.constructor.findOne({ email: value })
.then(model => model._id ? reject(new Error(errorMsg)) : resolve(true)) // if _id found then email already in use
.catch(err => resolve(true)) // make sure to check for db errors here
});
},
}
},
I implemented the following code to see if anything was wrong:
var mongoose = require('mongoose');
var bodyParser = require('body-parser');
var express = require('express');
var http = require('http');
var app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
var Schema = mongoose.Schema;
mongoose.connect('mongodb://localhost/test');
// Create a schema
var userSchema = new mongoose.Schema({
email: { type: String, required: true, unique: true},
password: String
});
var userModel = mongoose.model("user", userSchema);
app.post('/postuser', (req, res) => {
console.log('Requested data to server: ' + JSON.stringify(req.body._user));
var user = new userModel({
email: req.body._user.email,
password: req.body._user.password
});
// user.isNew = false;
user.save((err, data) => {
console.log('Analyzing Data...');
if(data) {
console.log('Your data has been successfully saved.');
res.json(data);
}
else {
console.log('Something went wrong while saving data.');
console.log(err);
res.send(err);
}
})
});
http.createServer(app).listen(3000, function(){
console.log('Express server listening on port 3000');
});
And I made sure that no collection by the name of users existed in my local MongoDB database. Moreover, I used Postman for sending API requests to my server at http://localhost:3000. There seemed to be no issue as I continued to add users with differing email values. And I only got the following error when I entered an email with a duplicate value
{
"code": 11000,
"index": 0,
"errmsg": "E11000 duplicate key error collection: test.users index: email_1 dup key: { : \"hot#mail.com\" }",
"op": {
"email": "hot#mail.com",
"password": "1234567",
"_id": "5919a3428c13271f6f6eab0f",
"__v": 0
}
}
These are the JSON requests that I sent:
{"_user": {"email": "hot#mail.com", "password": "1234"}}
{"_user": {"email": "sammy#mail.com", "password": "1234"}}
{"_user": {"email": "tommy#mail.com", "password": "1234"}}
{"_user": {"email": "tommy#mail.ae", "password": "1234567"}}
{"_user": {"email": "hot#mail.com", "password": "1234567"}}
The error mentioned above was sent back on the last request as the email hot#mail.com is repeated.
If you view the link http://mongoosejs.com/docs/api.html#schematype_SchemaType-unique , you'll see that the E11000 error is only sent when the email entered is not unique. Moreover, your email can't be an empty string or not be present as that violates the required property.
ES6:
const userSchema = new Schema({
name: String,
id: {
type: String,
required: true,
unique: true,
validate: async (value) => {
try {
const result = await userModel.findOne({ id: value })
if (result) throw new Error("duplicity detected: id :" + value);
} catch (error) {
throw new Error(error);
}
}
}
})
const userModel = mongoose.model<Document>('users', userSchema);
ES6 (TypeScript):
const userSchema = new Schema({
name: String,
id: {
type: String,
required: true,
unique: true,
validate: async (value: any): Promise<any> => {
try {
const result: Document | null = await userModel.findOne({ id: value })
if (result) throw new Error("duplicity detected: id :" + value);
} catch (error) {
throw new Error(error);
}
}
}
})
const userModel: Model<Document, {}> = mongoose.model<Document>('users', userSchema);
In your user schema set attribute email as unique (unique: true).
var userSchema = new mongoose.Schema({ email: { type: String, required: true, unique: true}, });
I am having difficulty with saving into a mongodb using mongoose. Here is the error i am getting
Application has thrown an uncaught exception and is terminated:
ValidationError: pageModel validation failed
my code
//page.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var pageShema = new Schema({
pageId: {
type: String,
required: true
},
logo: {
type: String
},
subline: {
type: String
},
header: {
type: String
},
created_on: {
type: Date,
default: Date.now
},
offense:{
type: Schema.Types.Mixed
},
deffense:{
type: Schema.Types.Mixed
}
});
var pageModel = mongoose.model('pageModel', pageShema);
module.exports = pageModel;
app.js
var mongoose = require('mongoose');
var dbURL = config.dbURL;
mongoose.connect(dbURL);
var pagedb = require('./models/page');
var page = new pagedb( {
pageId: 'test',
logo:'test logo',
subline: 'test subline',
header: 'test header',
created_on: Date.now,
offense: {},
deffense: {}
});
page.save(function (err, page) {
if (err) {
console.log('error!');
throw err;
};
console.log('page is created!');
});
It fails because of the date field. Use new Date() instead of Date.now.
var mongoose = require('mongoose');
var dbURL = config.dbURL;
mongoose.connect(dbURL);
var pagedb = require('./models/page');
var page = new pagedb( {
pageId: 'test',
logo:'test logo',
subline: 'test subline',
header: 'test header',
created_on: new Date(),
offense: {},
deffense: {}
});
page.save(function (err, page) {
if (err) {
console.log('error!');
throw err;
};
console.log('page is created!');
});
I'm using the 'slug' npm library to give-strings-dashes-for-url-cleanliness. The library works when I console.log() a string, and it's required properly into all of the relevant controllers.
However, I can't figure out how implement slug() properly to format my URLs. The problem I'm having is that a product name might be "Foo Bar Baz Quux", but I can't seem to find the right implementation for slug() without disrupting the connection between the app.js route and the findOne query via mongoose.
app.js
app.get('/market/:product_name', marketController.getProduct);
controller.js
exports.getProduct = function(req, res, next) {
var product_name = req.params.product_name;
// var slugProduct = slug(product_name);
Product.findOne({'name': product_name}, function(err, product) {
if (err) return next(err);
return res.send(product.data);
});
}
Perhaps you could "slugify" a product by defining a slug property while setting up your "Product Schema". I can confirm it works if using mongoose with along mongoose-slug-generator plugin, see link... You could set it up as follows:
const mongoose = require('mongoose');
const { Schema } = mongoose;
const slug = require('mongoose-slug-generator');
// * mongoose slug generator options
const options = {
separator: '-',
lang: 'en',
truncate: 120
};
// * Init mongoose slug generator plugin
mongoose.plugin(slug, options);
const ProductSchema = new Schema({
name: {
type: String,
trim: true
},
slug: {
type: String,
slug: 'name', // genarating slug from multiple properties is allowed ['name', 'brand']
unique: true
},
price: {
type: Number,
required: true
},
brand: {
type: String,
default: 'Apple'
}
});
module.exports = mongoose.model('Product', ProductSchema);
exports.getProduct = function(req, res, next) {
const { product_name } = req.params;
Product.findOne({ slug: product_name }, function(err, product) {
if (err) return next(err);
if (!product) {
return res.status(404).json({
message: 'Product data not found.'
);
}
res.status(200).send(product);
});
}
app.get('/market/:product_name', marketController.getProduct)
// request
const response = await axios.get('/api/market/my-awesome-product')
// response.data
{
"_id": "60fh4d37ac1a1c6f58d6a5f4",
"name": "My Awesome Product",
"slug": "my-awesome-product",
"price": 85.9,
"brand": "Apple"
}