Meteor getting double insert on mongo database - javascript

The following code is an event to gathering information regarding expenses and done tasks into a bills database. Everytime I run this process I'm getting 2 entries into the bills database. The first entry is always empty and the second one has the entries I want.
'click .ConfirmCloseCase': function (event) {
var caseID = Session.get('CurrentClosingCaseID');
var TasksToChange = Tasks.find({caseID:caseID,done:true,billed:false});
var ExpensesToChange = Expenses.find({caseID:caseID,billed:false});
// Create new Entry into bills Database
Bills.insert({"expensestotal":0,"taskstotaltime":0}, function(error, result) {
// Set all Tasks to billed
TasksToChange.forEach(function(task){
Tasks.update(task._id, {$set: {"billed": true} })
Meteor.call( 'BillsUpsert', result, {$push: {"tasks": task._id}} );
Meteor.call( 'BillsUpsert', result, {$inc: {"taskstotaltime": task.hours}} );
})
// Set all Expenses to billed
ExpensesToChange.forEach(function(expense){
Expenses.update(expense._id, {$set: {"billed": true} })
Meteor.call( 'BillsUpsert', result, {$push: {"expenses": expense._id}} );
Meteor.call( 'BillsUpsert', result, {$inc: {"expensestotal": expense.amount}} );
})
Router.go('/Bills');
})
},
The Meteor call is like so:
Meteor.methods({
BillsUpsert: function( id, doc ){
Bills.update( id, doc );
}
});
My guess it that I'm getting 2 entries into the database is because the code is running asynchronously. Is this a correct assumption?... and as a followup is there an "easy" fix? :)

I don't think the asynchronous calls are the root cause of your problem but you can significantly simplify this code by moving all your inserts and updates to the server:
'click .ConfirmCloseCase': function (event) {
Meteor.call('closeCase',Session.get('CurrentClosingCaseID'),function(err,result){
if ( err ){
// handle error
} else {
Router.go('/Bills');
}
});
}
Method:
Meteor.methods({
closeCase: function(caseId){
// you need to implement security checks on caseId here!
// Synchronously create new document in Bills collection
var billId = Bills.insert({ expensestotal: 0, taskstotaltime:0 });
// Set all Tasks to billed
var TasksToChange = Tasks.find({ caseID: caseID, done: true, billed: false });
TasksToChange.forEach(function(task){
Tasks.update(task._id, {$set: { billed: true} });
Bills.update(billId,{ $push: { tasks: task._id }, $inc: { taskstotaltime: task.hours }} );
)};
// Set all Expenses to billed
var ExpensesToChange = Expenses.find({ caseID: caseID, billed: false});
ExpensesToChange.forEach(function(expense){
Expenses.update(expense._id, {$set: { billed: true} });
Bills.update(billId,{ $push: { expenses: expense._id }, $inc: { expensestotal: expense.amount }} );
)};
}

After much trial and error I found the problem. The double entry into my database was not related to the code I posted and was due to a stupid mistake of my part of calling the function twice from two different places in my code.
Sorry for wasting people's time with this post.

Related

Why is document.remove() not working in Mongoose, Node.js?

Problem: I try to delete a document from the database using document.remove() in the following codes, but it does not remove this document from database. I am confident remove() is called because the pre hook I set up for 'remove' is called.
// Delete - delete a player and update the team
router.delete("/:player_id", function(req, res) {
Player.findById(req.params.player_id, function(err, foundPlayer) {
if(err) {
console.log(err);
req.flash("error", "Player you want to delete is NOT FOUND!");
res.redirect("back");
return;
}
foundPlayer.remove(function(err, removedPlayer) {
if(!err) {
console.log(removedPlayer); // prints null
}
}); // <<<<<<<<<<<
res.redirect("back");
});
});
I then use model.findByIdAndRemove() in the following codes, and it worked.
// Delete - delete a player and update the team
router.delete("/:player_id", function(req, res) {
Player.findByIdAndRemove(req.params.player_id, function(err, foundPlayer) {
if(err) {
console.log(err);
req.flash("error", "Player you want to delete is NOT FOUND!");
res.redirect("back");
return;
}
res.redirect("back");
});
});
I have two Schemas:
var TeamSchema = new mongoose.Schema({
name: String,
players: [
{
type: mongoose.Schema.ObjectId,
ref: "Player"
}
],
});
var PlayerSchema = new mongoose.Schema({
name: String,
team: {
type: mongoose.Schema.Types.ObjectId,
ref: "Team"
}
});
Thank you so much in advance!
remove() has been deprecated,
try this
Player.deleteOne(req.params.player_id,function(err, removedPlayer) {
if(!err) {
console.log(removedPlayer); // prints null
}
}); // <<<<<<<<<<<
You have used the .remove() on the foundPlayer which was returned by the findByID . You should use the remove directly on the model from which you are trying to remove the document. For eg. The following would work -
Player.remove({_id:req.params.player_id},function(err, foundPlayer){
if(!err)
console.log(foundPlayer);
});
If the player with given _id has been found you will get something logged onto the console like this -
deleted
{ n: 1, ok: 1, deletedCount: 1 }
NOTE :
Trying to use remove() will probably give you an warning saying -
DeprecationWarning: collection.remove is deprecated. Use deleteOne, deleteMany, or bulkWrite instead.
So, you must use deleteOne or deleteMany instead according to your requirement. So your code should be like this -
Player.deleteOne({_id:req.params.player_id},function(err, foundPlayer){
if(!err)
console.log(foundPlayer);
});
You can also choose to use the following if you want to use the foundPlayerdoucment itself to be used in callback -
findOneAndDelete() / findByIdAndDelete() : Finds a matching document, removes it, passing the found document (if any) to the callback. Executes immediately if callback is passed, else a Query object is returned.
Hope this helps !

How to filter Mongoose Schemas that has a `null` value in one of its items inside?

I am fairly new to Coding and JavaScript altogether. I am trying to render the Data inside stored inside my MongoDB in my EJS template.
The specific code is here in app.js
// Individual Customer's page
app.get("/customers/all-customers/:customerPhoneNumber", function (req, res) {
const requestedCustomerPhoneNumber = req.params.customerPhoneNumber;
Customer.findOne({ phone: requestedCustomerPhoneNumber }, function (
err,
foundCustomer
) {
Purchase.find({ customerPhone: requestedCustomerPhoneNumber }, function (
err,
foundPurchases
) {
// console.log(foundPurchases);
foundPurchases.forEach(function (purchase) {
const planPurchaseID = purchase.purchaseID;
CompletedSession.findOne({ planPurchaseID: planPurchaseID }, function (err, foundSession) {
res.render("customer-page", {
customerPurchases: foundPurchases,
pageTitle: foundCustomer.name,
customerID: foundCustomer._id,
customerPhone: foundCustomer.phone,
customerWhatsapp: foundCustomer.whatsappNumber,
customerEmail: foundCustomer.email,
customerDOB: foundCustomer.dob,
customerGender: foundCustomer.gender,
customerRegDate: foundCustomer.registrationDate,
comletedSessions: foundSession.sessions,
});
})
})
});
});
});
In this code, Some of the document has null value of sessions in foundSession, which cause my program to crash when I try to render the array with forEach() loop.
Is there any way to automatically drop foundSession with null value of sessions so that I can only pass the foundSession that has no null value of sessions and prevent my program from crashing?

Collection helper throws a not defined error

Im trying to set up a list of "rooms". The intended sequence:
Click name of the user on his/her profile page
Check for existing room. If yes, go to that room, if not set up new room.
Im using both dburles:collection-helpers and reywood:publish-composite.
Its throwing me this error.
TypeError: Cannot read property 'username' of undefined
at Document.Rooms.helpers.recName (rooms.js:18)
And line 18 is:
return Meteor.users.findOne({ _id: this.receiver }).username;
i.e. _id: this.receiver is undefined.
I also tried to add protective checks in the collection helpers but error remains. I.e. return user && user.username for example.
One thing I note is that, I noticed when I click on the user, it goes to the room linked to the user's id. However when I click back, it jumps to a blank room with a different id thats unrecognised.
The relevant codes:
Server publish
Meteor.publish("onlusers", function (){
return Meteor.users.find({});
});
Rooms.js collection helper
Rooms.helpers({
recName: function() {
return Meteor.users.findOne({ _id: this.receiver }).username;
}
});
User.js (for profile page events)
Template.usersShow.events({
'click .user': function() {
var receiver = this._id;
Session.set('chatId', this._id);
var res = Rooms.findOne({
$or: [
{ owner : this._id },
{ receiver : this._id }
]
});
if(res){
Router.go('roomDetail', { "_id" : res._id });
} else {
var newRoom = Rooms.insert({
owner : Meteor.userId(),
receiver : receiver,
username : Meteor.user().username,
});
Session.set('roomid', newRoom);
Router.go('roomDetail', { "_id" : newRoom });
}
}
});
Your diagnosis:
_id: this.receiver is undefined.
May be misleading. What is also possible is that the user subscription isn't completely loaded when your helper runs. I was helping someone else with a similar problem with publish-composite the other day - the subscription is marked as ready when the parents are ready but the children may not have finished loading yet. I think of this as a bug in publish-composite, all the related objects really need to be there before the subscription can be marked as ready.
Instead of returning:
return Meteor.users.findOne({ _id: this.receiver }).username;
You can do:
var user = Meteor.users.findOne({ _id: this.receiver });
return user && user.username;
So you'll get nothing back until the user object loads but you won't throw an error.

Hook in meteor to catch all collection method errors

I have a collections for Errors that displays to the user. I want to insert into this collection whenever a user receives an error, so it can be displayed in a template.
I have a few hooks on my collections that will reject it.
// only admins can create and update plans
Plans.allow({
insert: function(userId, doc) {
return Roles.userIsInRoles(userId, 'admin');
},
update: function(userId, doc) {
return Roles.userIsInRoles(userId, 'admin');
}
});
// Can only have one active plan currently
Plans.deny({
update: function(userId, doc) {
var now = new Date();
Plans.find({
active: true,
_id: { $in: doc.planIds },
dateStart: { $gt: now },
dateEnd: { $lt: now }
}).count() > 0;
}
});
My question is; can I listen to these events and, when rejected, take a particular action on the client and server?
You can insert on the collection via the callback function on whatever insert/update/remove you have.
If you want to do to on the server way (sing Meteor.methdos/Meteor.call), this is the workflow.
JS
//server
Meteor.method({
insertDoc:function(doc){
Plans.insert(doc)
}
})
//Client
Errors = new Mongo.Collection(null) //client side only
Meteor.call('insertDoc',{test:doc},function(err,result){
if(err){
Error.insert({error:err.reason}) //if there is a error lets insert it
}
})
//and the helper to show the error.
Template.example.helpers({
showError:function(){
return Error.find();
}
})
HTML
<template name="example">
<span>Sorry there was an error: {{error}}</span>
</template>
You got the idea.

Username not changing properly using MongoDB update function?

Below is a snippet of my code where I begin by searching the collection of notes to see if any of them contain the username that I am changing my current session's username to. If the username has not yet been used, it may be changed to that so I change the current session's username and then I update every note to be under this new username then display a changesuccess.jade file. However, when I run the code everything appears to run fine exept the username for each note doesn't change. I feel like it's due to the find() method on the 5th line. Any help would be greatly appreciated!
router.post('/changeusername',function(req, res) {
var newUsername = req.body.username;
var user = req.session.username;
var userFound = notesCollection.find( { owner: newUsername } )
var results = function(infoInput) {
res.render("changedUser.jade", {title: "Username Change",
info: infoInput});
}
var checkChange = function(err) {
if (err) {
results("Username change failed!");
} else {
results("Username changed!");
}
}
console.log(userFound);
if (!userFound.length) {
notesCollection.update({ owner: user },
{ owner: newUsername},
{ multi: true },
checkChange);
} else {
res.render("changedUser.jade", {title: "Username Change",
info: "Username change failed!"});
}
});
If i understand your problem correctly, you are trying to update a collection in mongodb and it is not getting updated.
So the problem is with the way you are calling mongoose#update.
Since you want to update 'owner', you should use mongodb#$set
Mongoose supports the same $set operator in the conditions too.
So just a little correction for your case:
var conditions = { owner: user }
, update = { $set: { owner: newUsername }}
, options = { multi: true };
notesCollection.update(conditions, update, options, callback);
function callback (err, numAffected) {
// numAffected is the number of updated documents
})
So try this now.

Categories

Resources