Auto incrementing field in mongoose - node.js

I'm trying to implement an auto incrementing field in mongodb as highlighted in the docs (http://docs.mongodb.org/manual/tutorial/create-an-auto-incrementing-field/). However, I'm not exactly sure what is the best way to do so in mongoose.
I have a CounterSchema and a UserSchema, and I'm wondering where to put the getNextSequence function, and how to call it from the User Schema?
//counter.js
var CounterSchema = new Schema({
category: String,
seq: Number
});
//done in mongo shell
db.counters.insert({category: 'userIndex', seq: 0})
//user.js
var UserSchema = new Schema({
name: String,
UserIndex: Number
}
//per mongodb docs -> not sure where I should insert it
function getNextSequence(name) {
var ret = db.counters.findAndModify(
{
query: { _id: name },
update: { $inc: { seq: 1 } },
new: true
}
);
return ret.seq;
}

Try pre save async middleware like this
User.pre('save', true, function(next, done){
var self = this;
var Autoincrement = mongoose.model('Autoincrement');
if (self.isNew){
Autoincrement.getNext(function(err, val){
if (err) return done(err);
self.auto = val;
done();
});
}else{
// done should be called after next
setTimeout(done,0);
}
next();
});

Related

How to get MongoDB _id of an inserted document

I want to create a document in my MongoDB database and take the _id of the new document.
This is what I'm doing:
const mongoose = require("mongoose");
const billingSchema = require("./models/billing");
const { ObjectId } = require("bson");
const { MongoClient } = require("mongodb");
const mongouri = "***";
var connection = mongoose.createConnection(mongouri);
var Bills = connection.model("Fatturazione", billingSchema, "Fatturazione");
exports.createBill = (b) => {
return new Promise((resolve, reject) => {
Bills.Create(b, function (err) {
if (err) {
reject(err);
} else {
console.log(mongoose.Types.ObjectId(b._id));
resolve();
}
});
});
};
and this is my Schema:
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
//schema define the structure of the document
const billingSchema = new Schema({
data_fatturazione: {
type: Date,
required: true,
},
data_saldo: {
type: Date,
required: false,
},
totale: {
type: Number,
required: false,
},
pagato: {
type: Boolean,
required: false,
},
});
module.exports = billingSchema;
In the console.log() I want to print the _id of the last inserted document but it prints a non-existing id (it doesn't correspond to the _id of the last created document in the database). I also tried without using mongoose.Types.ObjectId() but it prints undefined. I don't understand where is the problem.
I call the function createBill() in another js file, passing an object with the correct fields.
You are trying to get the _id of argument b, which is passed to your createBill, which is logically undefined. Instead you must get the _id from a result of Bill.create, mongoose callbacks take 2 arguments as #Joe mentioned in the comments, so your code must look like this:
exports.createBill = (b) => {
return new Promise((resolve, reject) => {
Bills.Create(b, function (err, result) {
if (err) {
reject(err);
} else {
console.log(result._id);
resolve(result);
}
});
});
};

updating nested documents in mongoDB(node.js)

i am trying to update a value in the object of my embedded schema(comments schema) whose value i had previously stored 0 by default. i have tried all the ways to update but none of the stackoverflow answer worked.
my code is
var checkedBox = req.body.checkbox;
User.updateOne({_id: foundUser._id},{$set :{comments:{_id :checkedBox,cpermission:1,}}},function(err,updatec){
if(err){
console.log(err);
}
else{
console.log("successfull");
console.log(updatec);
}
});
i had comment schema nested in user schema,here foundUser._id is the particular users id,and checkedBox id is the embedded objects particular id. previously my cpermission was 0,set by default,but now i want to update it to 1. although this is updating my schema,but deleting the previous images and comments in the schema aswell.
where am i going wrong?
here is my schema
const commentSchema = new mongoose.Schema({
comment: String,
imagename: String,
cpermission:{type:Number,default:0},
});
const Comment = new mongoose.model("Comment", commentSchema);
const userSchema = new mongoose.Schema({
firstname: String,
lastname: String,
email: String,
password: String,
comments: [commentSchema],
upermission:{type:Number,default:0},
});
userSchema.plugin(passportLocalMongoose);
const User = new mongoose.model("User", userSchema);
First, you need to convert checkbox in the array, as it will be a string if you select a single element
Then wrap it with mongoose.Types.ObjectId as a precaution
Then you can use arrayFilters to update multiple matching array elements
var checkedBox = req.body.checkbox;
if (!Array.isArray(checkedBox)) {
checkedBox = [checkedBox]
}
checkedBox = checkedBox.map(id => mongoose.Types.ObjectId(id))
User.updateOne(
{ _id: foundUser._id }, // filter part
{ $set: { 'comments.$[comment].cpermission': 1 } }, // update part
{ arrayFilters: [{ 'comment._id': {$in: checkedBox }}] }, // options part
function (err, updatec) {
if (err) {
console.log(err);
}
else {
console.log("successfull");
console.log(updatec);
}
});
your comment is the array of documents. if you want to update an element of an array must be select it. for that must be added another condition to the first section of updateOne then in seconde section use $ for update selected element of the array.
User.updateOne(
{_id: foundUser._id, 'comments._id': checkedBox},
{
$set: {'comments.$.cpermission': 1}
}
, function (err, updatec) {
if (err) {
console.log(err)
}
else {
console.log('successfull')
console.log(updatec)
}
})
for more information, you can read this document form MongoDB official website.
Array Update Operators
var checkedBox = req.body.checkbox;
User.updateOne(
{ _id: foundUser._id, "comment._id": checkedBox },
{ $set: { "comment.$.cpermission": 1 } },
function (err, update) {
if (err) {
console.log(err);
} else {
console.log("successfull");
console.log(update);
}
}
);

Virtual field not setting field in mongoose model

I am new to nodeJS and mongoose. I am trying to make a user model that does not save a password as plain text. In other backend frameworks you can accomplish this with an ORM by utilizing a virtual field. I looked up the docs for Mongoose and found that this can be accomplished. Following the dics I created the following Mongoose model. Mind you this is not the final implementation and is for merely testing my understanding of how Mongoose handle virtual fields.
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const userSchema = new Schema({
name: {type: String, required: true},
email: {type: String, required: true},
passwordHash: {type: String, required: true}
});
userSchema.virtual("password")
.get(() => this._password)
.set(val => {
this._password = val;
console.log("setting: ", val);
this.passwordHash = "test";
})
module.exports = mongoose.model("Users", userSchema);
I also have the following test for this model
it("should not save passwords as plain test", done => {
const user = new User({name: "john", email: "john#example.com", password: "password1234"});
console.log(user);
user.validate(({errors}) => {
expect(errors).to.not.exist
});
done();
});
The test fails because I have an error. The error states that the passwordHash field is missing. I know I have that field as required, but I assign the value "test" to this.passwordHash in the set function just like the docs say to do. This is where I get stuck. Any guidance is much appreciated.
I think problem is with this context in userSchema.virtual("password") function
userSchema.virtual("password")
.get(() => this._password) // this points to global object
.set(val => {
this._password = val; // this points to global object
console.log("setting: ", val);
this.passwordHash = "test";
});
This is one of exceptions when you cant use Arrow function.
userSchema.virtual("password")
.get(function() {
return this._password;
})
.set(function(val) {
this._password = val;
console.log("setting: ", val);
this.passwordHash = "test";
});
Let me know is it working now properly.
My general advice: for hash/check passwords use Schema.pre('save') hook. Eg.:
// before save user
userSchema.pre('save', function(next) {
if (this.isModified('password')) { //only if password is modified then hash
return bcrypt.hash(this.password, 8, (err, hash) => {
if (err) {
return next(err);
}
this.password = hash; //save hash in UserSchema.password in database
next();
});
}
next();
});
Schema.pre is part of middleware. More about middleware in mongoose: http://mongoosejs.com/docs/middleware.html

Mongoose query not returning values

I have a CosmosDB collection called plotCasts, which has objects that look like this:
{
...
"owner" : "winery",
"grower" : "Bill Jones",
...
}
I have the following Mongoose schema:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const plotCastSchema = new Schema({
owner: String,
grower: String,
...
});
const ModelClass = mongoose.model('plotCast', plotCastSchema);
module.exports = ModelClass;
However, when I query the database using the query below, I get an empty array for a result. Any idea why?
PlotCast.find({ owner: 'winery' }).lean().exec(function(err, results) {
if (err) {
res.send(err);
} else if (!results) {
res.send(null);
} else {
res.send(results);
}
});
Okay, you named your model plotCast but your collection is plotCasts.
You can force your collection name this way:
const plotCastSchema = new Schema({
owner: String,
grower: String,
...
}, { collection: 'plotCasts' });
Or, simply define your Model in mongoose with the collection name as first argument, this way:
const ModelClass = mongoose.model('plotCasts', plotCastSchema);
Please let me know if that's it :)
the problem is naming the db always saves schema in plural form so it should be like below
PlotCasts.find({ owner: 'winery' }).lean().exec(function(err, results) {
if (err) {
res.send(err);
} else if (!results) {
res.send(null);
} else {
res.send(results);
}
});

How do I insert an element into an existing document?

I have an existing document that contains a nested array of elements (I'm not exactly sure of the terminology here). I have no problem creating the document. The problem arises when I need to insert a new element into the existing document. The code below may clarify what I'm trying to do:
Controller:
var Post = require('./models/post');
app.post('/post/:id/comment', function(req, res) {
var updateData = {
comments.comment: req.body.comment
comments.name: req.body.name,
};
Post.update({_id: req.params.id},updateData, function(err,affected) {
console.log('affected rows %d', affected);
});
});
Model:
var mongoose = require('mongoose');
var postSchema = mongoose.Schema({
post : String,
name : String,
created : {
type: Date,
default: Date.now
},
comments : [{
comment : String,
name : String,
created : {
type: Date,
default: Date.now
}
}]
});
module.exports = mongoose.model('Posts', postSchema);
So, each post can contain multiple comments. I'm just not sure how to insert a new comment into an existing post.
Since comments is declared as array, try to use
Post.update({_id:yourid}, { $push : { comments: { comment: '', name: '' } } }, ...
You can convert the object returned from mongodb in to an js object, and push new comment into the comments array. See the following:
var postSchema = require('./postSchema'); // your postSchema model file
postSchema.findOne({name: 'name-of-the-post'}, function (err, doc) { //find the post base on post name or whatever criteria
if (err)
console.log(err);
else {
if (!doc) { //if not found, create new post and insert into db
var obj = new postSchema({
post: '...'
name: '...'
...
});
obj.save(function (err) {
if (err)
console.log(err);
});
} else {
// if found, convert the post into an object, delete the _id field, and add new comment to this post
var obj = doc.toObject();
delete obj._id;
obj.comments.push(req.body.comment); // push new comment to comments array
postSchema.update(
{
'_id': doc._id
}, obj, {upsert: true}, function (err) { // upsert: true
if (err)
console.log(err);
});
}
console.log('Done');
}
});

Resources