Meteor setInterval and query - javascript

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.

Related

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) { });
}

Updating Parameters Within setInterval() - Parameters do not update

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.

Fire an event when an NFC card is presented

I am attempting to build a webapp on a Chromebook, I need it to read RFID card serial numbers with an ACR122U NFC. I am using chrome-nfc.
I am reading cards happily, but I do not know how to fire an event when a card is presented.
Are there any events in chrome-nfc I can use to know when a card has been presented to the reader?
EDIT: I have been trying to use chrome.nfc.wait_for_tag, but it does not behave as I would expect.
// With a card on the reader
chrome.nfc.wait_for_tag(device, 10000, function(tag_type, tag_id){
var CSN = new Uint32Array(tag_id)[0];
console.log ( "CSN: " + CSN );
});
[DEBUG] acr122_set_timeout(round up to 1275 secs)
DEBUG: InListPassiveTarget SENS_REQ(ATQA)=0x4, SEL_RES(SAK)=0x8
DEBUG: tag_id: B6CA9B6B
DEBUG: found Mifare Classic 1K (106k type A)
[DEBUG] nfc.wait_for_passive_target: mifare_classic with ID: B6CA9B6B
CSN: 1805372086
// with no card on the reader
chrome.nfc.wait_for_tag(device, 10000, function(tag_type, tag_id){
var CSN = new Uint32Array(tag_id)[0];
console.log ( "CSN: " + CSN );
});
[DEBUG] acr122_set_timeout(round up to 1275 secs)
DEBUG: found 0 target, tg=144
Both return the results as above immediately, it does not seem to matter what number I use for a timeout...
If I call the function with no card on the reader, and then immediately put the card on the reader after function call, I get no output in the console.
I'm not familiar with chrome-nfc, but taking a shot in the dark by reverse engineering the source, it looks like you would want to use the wait_for_tag method, like:
chrome.nfc.wait_for_tag(device, 3000, function(tag_type, tag_id) {
// Do your magic here.
});
...Where device is your reader, 3000 is the maximum time to wait (in ms), and replacing // Do your magic here. with your desired logic. If it times out, both tag_type and tag_id will be null.
If you wanted to wait indefinitely, you could just recursively call a function with the above code. Example:
function waitAllDay(device) {
chrome.nfc.wait_for_tag(device, 1000, function(tag_type, tag_id) {
if(tag_type !== null && tag_id !== null)
{
// Do your magic here.
}
waitAllDay(device);
});
}
That's assuming you want it to continue waiting even after a tag has been presented. Wrap the waitAllDay(device); in an else if you want it to stop once a tag is read.
UPDATE: It seems the wait_for_tag method does not work as intended, so I'm proposing a second solution. I'm leaving the existing solution in place in case the method is fixed by the developers of chrome-nfc.
Another thing to try is to use chrome.nfc.read, passing in a timeout option, inside a window.setInterval.
var timer = window.setInterval(function () {
chrome.nfc.read(device, { timeout: 1000 }, function(type, ndef) {
if(!!type && !!ndef) {
// Do your magic here.
// Uncomment the next line if you want it to stop once found.
// window.clearInterval(timer);
}
});
}, 1000);
Be sure and call window.clearInterval(timer) whenever you want it to stop watching for tags.
While I do not consider this a proper solution; here is a workaround I am using for the time being.
function listen_for_tag(callback, listen_timeout){
var poll_delay = 400; //ms
var listen_loop = null;
if(!listen_timeout){
listen_timeout = 99999999;
}
function check_for_tag(){
if(listen_timeout < 0) {
clearInterval(listen_loop);
console.log("we didnt find a tag. finished");
}
chrome.nfc.wait_for_tag(dev_manager.devs[0].clients[0], 10, function(tag_type, tag_id){
console.log ( "FOUND A TAG!!" );
clearInterval(listen_loop);
// handle the callback (call it now)
var C = callback;
if (C) {
callback = null;
window.setTimeout(function() {
C(tag_type, tag_id);
}, 0);
}
});
listen_timeout -= poll_delay;
}
listen_loop = setInterval(check_for_tag, poll_delay);
}

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.

Meteor observe changes added callback on server fires on all item

Tracker.autorun(function() {
DATA.find().observeChanges({
added: function(id, doc) {
console.log(doc);
}
});
});
This code is being called on the server. Every time the meteor server starts, the added function fires for every single item in the database. Is there a way to have the added callback fire only when new items are added?
added will be called for every document in the result set when observeChanges is first run. The trick is to ignore the callback during this initialization period. I have an expanded example in my answer to this question, but this code should work for you:
(function() {
var initializing = true;
DATA.find().observeChanges({
added: function(id, doc) {
if (!initializing) {
console.log(doc);
}
}
});
initializing = false;
})();
Note that Tracker.autorun is a client-only function. On the server I think it only ever executes once.
I struggled with this for a long time. For some reason, David's answer did not work for me - it was firing after the initializing variable was set to false.
This pattern from Avi was successful for me:
var usersLoaded = false;
Meteor.subscribe("profiles", function () {
// at this point all new users sent down are legitimately new ones
usersLoaded = true;
});
Meteor.users.find().observe({
added: function(user) {
if (usersLoaded) {
console.log("New user created: ", user);
}
}
});
Since it is initialization issue, you can do this.
var observerOfMessages = Messages.find({}).observe({
added: function(doc){
if(!observerOfMessages) return;
console.log(doc)
}
});
This is more elegant actually.
Provide a selector for the query which does not match old items. If using mongo ObjectID as _id you could query for items that have _id greater than the latest item's:
const latest = DATA.findOne({}, {sort: {_id: -1}})
DATA.find({_id: {$gt: latest._id}}).observeChanges({
added: function() { ... }
})
Or with createdAt timestamp:
const currentTime = new Date()
DATA.find({createdAt: {$gt: currentTime}}).observeChanges({
added: function() { ... }
})
Here's another way to solve this:
Meteor.subscribe('messages', function() {
var messages = Messages.find();
var msgCount = messages.count();
messages.observe({
addedAt: function(doc, atIndex) {
if(atIndex > (msgCount - 1)) console.log('added');
}
});
});
Should only fire for docs added after the existing amount is delivered. It's important that this goes in an onReady callback for Meteor.subscribe so that the msgCount changes as your subscription does... if for example, you're paginating your subscriptions.
cursor.observe() documentation

Categories