Can't Update Data in MongoDB using Mongoose - node.js

These are my Schemas
const dataSchema = new mongoose.Schema({
email:String,
date:String,
amount:Number
})
const userSchema = new mongoose.Schema({
email: String,
password: String,
data:[{
type: mongoose.Schema.Types.ObjectId,
ref: "UserData",
}],
})
const User = new mongoose.model("User", userSchema);
const UserData = new mongoose.model("UserData", dataSchema);
I wish to update Data whenever a user post it. If the Userdata on the particular date already exists i wish to update it by adding the prev amount and new amount
app.post("/insert",(req,res)=>{
const username = req.cookies.username;
const Date = now.toLocaleDateString("en-Uk");
const Amount = req.body.value;
function createNewData(){
const userData = new UserData({
email:username,
date:Date,
amount: Amount
});
userData.save((err)=>{
if(err){
console.log(err);
}else{
console.log('newdatasaved');
res.redirect("/")
}
});
User.findOneAndUpdate({email:username},{$push:{data:userData._id}},(err)=>{
if(err){
console.log('cant push');
}else{
console.log('pushed data');
}
});
}
UserData.findOne({email:username,date:Date},(err,found)=>{
if(err){
createNewData();
console.log('cant find on particular date new created');
}else{
if(found){
let a = Number(found.amount);
let b = Number(Amount)+a;
UserData.findOneAndUpdate({email:username,date:Date},{$set:{amount:b}});
console.log('updated in existing');
res.redirect("/");
}
}
})
})
But it seems the data is always zero in database
See the amount section it is still zero.
Can anyone tell me what am i doing wrong. I have used set method but new data is unable to be posted.

You have to add callback to your update function.
UserData.findOneAndUpdate(
{email:username,date:Date},
{$set:{amount:b}},
function (error, success){}
)
If you don't want to use callback, then you have to use .exec()
UserData.findOneAndUpdate(
{email:username,date:Date},
{$set:{amount:b}},
).exec()

did you check the value of amount? Check the amount value in console.

Related

Query was already executed

I want to calculate averageRating from my reviews collection. So, firstly I make an aggregation pipeline to find the avgRating and ratingQuantity by matching with item ID.
Then I make an post middleware(document middleware) and when any one create a new review then the averageRating and ratingQuantity fields are get updated, but the problem is that this only works on save not on update or delete. So, i make a query middleware and then for getting the document I execute the query but got error Query was already executed Please Help!!!
My reviewModel.js code
const mongoose = require('mongoose');
const movieModel = require('./movieModel');
const reviewSchema =new mongoose.Schema({
review:{
type:String,
required:[true,"review can't be blank"],
maxlength:100,
minlength:10
},
rating:{
type:Number,
required:[true,"review must have a rating"],
max:10,
min:1
},
movie:{
type:mongoose.Schema.ObjectId,
ref:'movies',
required:[true,'review must belong to a movie']
},
user:{
type:mongoose.Schema.ObjectId,
ref:'users',
required:[true,'review must belong to a user']
}
},
{
toJSON:{virtuals:true},
toObject:{virtuals:true}
});
reviewSchema.pre(/^find/,function(next){
this.populate({
path:'movie',
select:'name'
}).populate({
path:'user',
select:'name'
});
next();
})
reviewSchema.index({movie:1,user:1},{unique:true});
reviewSchema.statics.calcAvgRating = async function(movieId){
console.log(movieId);
const stats = await this.aggregate([
{
$match:{movie:movieId}
},
{
$group:{
_id:'$movie',
nRating:{$sum:1},
avgRating:{$avg:'$rating'}
}
}
])
console.log(stats);
const movie = await movieModel.findByIdAndUpdate(movieId,{
ratingsQuantity:stats[0].nRating,
avgRating:stats[0].avgRating
});
}
reviewSchema.post('save',function(){
this.constructor.calcAvgRating(this.movie);
})
reviewSchema.pre(/^findOneAnd/,async function(next){
const r = await this.findOne();
console.log(r);
next();
})
const reviewModel = mongoose.model('reviews',reviewSchema);
module.exports = reviewModel;
My updateOne controller
exports.updateOne = Model=> catchAsync(async(req,res,next)=>{
console.log("handler");
const doc = await Model.findByIdAndUpdate(req.params.id,req.body,{
new:true,
runValidators:true
});
if(!doc)
return next(new appError('Ooops! doc not found',404));
sendResponse(res,200,'success',doc);
})
Try this
reviewSchema.post(/^findOneAnd/,async function(doc){
const model=doc.constructor;
})
Here doc is actually the current executed document and by doing doc.constructor you got its model. On that model you can use the calcAvgRating

Can we Insert data in Object collection in Mongodb

Can anyone please help i tried to insert data in object, but not using array.
I need output like this
{"_id":{"$oid":"5bacbda18ffe1a2b4cb9b294"},
"type":{"name":"prudhvi",
"headings":["Abstract","Introduction","Models"]}}
but i am getting like this
{"_id":{"$oid":"5c52d7484c7644263cbc428a"},
"name":"prudhvi",
"headings":["Abstract","Introduction","Models"],
"__v":{"$numberInt":"0"}}
and I wrote my Collection like this
var articleTypeSchema = new mongoose.Schema({
type: { type: mongoose.Schema.Types.Object, ref: 'typeSchema' }
});
var typeSchema = {
name:String,
headings:[String],
};
var Collections = {
type: mongoose.model('article_types',typeSchema)
}
This is my backend what i wrote
userRouter.post('/addarticletype',(req,res)=>{
Collections.type.create({name:req.body.type,headings:req.body.head},function(err,result){
if (err) return res.status(500).send("There was a problem adding the information to the database");
else
res.status(200).send(result);
console.log(result);
})
})
In your model, change the data type to JSON instead of String and then when you are trying to create a new collection
var typeSchema = {
type:JSON,
};
Step 2: While creating collection, create a JSON object for that key.
userRouter.post('/addarticletype',(req,res)=>{
Collections.type.create({type:{name:req.body.type,headings:req.body.head}},function(err,result){
if (err)
return res.status(500).send("There was a problem adding the information to the database");
else
res.status(200).send(result);
console.log(result);
})
})
Step 3 : Done
You need to rewrite the model as below:
var typeSchema = new mongoose.Schema ({
name:String,
headings:[String],
});
var articleTypeSchema = new mongoose.Schema({
type: { type: mongoose.Schema.Types.Object, ref: 'typeSchema' }
});

Check if ID exists in a collection with mongoose

For instance, I have a collection User:
var mongoose = require('mongoose');
var UserSchema = new mongoose.Schema({
email: String,
googleId: String,
facebookId: String,
displayName: String,
active: Boolean
});
module.exports = mongoose.model('User', UserSchema);
And then I have an ID:
var userID = "some-user-id"
What is the right way to just check if this id exists in the User collection. I don't need it to read the file or return it, I just need the true or false value.
Here is one way to achieve it:
User.findOne({
_id: userID
}, function (err, existingUser) {
But is there faster and more efficient way?
Use count rather than findOne.
This will (under the hood) cause mongoose to use find : http://docs.mongodb.org/manual/reference/method/db.collection.count
findOne() will read + return the document if it exists
On the other hand, find() just returns a cursor (or not) and only reads the data if you iterate over the cursor.
So in our case, we're not iterating over the cursor, merely counting the results returned.
User.countDocuments({_id: userID}, function (err, count){
if(count>0){
//document exists });
}
});
You can now use User.exists() as of September 2019 like so:
const doesUserExit = await User.exists({ _id: userID });
From the docs:
Under the hood, MyModel.exists({ answer: 42 }) is equivalent to
MyModel.findOne({ answer: 42 }).select({ _id: 1 }).lean().then(doc =>
!!doc)
The accepted answer is fine for small collections.
A faster way on larger collections is to simply use this:
const result = await User.findOne({ _id: userID }).select("_id").lean();
if (result) {
// user exists...
}
// or without "async/await":
User.findOne({ _id: userID }).select("_id").lean().then(result => {
if (result) {
// user exists...
}
});
It won't return all fields. I believe they are currently working on a new feature to support what you (and I) want.
In the meantime you could create a plugin, very simple and reusable.
Create an any.js file with this code:
module.exports = function any(schema, options) {
schema.statics.any = async function (query) {
const result = await this.findOne(query).select("_id").lean();
return result ? true : false;
};
}
Then in your model you do this:
var mongoose = require('mongoose');
const any = require('./plugins/any'); // I'm assuming you created a "plugins" folder for it
var UserSchema = new mongoose.Schema({
email: String,
googleId: String,
facebookId: String,
displayName: String,
active: Boolean
});
UserSchema.plugin(any);
module.exports = mongoose.model('User', UserSchema);
...and use it like this:
const result = await User.any({ _id: userID });
if (result) {
// user exists...
}
// or without using "async/await":
User.any({ _id: userID }).then(result => {
if (result) {
// user exists...
}
});
OR you can simply use exists function, without making any async/await:
myData = {_id: userID};
User.exists(myData,(error, result)=>{
if (error){
console.log(error)
} else {
console.log("result:", result) //result is true if myData already exists
}
});
You can play with the result now!
User.exists({ _id: userID }).then(exists => {
if (exists) {
res.redirect('/dashboard')
} else {
res.redirect('/login')
}
})
More info can be found at Mongoose docs.
The accepted answer is excellent, but I would really recommend using estimatedDocumentCount() if you are searching existing document by an indexed property (like _id of X).
On the other hand, this should actually work better and is cleaner.

mongoose unique: true not work [duplicate]

This question already has answers here:
Unique index in mongoose not working
(1 answer)
Mongoose Unique index not working!
(35 answers)
Closed 8 years ago.
why mongoose unique not work at all in this script
var child_process = require('child_process');
// Load required packages
child_process.exec("mongo test --eval 'db.users.drop();'", function(err){
var mongoose = require('mongoose');
console.log(mongoose.version);
mongoose.connect('mongodb://localhost:27017/test');
// Define our user schema
var json = {};
json.phone = { type: String, required: true, unique: true};
var UserSchema = new mongoose.Schema(json);
var Model = mongoose.model('user', UserSchema);
var jp = new Model({ phone: "123456"});
mongoose.connection.on('open', function(){
console.log(jp);
jp.save(function(err){
console.log(err);
var jp2 = new Model({ phone: "123456"});
console.log(jp2);
jp2.save(function(err){
console.log(err);
process.exit();
});
})
});
});
I'm quite confused, the result is like
3.8.20
{ phone: '123456', _id: 54856cceb5b40f7a88fcc2af }
null
{ phone: '123456', _id: 54856cceb5b40f7a88fcc2b0 }
null
Thank you for your help.
This happens because you're saving the duplicated document before mongoose has finished creating the index. Mongoose creates the indexes on the go, after your app has started.
So, to ensure that your document will be saved only after the indexes were created, you have to listen to the index event of your model. For example:
Model.on('index', function (error) {
console.log(jp);
jp.save(function(err){
console.log(err);
var jp2 = new Model({ phone: "123456"});
console.log(jp2);
jp2.save(function(err){
console.log(err);
process.exit();
});
})
});
Now, when you try to save the second document (the duplicated one), your MongoDB will raise an error, because your save calls will just run after the indexes were created.

mongoose.js: _id of embedded document

I am trying to save a task to a list of tasks with mongoose and MongoDB. I want to save it redundantly in the tasks collection and in the corresponding list document as embedded document.
It works fine but one little thing: The list's embedded documents don't have their objectIds. But I need them in order to connect them logically with the documents in the tasks-collection.
My Schemas:
var TaskSchema = new Schema({
_id: ObjectId,
title: String,
list: ObjectId
});
var Task = mongoose.model('task', TaskSchema);
var ListSchema = new Schema({
_id: ObjectId,
title: String,
tasks: [Task.schema]
});
var List = mongoose.model('list', ListSchema);
My controller/router:
app.post('/lists/:list_id/tasks', function(req, res) {
var listId = req.params.list_id;
// 1. Save the new task into the tasks-collection.
var newTask = new Task();
newTask.title = req.body.title;
newTask.list = listId;
console.log('TaskId:' + newTask._id); // Returns undefined on the console!!!
newTask.save(); // Works fine!
// 2. Add the new task to the coresponding list.
list.findById(listId, function(err, doc){
doc.tasks.push(newTask);
doc.save(); // Saves the new task in the list but WITHOUT its objectId
});
res.redirect('/lists/' + listId)
});
Can I use mongoose a different way to achieve that? Or do I have to save the task and then query it before saving it in the list?
Thank you for advise :-)
I solved it by using an awesome feature called populate!
Also dtryon was right: You don't need to declare your _id ObjectIds in your models, they are getting added anyways. And you have to nest this stuff because your task is being saved asyncronously so you must get sure that runs before the other stuff.
Here is the solution:
Schemas:
var TaskSchema = new Schema({
title: String,
list: { type: Schema.ObjectId, ref: 'list' }
});
var Task = mongoose.model('task', TaskSchema);
var ListSchema = new Schema({
title: String,
tasks: [{ type: Schema.ObjectId, ref: 'task' }]
});
var List = mongoose.model('list', ListSchema);
Controller/router:
app.post('/lists/:list_id/tasks', function(req, res) {
var listId = req.params.list_id;
var newTask = new Task();
newTask.title = req.body.title;
newTask.list = listId;
// WHEN SAVING, WRAP THE REST OF THE CODE
newTask.save(function (err){
if (err) {
console.log('error saving new task');
console.log(err);
} else {
console.log('new task saved successfully');
list.findById(listId), function(err, doc){
doc.tasks.push(newTask);
doc.save(function (err){
if (err) {
console.log('error adding new task to list');
console.log(err);
} else {
console.log('new task saved successfully');
res.redirect('/lists/' + listId);
}
});
});
});
});
});
Now it references correctly and with the populate feature you can access the entries very comfortably. Works like charm :-)
I think this may have to do with async. You may not be seeing the ObjectId because you are calling list.findById potentially before you are pushing the newTask. Further, you will get into trouble doing a res.redirect outside of the async operation as well.
Try this:
app.post('/lists/:list_id/tasks', function(req, res) {
var listId = req.params.list_id;
// 1. Save the new task into the tasks-collection.
var newTask = new Task();
newTask.title = req.body.title;
newTask.list = listId;
console.log('TaskId:' + newTask._id); // Returns undefined on the console!!!
// WHEN SAVING, WRAP THE REST OF THE CODE
newTask.save(function (err){
if (err) {
console.log(err);
// do something
}
// 2. Add the new task to the coresponding list.
list.findById(listId, function(err, doc){
doc.tasks.push(newTask);
doc.save(); // Saves the new task in the list but WITHOUT its objectId
// REDIRECT AFTER SUCCESS
res.redirect('/lists/' + listId)
});
});
});

Resources