mongoose index not work, when use mocha - node.js

My project files:
I have a User model:
user.model.js
var UserSchema = new Schema({
username: { type: String, required: true, unique: true },
location: { 'type': {type: String, enum: "Point", default: "Point"}, coordinates: { type: [Number], default: [0,0]} },
});
UserSchema.index({location: '2dsphere'});
And have an Express route:
user.route.js
function create(req, res) {
User.create(req.body, function(err, user) {
if (err) return res.send(err);
return res.send(user);
})
}
user.test.js
describe('test_user', function() {
it('create user', function(done) {
request('http://localhost/user')
.post('/')
.send({
username: 'john',
password: "123"
})
.end(function(err, res) {
if (err) cb(err);
done()
});
});
});
I have a drop_databse.js file, it run every time before run test:
drop_databse.js
var MongoClient = require('mongoose/node_modules/mongodb').MongoClient
MongoClient.connect('mongodb://localhost/miccity_test', function(err, db) {
if (err) return console.log(err);
db.dropDatabase(function(err) {
if (err) return console.log('drop databse failed!! :\n' + err);
db.close();
})
})
when I test
when I run test:
$ node drop_database.js && mocha
test_user
✓ create user
1 passing (21ms)
But, I notice user collection in database has not index of location field !
But I create a single file:
test.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
mongoose.connect('mongodb://localhost/miccity_test');
var db = mongoose.connection;
db.on('error', console.error.bind(console, 'mongodb connection error:'));
var UserSchema = new Schema({
username: { type: String, required: true, unique: true },
location: { 'type': {type: String, enum: "Point", default: "Point"}, coordinates: { type: [Number], default: [0,0]} },
});
UserSchema.index({location: '2dsphere'});
var User = mongoose.model('User', UserSchema);
User.create({username: "test"}, function (err) {
if (err) console.log(err);
})
And run: node drop_databse.js && node test.js, then i check the index of location, it exist!
Is it mean mocha can not create index of collection?
Edit:
i found the trick way:
use Model.ensureIndexes to Sends ensureIndex commands to mongo for each index declared in the schema.
function create(req, res) {
User.ensureIndexes(function(err){
User.create(req.body, function(err, user) {
if (err) return res.send(err);
return res.send(user);
})
}
it is maybe a bug for mongoose, I already submit this issue to github.

Related

mongoose only insert id, createdAt and updated fields

I'm learning to use Node.js and MongoDB. I have a problem when I try to save data to the database.
Here's my code
const Test = require('../models/test');
const test = (req, res, next) => {
let url = "http://localhost:3000/article"
request(url, (req, (err, fields) => {
if (err) {
return res.status(400).json({
error: "error"
})
}
var objTest = JSON.parse(fields.body);
console.log(objTest.user)
let test = new Test(objTest)
console.log("ini",test)
test
.save()
.then(result => {
res.send(result)
})
.catch(err => {
console.log(err);
res.status(500).json({
error: err
});
});
}))
}
here my Test Schema
const testSchema = new mongoose.Schema(
{
tittle: {
type: String,
index: true,
},
content: {
type: String,
},
postedBy: { type: mongoose.Schema.ObjectId, ref: 'Author' },
created: {
type: Date,
default: Date.now,
},
},
{
timestamps: true,
},
);
module.exports = mongoose.model('Test', testSchema, 'tests');
The response in Postman is only id, createdAt and updatedAt. Thank you.

Mongoose: How to prevent mongodb to save duplicate email records in database

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}, });

Mongoose error in required properties

I'm connecting a Angular 2 app to MongoDB via Mongoose.
I'm trying to store some data, but i obtain an error on all required properties.
I set up a schema, serverside:
var mongoose = require("mongoose");
var Schema = mongoose.Schema;
var uniqueValidator = require("mongoose-unique-validator");
var schema = new Schema({
email: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
nome: {
type: String,
required: true
},
cognome: {
type: String,
required: true
},
dataNascita: {
type: Date
},
telefono: {
type: String
},
classifica: {
type: String
}
});
schema.plugin(uniqueValidator);
module.exports = mongoose.model("User", schema);
The user object is clearly filled:
Mongoose responds with an error:
Thanks in advance for any help.
Max
Update:
The call from a Angular service:
#Injectable()
export class AuthService {
constructor(private http: Http) {
}
addUser(utente: Utente) {
const body = JSON.stringify(utente);
return this.http.post('http://localhost:3000/utente', body)
.map((response: any) => {
console.log(response);
response.json();
})
.catch((error: Response) => Observable.throw(error.json()
));
}
}
The Moongose call:
var express = require('express');
var router = express.Router();
var User = require('../models/users');
router.post('/', function (req, res, next) {
var user = new User({
email: req.body.email,
password: req.body.password,
nome: req.body.nome,
cognome: req.body.cognome,
dataNascita: req.body.dataNascita,
telefono: req.body.telefono,
classifica: req.body.classifica
});
console.log(res);
user.save(function (err, result){
console.log(err);
console.log(res);
if (err){
return res.status(500).json({
titolo: "Errore durante il salvataggio",
errore: err
});
}
res.status(201).json({
messaggio: 'Utente salvato correttamente',
oggetto: res
});
});
});

Best practice for structuring Express routes that handle MongoDB queries

I am building a RESTful service for querying a movie database using
Express.js, Node.js and MongoDB and I am a beginner on all of them.
My question is what is the best practice for structuring db queries with Node so that I take advantage of the callback mechanisms and not block the server but at the same time not write bloated code.
I modified the code provided by the express-generator and I think it achieves the former but not the latter. What are your comments?
If you could provide a general skeleton for an Express route that handles db queries, I would appreciate it.
Below is my code
var findMovie = function(db, callback, req, res) {
var path = req.path.split("\/");
var cursor = db.collection('movies').find({"_id" : ObjectId(path[path.length - 2])});
var query = [];
cursor.each(function(err, doc) {
assert.equal(err, null);
if (doc != null) {
query.push(doc);
} else {
res.json(query);
callback();
}
});
}
router.get('/movies/:id/info/', function(req, res, next){
MongoClient.connect(url, function(err, db) {
assert.equal(null, err);
findMovie(db, function() {
db.close();
}, req, res);
});
});
First if you use mongoDB in node i will definately recommend to use mongoose or some other object modeling tool, its much easier than native driver.
then it might look like this:
/model/user.js
var mongoose = require('mongoose');
var UserSchema = new mongoose.Schema({
createdAt: {type: Date, default: Date.now},
updatedAt: {type: Date, default: Date.now},
email: {type: String, unique: true, required: true},
password: {type: String, required: true},
active: {type: Boolean, default: true},
role: {type: String, default: 'user'},
accessLevel: {type: Number, default: 1}
}, {
collection: 'users'
});
module.exports = mongoose.model('User', UserSchema);
/controllers/users.js
var User = require('../model/user');
exports.create (req, res, next) {
var newUser = new User(req.body);
newUser.save(function (err) {
if (err)
return next(err);
res.json({
message: 'User created'
});
});
}
exports.listAll (req, res, next) {
User.find({}, function (err, users) {
if (err)
return next(err);
res.json(users)
});
}
exports.getById (req, res, next) {
User.findById(req.params.id, function (err, user) {
if (err)
return next(err);
res.json(user)
});
}
/routes/users.js
var controller = require('../controllers/users');
var router = require('express').Router();
router.route('/users')
.post(controller.create)
.get(controller.listAll)
router.route('/users/:id')
.get(controller.getById)
.delete(controller.remove)

Mongoose - findByIdAndUpdate - doesn't work with req.body

I have a problem with update documents in mongodb over mongoose.
My model bellow:
var mongoose = require('mongoose');
var bcrypt = require('bcrypt-nodejs');
var UserSchema = new mongoose.Schema({
first_name:{
type: String
},
last_name:{
type: String
},
email:{
type: String,
unique: true,
required: true
},
password:{
type: String,
required: true
},
is_active:{
type: Boolean,
default: true
},
last_login:{
type: Date
}
});
module.exports = mongoose.model('User', UserSchema);
Controller put function bellow:
exports.updateUser = function (req, res) {
console.log(req.body);
User.findByIdAndUpdate(req.body.user_id, {$set:req.body}, function(err, result){
if(err){
console.log(err);
}
console.log("RESULT: " + result);
});
res.send('Done')
}
Output on console:
Listening on port 3000... { first_name: 'Michal', last_name: 'Test' }
PUT /api/users/54724d0fccf520000073b9e3 200 58.280 ms - 4
The printed params are provided as form-data (key-value). Looks that is not working at least for me any idea what is wrong here?
You have to use req.params.user_id instead req.body.user_id
exports.updateUser = function (req, res) {
console.log(req.body);
User.findByIdAndUpdate(req.params.user_id,{$set:req.body},{new:true}, function(err, result){
if(err){
console.log(err);
}
console.log("RESULT: " + result);
res.send('Done')
});
};
I found the mistake. Note that I'm calling
req.body.user_id
where should be
req.params.user_id
url is (PUT) http://127.0.0.1:3000/api/users/54724d0fccf520000073b9e3
Further, the req.body would have key value as Text and realized as String object, inside the code. Thus, it is useful to parse the string into JSON using JSON.parse(req.body.user) - while the user is the key and { first_name: 'Michal', last_name: 'Test' } is the value.
console.log(req.body);
var update = JSON.parse(req.body.user);
var id = req.params.user_id;
User.findByIdAndUpdate(id, update, function(err, result){
if(err){
console.log(err);
}
console.log("RESULT: " + result);
res.send('Done')
});
Note: the update value is sent to Mongo DB as
{$set: { first_name : 'Michal`, last_name: 'Test' }
Further reference: Mongoose JS documentation - findByIdAndUpdate

Resources