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
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 !
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?
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.
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.
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.