Updating Parameters Within setInterval() - Parameters do not update - javascript

This will be my last question, should I receive an answer. I have a nodeJS program, which runs damn near perfectly. However, I appear to be stuck in a loop at the end. The reason for this I believe is because I base when to kill my loop, off of a Database Item, fed into the function through setInterval().
This Parameter, say set at 0, and raised to 1 at the end of the script, always starts at 0 again when the setInterval function is called. It gets updated in the database, and in the script at the end of the function, ut when the function restarts, it has the original value. Any ideas to fix this?
My code
router.get('/run', function(req, res) {
console.log("res.locals.user.username);
const receiver = res.locals.user.email;
// let threshold = parseInt(res.locals.user.threshold);
const check_rate = parseInt(res.locals.user.checkRate);
const email = res.locals.user.Login;
const password = res.locals.user.Password;
const props = {
var1,
var2
};
const switches = "";
adduser.find({
ownerUsername: res.locals.user.username
}).then(function(results) {
res.render('index', {
var12: results
});
setInterval(function() {
let threshold = parseInt(res.locals.user.threshold);
doSomething(threshld, check_rt, email, password, var1,);
}, 10 * 1000);
// console.log("restarting function \n Threshold value is " +res.locals.user.threshold);
})
});
function doSomething(Threshold, checkRate, email, password, var1, receiver, switches) {
console.log("Running dosomething() Now");
if (res.total_missed >= Threshold) {
console.log(res.total_missed);
console.log("res.total_missed is = " + res.total_missed);
Threshld++
User.update({
var1: var1
}, {
thrshld: Threshld,
}, function(err, res) {
//handle it
})
doSomethingElse(password, email, props);
}
}
})
}
Okay so yes this is the best example of my code that I can make without just copy pasting. The issue here is that the line let Threshold = res.locals.user.threshold; is not being updated, and pulls a 0 from the database every time, when the threshold in the database gets incremented and updated to 1 during the doSomething() function.
Once again, thank you for any help in advance!

let Threshold = res.locals.user.threshold; is not pulling from the database. You're pulling it off the response object...
So you should ask yourself, why is a 0 being sent over the wire to your GET endpoint? And also why is it on the response?
Furthermore, are you sure you're updating the user in the DB? This is what I see:
function doSomething(Threshold, checkRate, email, password, var1, receiver, switches) {
console.log("Running dosomething() Now");
console.log("Threshold in Start of Check Function is = " + Threshold);
if (res.total_missed >= Threshold) {
console.log(res.total_missed);
console.log("res.total_missed is = " + res.total_missed);
Threshold++
User.update({
username: var1 // <--- searches for username given some param: var1
}, {
threshold: Threshold, /// < ---- updates this user property with this new value
}, function(err, numberAffected, rawResponse) {
//handle it
})
So you feed the user over var1. Let's look at how you're injecting that user:
doSomething(threshold, check_rate, email, password, var1, receiver, switches);
So you directly pass in var1. No idea what var1 represents to begin with. Bad naming conventions. That aside... where is it set? The only other thing I see, based on code you've provided, is:
const props = {
var1,
var2
};
So, my guess is that you're providing null or undefined to your database update function, and it's not actually updating the user. Then I will further bet, that somewhere along the way, when you query and provide data to your request on the locals.user, it's just always finding 0 because your'e not updating it.
Furthermore, you are also pulling a ton of information off your response object, and not your request...

very simply,
initialize your variable at the beginning of this page, outside of the get request.

Related

Firebase Function admin.database() null parameter in a transaction

I'm using Firebase Functions with the "Spark Plan" (free). This is part of my function:
return query.once("value").then(function(snapshot) {
snapshot.forEach(function(childSnapshot) {
var childData = childSnapshot.val();
if (childData.displayName === ally) {
existAlly = true;
console.log('uid: '+uid)
var ref = admin.database().ref('users/'+uid).transaction(function (current_value) {
console.log('current_value: '+uid)
current_value.mainAlly = ally;
current_value.coins = (current_value.coins || 0) + 10
return current_value;
}).then(() => {
console.log('New Ally added');
return true;
});
...
Here the logs, you can see "'current_value: null'
But, ss you can see in the next picture, the "ref" is correct:
So, is it a billing issue? The "admin.database()" stops working after a while? Or is it something else?
Thanks!
EDIT: I just did another test, and now the error is in "coins", with the same code:
Everything is working as expected. When working with transactions, you can expect that your handler function will get called the first time with null (which you will have to check for), then again with the actual contents of the database. You should review the documentation, and pay special attention to the note that says:
Transaction Function is Called Multiple Times
Your transaction handler is called multiple times and must be able to
handle null data. Even if there is existing data in your database it
may not be locally cached when the transaction function is run.

How do I reject writing value to Firebase if have the same value in my DB? (Javascript)

I'm creating my custom order id with auto-increment generator function for my project. I will state my question here, if you want to know the whole story please read below.
As written in the title, I need a way to reject my set to Firebase and it has to be done in 1 query. Currently, it will write my orderID to Firebase without rejecting it. But I need to reject if there is the same ID in the table.
The short version of my code will be posted here, the whole function will be posted below.
firebase.database().ref('orderCounter/orderIDsChecker/'+orderID).set({
id: orderID,
}, function(error) {
if (error) {
console.log('Order ID fail to generate. Regenerating new ID')
createOrderID(orderCounterRef);
} else {
console.log('Order ID created!')
}
});
}
The story,
I'm creating my own custom order id with auto-increment generator function for my project. The problem is that if multiple users creating order at the same time, it will generate the same id. Yes, I can use transaction() to solve the problem but I have no idea how to use it. Therefore, I have created my own version of the "transaction". With my method, I am able to prevent duplicates id unless 2 or more users create order within 1 second of gap. Or if anyone is kind enough to show me an example of how to write a transaction for my function, I thank you in advance.
The flow of the code is,
Get "currentMonth" and "orderIdCounter" from Firebase -> orderIdCounter +1 and update to Firebase -> start the process of generating order id -> Send the generated id to firebase -> If return success "order ID created", If not "got duplicate id" Re-run the whole process.
Below is the code for my order id generator function.
function createOrderID(orderCounterRef){
var childData = [];
var orderID;
//Get the Current Month and Order ID Counter from Firebase
orderCounterRef.on('value', function(snap) { childData = snapshotToArrayWithoutID(snap); });
var currentMonth = childData[0];
var orderIDCounter = childData[1];
if (orderIDCounter !== undefined){
//Update orderIDCounter on Firebase.
//This is to prevent duplicate orderID when multiple users is creating order at the same time.
var IDCounter = parseInt(orderIDCounter) + 1;
//Set IDCounter to 3 digits
IDCounter = ('00' + IDCounter.toString()).slice(-3);
firebase.database().ref('orderCounter/orderIDCounter').set(IDCounter);
//Handle the process to generate Order ID. Return in YYMMxxx(auto increment) format.
orderID = handleCreateOrderID(currentMonth, (parseInt(orderIDCounter) - 1));
//Check if duplicate ID on firebase
firebase.database().ref('orderCounter/orderIDsChecker/'+orderID).set({
id: orderID,
}, function(error) {
if (error) {
console.log('Order ID fail to generate. Regenerating new ID')
createOrderID(orderCounterRef);
} else {
console.log('Order ID created!')
}
});
}
return orderID;
}
My DB:
You should indeed use a transaction as you have mentioned in your question.
The following should do the trick:
//Declare a function that increment a counter in a transaction
function createOrderID() {
var orderIdRef = firebase.database().ref('orderId');
return orderIdRef.transaction(function(currentId) {
return currentId + 1;
});
}
//Call the asynchronous createOrderID() function
createOrderID().then(function(transactionResult) {
console.log(transactionResult.snapshot.val());
});
If you want to start the counter at a specific value, just create an orderId node in your database and assign a specific value to it, e.g; 1912000.
If you just want to start at 1, you don't need to create a node, it will be automatically created with the first call to the createOrderID() function.
Thank you, #samthecodingman & #Renaud Tarnec for your advice.
I took #samthecodingman's code and change a bit to fit my project. But I use generateOrderID() only to call the result and it works well. But you won't get any value with just the code. I call out another function (connectToFirebase) whenever users enter the page. I am not sure why it works or if this is the right way, but it works for me and that's good enough.
export function generateOrderID(){
var orderId;
var childData = [];
const orderCounterRef = firebase.database().ref('orderCounter/');
//Get the Current Month from Firebase
orderCounterRef.on('value', function(snap) { childData = snapshotToArrayWithoutID(snap); });
//Check ID format YYMMXXX (XXX=auto_increment). Hanlde auto_increment for Year and Month
handleOrderIdFormat(childData[0], orderCounterRef)
//transaction
orderCounterRef.child('orderId').transaction(function(currentId) {
orderId = (currentId||0) +1;
return orderId;
}, function(err) {
if( err ) {
console.log(err)
}
});
return orderId;
}
export function connectToFirebase(){
//Connection Firebase Database
const orderCounterRef = firebase.database().ref('orderCounter/');
orderCounterRef.on('value', function(snap) { });
}

firebase transaction and if-then-else behaviour

I have some changes in my requirements:
Not only Create/Request/Cancel an entire Offer but do some actions on Offer's details:
Here is an offer in the activeOffers list:
activeOffers
-LKohyZ58cnzn0vCnt9p
details
direction: "city"
seatsCount: 2
timeToGo: 5
uid: "-ABSIFJ0vCnt9p8387a" ---- offering user
A user should be able to 'ask for seats' and if it's successful the Offer record should look like this:
activeOffers
-LKohyZ58cnzn0vCnt9p
details
direction: "city"
seatsCount: 1 ----- reduced count
timeToGo: 5
uid: "-ABSIFJ0vCnt9p8387a"
deals
-GHFFJ0vCnt9p8345b ----- the userId of asking user
seatsCount: 1
status: "asked"
But I have 3 problems after executing the source shown below:
(as shown above offer has 2 seats and a user asks for 1 seat)
After execution in my log I have BOTH "Reducing seats count by 1" and "Not enought seats"... i.e: the 'then' and 'else' part of 'if-then-else' :o
function result is [] - i.e. no deal created.
I'm not sure how to do the TODO: part - to add child (the new deal object) under dealsRef using asking userId as KEY because I think I don't need an autogenerated key here.
input data has the following structure:
data
"uid": "-GHFFJ0vCnt9p8345b", ----- the userId of asking user
"id": "-LKohyZ58cnzn0vCnt9p", ----- the id of offer
"details":
"seatsCount": 1
And here is my code:
dealSeats = function(data) {
const TAG = '[dealSeats]: ';
var details = data.details;
var info = data.info;
var entryRef = db.ref('activeOffers/' + data.id);
var entryDetailsRef = entryRef.child('details');
var seatsCountRef = entryDetailsRef.child('seatsCount');
var success = false;
return seatsCountRef.transaction((current)=>{
var value = current;
if (value >= details.seatsCount) {
success = true;
value = value - details.seatsCount;
console.log(TAG + 'Reducing seats count by ' + details.seatsCount);
} else {
console.log(TAG + 'Not enought seats');
}
return value;
})
.then(()=>{
var deal = [];
if (success) {
console.log(TAG + 'Succes');
deal.seatsCount = details.seatsCount;
deal.status = 'asked';
// TODO: here should add the new deal to dealsRef
return deal;
} else {
console.log(TAG + 'Failure');
return deal;
}
})
}
And as you can see - I'm not sure what is the right way to check if transaction is succeeded...
The reference documentation for DatabaseReference.transaction says:
... until your write succeeds without conflict or you abort the transaction by not returning a value from your update function.
So the way to abort the transaction is by not returning any value from your update function. That means the entire first block can be simplified to:
seatsCountRef.transaction((current)=>{
if (current >= details.seatsCount) {
return value - details.seatsCount;
}
})
Now it either returns the new value, or it returns nothing. The latter will then make Firebase abort the transaction.
To detect the final output of a transaction, I find it easiest to work with a completion callback (instead of a Promise), since it gives you all parameters in one call:
seatsCountRef.transaction((current)=>{
if (current >= details.seatsCount) {
return value - details.seatsCount;
}
}, function(error, committed, snapshot) {
if (error) {
console.log('Transaction failed abnormally!', error);
} else if (!committed) {
console.log('We aborted the transaction, because there are not enough seats.');
} else {
console.log('Seat count updated');
}
})
The most common cause for that first error condition will be that the transaction had to be retried too frequently, meaning that too many users are trying to claim seats at the same time. A typical solution here would be to back off, i.e. have the client retry later.

Meteor setInterval and query

I want to have a function that checks every 5 seconds for all entries in database for some value is false and if finds then checks some logic condition and changes the value to true if the logic condition is met.
My function works well until I have something with isItReady: false in my collection. When I don't have, it obviously doesn't find anything and I start getting errors.
How should I do this correctly? I don't want to stop my interval because maybe something will be entered into the collection soon and then my inverval is stopped?
How can I do something like this:
If nothing matches my search criterea - productDate = Products.findOne({isItReady: false}); the interval is stopped and as soon as something new gets inserted I will start the inverval again?
var logicCheck = Meteor.setInterval( function () {
productDate = Products.findOne({isItReady: false}); //query to find all entries with isItReady: false
var timeNow = Date();
var timeCreated = productDate.startOfCountdown;
timeCreated = timeCreated.toString(); //converts timeCreated from object to String(in Mongo its a object)
var productId = productDate._id;
console.log(typeof timeNow) //string
console.log(typeof timeCreated) //string
console.log(timeNow + "timeNow")
console.log(timeCreated + "timeCreated")
if (timeCreated <= timeNow) {
console.log("check") //this works well
Products.update({_id: productId}, {$set: {isItReady: true}}, function(error, result) {
console.log(productId) //all good
if (error){
console.log(error.reason) //check the error
} else{
console.log("File with the id: " + result + " just get update")
}
});
}
}, 5000);
Your approach of polling MongoDB every 5 seconds is very non-Meteoric. You'd be much better off creating a Tracker.autorun function to instantly react to any product that has isItReady == false
For example:
Tracker.autorun(function(){
var notReadyProducts = Products.find({ isItReady: false });
notReadyProducts.forEach(function(p){
if ( your logic ... ) Products.update({ _id: p._id },{ $set: { isItReady: true }});
});
});
This assumes by the way that you're publishing (on the server) and subscribing to (on the client) a set of Products that is going to include these not ready products.
With this pattern no code will be running 99.99% of the time and then at the precise moment that a product is made not ready this code will kick in.
You might want to take a look at this video to learn more about reactive programming and how it completely changes the way you approach common problems. There are many other resources available as well.

Why is hashtagseen[] empty after I call the addposthashtags function?

I am trying to add hashtags in the post's hashtag[] array as a object with a num:1 variable to the users hashtagseen[] array if it is not already in it else add 1 the num if the hashtag is already in the hashtagseen[] array. How do I fix my code? Here is the code, thanks in advanced.
edit: I think I am not finding post.hashtag with this.hashtag and that is why it will not go to else. Just a guess.
The user object
Accounts.createUser({
username: username,
password: password,
email: email,
profile: {
hashtagsl:[],
}
});
collections/post.js
var post = _.extend(_.pick(postAttributes, 'title', 'posttext','hashtags'), {
userId: user._id,
username: user.username,
submitted: new Date().getTime(),
commentsCount: 0,
upvoters: [], votes: 0,
});
calling it
Meteor.call('addposthashtags',this.hashtags,Meteor.user().profile.hashtagsl);
lib/usershash
Meteor.methods({
addposthashtags: function (hashtags,hashtagsl) {
//supposed to make hashtagseen a array with the names from the hashtagsl object in it
var hashtagseen = _.pluck(hashtagsl, 'name');
//supposed to run once per each hashtag in the posts array.
for (var a = 0; a < hashtags.length; a++) {
//supposed set hashtagnumber to the number indexOf spits out.
var hashnumber=hashtagseen.indexOf(hashtags[a]);
//supposed to check if the current hashtag[a] === a idem in the hashtagseen.
if(hashnumber===-1){
var newhashtag = this.hashtags[a];
//supposed to make the object with a name = to the current hashtags
Meteor.users.update({"_id": this.userId},{"$push":{"profile.hashtagsl": {name: newhashtag, num: 1}}})
} else {
var hashi = hashtagseen[hashnumber];
//supposed to ad one to the num variable within the current object in hashtagsl
Meteor.users.update({"_id": this.userId, "profile.hashtagsl.name":hashi},{"$inc":{"profile.hashtagsl.num":1}});
}
}
}
});
Your addposthashtags function is full of issues. You also haven't provided a "schema" for hashtag objects.
addposthashtags: function () {
for (a = 0; a < this.hashtag.length; a++) {
// Issue1: You're querying out the user for every iteration of the loop!?
for (i = 0; i < Meteor.user().profile.hashtagseen.length; i++) {
// Issue2: You're comparing two _objects_ with ===
// Issue3: Even if you use EJSON.equals - the `num` property wont match
// Issue4: You're querying out the user again?
if (this.hashtag[a] === Meteor.user().profile.hashtagseen[i]) {
// Issue5 no `var` statement for hashtagseeni?
// Issue6 You're querying out the user again??
hashtagseeni = Meteor.user().profile.hashtagseen[i];
//Issue7 undefined hashtagsli?
//Issue8 Calling multiple methods for the one action (eg in a loop) is a waste of resources.
Meteor.call('addseen', hashtagsli);
} else {
//Issue9 no `var` statement for newhashtag?
newhashtag = this.hashtag[a];
newhashtag.num = 1;
//Issue8b Calling multiple methods for the one action (eg in a loop) is a waste of resources.
Meteor.call('updateUser', newhashtag, function (err, result) {
if (err)
console.log(err);
});
}
}
}
}
Also, the method has similiar issues:
addseen: function (hashtagseeni) {
// Issue10: var `profile` is undefined
// Issue11: should use `this.userId`
// Issue12: hashtagseeni wouldn't match profile.hashtagseen due to "num" field.
Meteor.users.update({"_id": Meteor.userId, "profile.hashtagseen": profile.hashtagseeni}, {"$inc":{"profile.hashtagseen.$.num":1}});
}
New issues with your new set of code:
Meteor.methods({
addposthashtags: function (hashtags,hashtagsl) {
//Issue1 `hashtag` is undefined, guessing you mean `hashtags`
//Issue2 no `var` for a
for (a = 0; a < hashtag.length; a++) {
//Issue3 no `var` for i
//Issue4 Why are you looping through both?
// don't you just want to check if hashtag[a] is in hashtagsl?
for (i = 0; i < hashtagsl.length; i++) {
if (hashtags[a] === hashtagsl[i].name) {
var hashi = hashtagsl[i].name;
//supposed to ad one to the num variable within the current object in hashtagsl.
// Issue5: This query wont do what you think. Test until you've got it right.
Meteor.users.update({"_id": Meteor.userId, 'profile.hashtagsl':hashi}, {"$inc":{"num":1}});
} else {
// Issue6 `this.hashtag` isn't defined. guessing you mean `hashtags[a]`
var newhashtag = this.hashtag[a];
// Issue7 superfluous statement
var newhashtagnum = num = 1;
// Issue8 Obvious syntax errors
// Perhaps try Meteor.users.update({"_id": this.userId},{"$push":{"profile.hashtagsl": {name: newhashtag, num: 1}}})
Meteor.users.update({"_id": Meteor.userId, 'profile'},{"$addToSet":{"hashtagsl"[newhashtag]=newhashtagnum}})
};
};
};
};
});
I'd suggest you trying the following
1) Assuming that after newhashtag=hashtag[a] you get a JSON object in newhashtag variable, try replacing newhashtag:{num:1}; with newhashtag.num = 1 - this will add the num variable to the object and set the value.
1.a) For debugging purposes try adding some console.log(JSON.stringify(newhashtag)); after each of the two lines where you're setting and changing the newhashtag variable - this way you'll know exactly what you're trying to add to the mongoDB document.
2) The update to increment the views also doesn't seem to me that will work. Couple of things to note here - $set:{'profile.hashtagseen[i]':num++} - MongoDB won't be able to identify the 'i' in 'profile.hashtagseen[i]' and 'num++' is not how increments are done in Mongo.
I'd suggest you look into the $inc and to the positional update documentation of MongoDB.
Your final increment update statement will look something like
Meteor.users.update({"_id": Meteor.userId, "profile.hashtagseen": profile.hashtagseen[i]}, {"$inc":{"profile.hashtagseen.$.num":1}});
I see that executing addposthashtags is in the client, and you must to pay attention because this function will execute in minimongo and doesn't work all operations. First you try execute this operation under mongo if it's work you must to create one function inside the folder server.
Add text of the documentation of Minimongo
In this release, Minimongo has some limitations:
$pull in modifiers can only accept certain kinds of selectors.
findAndModify, aggregate functions, and map/reduce aren't supported.
All of these will be addressed in a future release. For full Minimongo
release notes, see packages/minimongo/NOTES in the repository.
Minimongo doesn't currently have indexes. It's rare for this to be an
issue, since it's unusual for a client to have enough data that an
index is worthwhile.
You try create one method on the server, with the same operation.
Server:
Meteor.methods({
updateUser: function (newhashtag) {
Meteor.users.update(this.userId,
{
$addToSet: {'profile.$.hashtagseen': newhashtag}
});
}
});
Client:
Meteor.call('updateUser',newhashtag,function(err,result){
if (err)
console.log(err);// there you can print the erro if there are
});
Minimongo doesn't support alls operation, for test you can to execute in the console for testing the method if supported. After that you can to execute the operation under mongo directly, that clears your doubts.

Categories