Inconsistent access to Meteor collection within client code vs. in console - meteor

Something terribly simple is giving me a hard time. I can reproduce the issue by creating a blank app using mrt create, adding only the following:
On line 1, I create a new collection:
Rounds = new Meteor.Collection('rounds');
In the Meteor.isClient conditional code, I add:
Rounds.insert({ x: 1 });
Rounds.find({}).forEach(function (round) {
console.log(round);
});
When I run this code the first time (locally, using meteor run), the expected behavior emerges and the following line is printed to the console:
Object {x: 1, _id: "ps5KdA4b9it2ktQoD"}
If I refresh the browser, however, something unexpected happens. I expect to see multiple lines printed to the console, since I am adding additional rounds to my collection. However, only the most recent round is printed:
Object {_id: "dqMuz6CmK2K9myZn5", x: 1}
If, instead, I run the forEach lines above directly in the console, I get the expected behavior, with one printed line for each round in the collection:
Rounds.find({}).forEach(function (post){
console.log(post);
})
Object {_id: "KAcopbPDE9JC2rShY", x: 1}
Object {_id: "ps5KdA4b9it2ktQoD", x: 1}
Object {_id: "dqMuz6CmK2K9myZn5", x: 1}
Object {_id: "ecbWcRXXLQANy8Xyj", x: 1}
Object {_id: "QsbczHmZZMX59LzTZ", x: 1}
Why is the behavior different in these two cases? And, most importantly, how do I reproduce the behavior I find in the console (which I expected) in the actual code?
Thanks.

It might be that Meteor hasn't actually established a connection to the server yet, remember meteor loads js & html on the client first (at which point all the code is run), and it then tries to connect to the server.
During this state the commands you tell it to insert go in a queue that are inserted all at once when it connects. On the client the callbacks fire instantly if successful at the time of the insert.
So your code to insert is run, when the connection isn't yet established fully.
If you wait for a connection to establish (where Meteor.status().connected == true), or a subscription to complete (by inserting your data in the callback of Meteor.subscribe), then you shouldn't have this problem.
By the time you use the console the connection would already be established which is why you don't have the problem in the console.
You could have a go at also running a console.log(Meteor.status().connection) at the same time you try and insert the document and see.

Related

Postman Collection Run does pause for setTimeout calls

I put a simple setTimeout(function(), 10000) call in the Tests section of a request.
Works fine when I run the step by itself.
When I do a Collection Run, the step just gets executed and Postman moves on without pausing.
Is this by design?
I'd rather not have to put a delay of X seconds for every step.
it works as expected , check the console to see when was the request send
make sure request is saved:
THe orange indication shows that it is unsaved , you have to save it
use the below command in pre-request script.
let moment = require("moment")
console.log("before:", moment())
setTimeout(function () { console.log("after:", moment()) }, 10000)
and check in console:

Firebase + DialogFlow - Realtime database- Cloud function return query result only after second request

I'm writing a simple Google Action which will read the Firebase Realtime Database, and return result in the response. My problem is, the query result is being passed back in response to DialogFlow only after at least 2 attempts.
Below the screenshots showing the end result in the Simulator
First query screenshot
The first line of the response is returned from the Cloud Function, and contains values passed with the "Context". There is no second line in this response.
below is the screen showing the result after sending exactly the same request second time.
Second query screenshot
First line is the same as previously, but this time I also get the second line which contains the query result data.
It looks like my code is "working" (I get the correct data from the database), but for some reason it only works if I trigger it at least 2 times in quick succession.
Below is the code snipped which handle this request:
function googleAssistantHandler(agent) {
let conv = agent.conv();
let outCommandContext = agent.getContext('outcommand');
let outCharacterContext = agent.getContext('outcharacter');
let character = outCharacterContext.parameters.character;
let command = outCommandContext.parameters.command;
agent.add('<prosody rate="140%" pitch="0.4">' + character +' '+ command +'</prosody>');
var movesRef = admin.database().ref('characters/'+character.toLowerCase()+'/moves/');
movesRef.limitToFirst(1).orderByChild("notation")
.equalTo(command.toString()).on("child_added",function(snapshot){
agent.add(`record number is ` + snapshot.key);
});
}
I've tried using once() instead of on() (as it would make more sense in my case... i don't need to listen to changes on the database, i just want to retrieve data once)- but, I couldn't get it to work.
Can you guys help me out understanding why my query returns result only after the second trigger?
Thanks!
you are using a callback method to get the data from database so there is no guaranty that it will be called before your function is returned. to solve the issue, you need to use a Promise and return that Promise in your function so the last few lines of your function will look like this
return movesRef.limitToFirst(1).orderByChild("notation")
.equalTo(command.toString()).on("child_added").then(snapshot= > {
agent.add(`record number is ` + snapshot.key);
});
You need to always use promises when working with databases. Moreover, the first response that you see might be because of the failed function which timed out. If you see your console logs in firebase, you might see the errors. Also check your default response, if it has the text that User said $name or something similar, then that is what causes the issue in the first attempt.
If you still don't get it to work, try logging the returned data and post your logs here.

How to force server-side insert on Meteor's collection.insert

I've got a simple CSV file with 40,000 rows which I'm processing browser-side with papa-parse.
I'm trying to insert them one-by-one into a collection using the techniques in Discover Meteor and other 101 posts I find when Googling.
40000 insert browser-side pretty quickly - but when I check mongo server side it's only got 387 records.
Eventually (usually after 20 seconds or so) it starts to insert server-side.
But if I close or interrupt the browser, the already-inserted records disappear obviously.
How do I force inserts to go server-side, or at least monitor so I know when to notify the user of success?
I tried Tracker.flush() no difference.
I'd go server-side inserts in a Meteor.method, but all the server-side CSV libraries are more complex to operate than client-side (I'm a beginner to pretty much everything programming :)
Thanks!
This is the main part of my code (inside client folder):
Template.hello.events({
"submit form": function (event) {
event.preventDefault();
var reader = new FileReader();
reader.onload = function (event) {
var csv = Papa.parse(this.result, {header: true});
var count = 0;
_.forEach(csv.data, function (csvPerson) {
count++;
Person.insert(csvPerson);
console.log('Inserting: ' + count + ' -> ' + csvPerson.FirstName);
});
};
reader.readAsText(event.target[0].files[0]);
}
});
The last few lines of console output:
Inserting: 39997 -> Joan
Inserting: 39998 -> Sydnee
Inserting: 39999 -> Yael
Inserting: 40000 -> Kirk
The last few lines of CSV (random generated data):
Jescie,Ayala,27/10/82,"P.O. Box 289, 5336 Tristique Road",Dandenong,7903,VI,mus.Proin#gravida.co.uk
Joan,Petersen,01/09/61,299-1763 Aliquam Rd.,Sydney,1637,NS,sollicitudin#Donectempor.ca
Sydnee,Oliver,30/07/13,Ap #648-5619 Aliquam Av.,Albury,1084,NS,Nam#rutrumlorem.ca
Yael,Barton,30/12/66,521 Auctor. Rd.,South Perth,2343,WA,non.cursus.non#etcommodo.co.uk
Kirk,Camacho,25/09/08,"Ap #454-7701 A, Road",Stirling,3121,WA,dictum.eu#morbitristiquesenectus.com
The hello template is a simple form obviously, just file select and submit.
Client code is under client directory.
Person defined in a file in application root.
CSV parsed as strings for now, to avoid complexity.
The records inserted look fine, retrieve by name, whatever.
Person.find().count() browser-side in console results in 40000.
Happy to send the file, which is only 1.5MB and it's random data - not sensitive.
I think call() should work as follows:
On client side
Meteor.call("insertMethod",csvPerson);
And method on server side
insertMethod: function(csvPerson){
Person.insert(csvPerson);
}
In Meteor, on some scenarios, if you don't pass a callback the operation will sync.
If you run the code Person.insert(csvPerson); on the server, the operation will be sync not async. Depending on what you want to do, you might have serious problems in the future. On the client, it won't be sync but async.
Since node.js is an event-based server, a single sync operation can halt the entire system. You've to be really about your sync operations.
For importing data, the best option is to do at server-side inside Meteor.startup(function(){ //import code goes here}).
The solution propose by Sindis works but it slow and if the browser closes (for some reason), you're not keeping a track of the already inserted records. If you use Meteor.call("insertMethod",csvPerson);, this operation will be sync on the client.
The best option on your beginner scenario (not optimal) is to:
1- While (You have record to insert)
2- Call Meteor.call without a callback
3- Count all the inserted fields in the Collection
4- Save this value to localStorage
5- Go back to step 1
This works assuming that the order of insertion is the same on every insert attempt. If you browser fails, you can always get the value from localStorage and skip that number of records.

Meteor: Can't replace document in restricted collection

I am using Meteor 4.2 (Windows) and I am always getting the "update failed: 403 -- Access denied. Can't replace document in restricted collection" when I am trying to update an object in my collection. Strangely I had no problem inserting new ones, only updates are failing.
I tried to "allow" everything on my collection:
Maps.allow({
insert: function () { return true; },
update: function () { return true; },
remove: function () { return true; },
fetch: function () { return true; }
});
But still, this update fails:
Maps.update({
_id: Session.get('current_map')
}, {
name: $('#newMapName').val()
});
Is there something else I can check? Or maybe my code is wrong? Last time I played with my project was with a previous version of Meteor (< 4.0).
Thanks for your help.
PS: Just for information, when I do this update, the local collection is updated, I can see the changes in the UI. Then very quickly it is reverted along with the error message, as the changes has been rejected by the server-side.
Alright, the syntax was actually incorrect. I don't understand really why as it was working well before, but anyway, here is the code that works fine:
Maps.update({
Session.get('current_map')
}, {
$set: {
name: $('#newMapName').val()
}
});
It seems like it must be related to what you're storing in the 'current_map' session variable. If it's a db object, then it probably looks like {_id:<mongo id here>} which would make the update finder work properly.
I ran into the same issues, and found the following to work
Blocks.update {_id:block_id}, {$set: params}
where params is a hash of all the bits i'd like to update and block_id is the mongo object id of the Block i'm trying to update.
Your note about the client side update (which flashes the update and then reverts) is expected behavior. If you check out their docs under the Data and Security section:
Meteor has a cute trick, though. When a client issues a write to the server, it also updates its local cache immediately, without waiting for the server's response. This means the screen will redraw right away. If the server accepted the update — what ought to happen most of the time in a properly behaving client — then the client got a jump on the change and didn't have to wait for the round trip to update its own screen. If the server rejects the change, Meteor patches up the client's cache with the server's result.

Can I create an index in a mongo collection with Meteor? [duplicate]

I am trying to create a two column unique index on the underlying mongodb in a meteor app and having trouble. I can't find anything in the meteor docs. I have tried from the chrome console. I have tried from term and even tried to point mongod at the /db/ dir inside .meteor . I have tried
Collection.ensureIndex({first_id: 1, another_id: 1}, {unique: true}); variations.
I want to be able to prevent duplicate entries on a meteor app mongo collection.
Wondering if anyone has figured this out?
I answered my own question, what a noob.
I figured it out.
Start meteor server
Open 2nd terminal and type meteor mongo
Then create your index...for example I did these for records of thumbsup and thumbsdown type system.
db.thumbsup.ensureIndex({item_id: 1, user_id: 1}, {unique: true})
db.thumbsdown.ensureIndex({item_id: 1, user_id: 1}, {unique: true})
Now, just gotta figure out a bootstrap install setup that creates these when pushed to prod instead of manually.
Collection._ensureIndex(index, options)
Searching inside Meteor source code, I found a bind to ensureIndex called _ensureIndex.
For single-key basic indexes you can follow the example of packages/accounts-base/accounts_server.js that forces unique usernames on Meteor:
Meteor.users._ensureIndex('username', {unique: 1, sparse: 1});
For multi-key "compound" indexes:
Collection._ensureIndex({first_id:1, another_id:1}, {unique: 1});
The previous code, when placed on the server side, ensures that indexes are set.
Warning
Notice _ensureIndex implementation warning:
We'll actually design an index API later. For now, we just pass
through to Mongo's, but make it synchronous.
According to the docs "Minimongo currently doesn't have indexes. This will come soon." And looking at the methods available on a Collection, there's no ensureIndex.
You can run meteor mongo for a mongo shell and enable the indexes server-side, but the Collection object still won't know about them. So the app will let you add multiple instances to the Collection cache, while on the server-side the additional inserts will fail silently (errors get written to the output). When you do a hard page refresh, the app will re-sync with server
So your best bet for now is probably to do something like:
var count = MyCollection.find({first_id: 'foo', another_id: 'bar'}).count()
if (count === 0)
MyCollection.insert({first_id: 'foo', another_id: 'bar'});
Which is obviously not ideal, but works ok. You could also enable indexing in mongodb on the server, so even in the case of a race condition you won't actually get duplicate records.
The Smartpackage aldeed:collection2 supports unique indices, as well as schema-validation. Validation will both occure on server and client (reactivly), so you can react on errors on the client.
Actually why not use upsert on the server with a Meteor.method and you could also send also track it with a ts:
// Server Only
Meteor.methods({
add_only_once = function(id1,id2){
SomeCollection.update(
{first_id:id1,another_id:id2},{$set:{ts:Date.now()}},{upsert:True});
}
});
// Client
Meteor.call('add_only_once',doc1._id, doc2._id);
// actual code running on server
if(Meteor.is_server) {
Meteor.methods({
register_code: function (key,monitor) {
Codes.update({key:key},{$set:{ts:Date.now()}},{upsert:true});
}
...

Resources