How to use find() when it does not immediately return documents. Do I need callback?


#1

Is this because MongoDB is not cached by the time I call find()?
If so is there some command to wait for MongoDB to get cached?
On following link I found an example of defining callback but it didn’t work for me

find() callback

//CREATE COLLECTION.
PlayersList = new Mongo.Collection(‘players’);

//INSERT DOCUMENTS.
if(1==2) {
PlayersList.insert({ name : “David”, score : 10 });
PlayersList.insert({ name : “John” , score : 10 });
PlayersList.insert({ name : “Lucy” , score : 20 });
}

if (Meteor.isClient) {

//RETRIEVE CURSOR.
playersCursor = PlayersList.find();
console.log(playersCursor);

//RETRIEVE ARRAY.
playersArray = playersCursor.fetch();
console.log(playersArray);

//DISPLAY DOCUMENTS.
for(i=0;i<playersArray.length;i++) {
var id = playersArray[i]._id;
var name = playersArray[i].name;
var score = playersArray[i].score;
console.log(id + ’ : ’ + name + ’ - ’ +score);
}

}


#2

Where are you trying to display your documents?


#3

As soon as you subscribe to your meteor server publication it’ll return a handle with the following properties:

Meteor.subscribe returns a subscription handle, which is an object with the following properties:

stop()
Cancel the subscription. This will typically result in the server directing the client to remove the subscription’s data from the client’s cache.

ready()
True if the server has marked the subscription as ready. A reactive data source.

subscriptionId
The id of the subscription this handle is for. When you run Meteor.subscribe inside of Tracker.autorun, the handles you get will always have the same subscriptionId field. You can use this to deduplicate subscription handles if you are storing them in some data structure.

So you could use the ready() function to know when your collection is ready to display.


#4

Imho you are doing it wrong. Meteor use a reactive programming model. You are not supposed to wait on callback. You declare relationship between data and templates with functions linking the two sides, and the function are rerun when the data is changing.
Your functions are meant to be idempotents, which means you can run then one or ten times and still get the same result (assuming parameters doesn’t change).
So you db access should be put either in an autorun (a code smell, imho…) or better in a template helper, which will be automatically rerun when data change (or is available).

You should take your code and enclose it in a Tracker autorun, starting after “if(Meteor.isClient){”, and it will probably work.
Now, add more documents in the console and your code will be rerun.


#5

Thank you for your answers.

@hellstad
I am displaying them in JavaScript Console.

@rova
I am on autopublish currently so I don’t use subscribe.
If I were using subscription am I supposed to make infinite loop checking for ready() to return true?
Any example that could fit in my example?

@vjau
This example is not about templates.
Suppose I need to get some data from DB to make some calculations at some moment.
Like at the beginning while initializing the app.
Tracker autorun? What the hell is that? I’ll try to google?
Idempotents?


#6

If those are one time calculations, you would better do them on the server when data is available.

[quote]
Tracker autorun? What the hell is that? I’ll try to google? [/quote]

Start with meteor doc :wink:


#7

OK I have rewritten the code to use Tracker.autorun() and now the code will be executed the second time when collection is ready on the client. But the code doesn’t look clean. Is there some other way to say "Wait for collections to get cached before executing “if (Meteor.isClient) {}”

//CREATE LOCAL AND REMOTE COLLECTIONS.
people = new Mongo.Collection('people');  

//SERVER.
if (Meteor.isServer) {

  //INSERT DOCUMENTS.
  if( people.find().count() == 0 ) {
    people.insert({ name : "John", age : 20 });
    people.insert({ name : "Bill", age : 30 });
    people.insert({ name : "Lucy", age : 40 }); 
  }

}

//CLIENT.
if (Meteor.isClient) {
  
  console.log("Template.subscriptionsReady="+Template.subscriptionsReady);

  //UPDATE PERSON.
  Tracker.autorun(function () {
    person = people.findOne( {name : "John"} ); 
    if(person) { 
      people.update( person._id, {$set : {age: 60}} ) 
    };    
  });
  
  
  //BODY HELPERS.
  Template.body.helpers({   
    people: function() {
      return people.find(); 
    }  
  });  
    
}

#8

I don’t understand, why would you want to update a record each time your client restart ?
An autorun is not meant to do a one time operation.

To understand reactivity, you have to provide a real life example, what are you trying to achieve ?


#9

I am trying to make little tutorial/example which demonstrates how do you update record in Meteor. And in order to update a record I need to have one already inside DB. And since I want examples to be as simple as possible I dislike having to use Tracker.autorun() in a tutorials which is supposed to focus on updating record. Since I wasn’t able to find elegant way of demonstrating DB commands on client side I have rewritten all of the tutorials by using the commands on the server side where this problem doesn’t occur.

//CREATE LOCAL AND REMOTE COLLECTIONS.
people = new Mongo.Collection('people');  

//SERVER.
if (Meteor.isServer) {
  
  //CLEAR COLLECTION.
  people.remove({});
    
  //INSERT DOCUMENTS.
  people.insert({ name : "John", age : 20 });
  people.insert({ name : "Bill", age : 30 });
  people.insert({ name : "Lucy", age : 40 }); 

  //RETRIEVE DOCUMENT.
  person = people.findOne( {name : "John"} ); 
  
  //UPDATE DOCUMENT.
  people.update( person._id, {$set : {age: 70}} )  
  
  //RETRIEVE DOCUMENTS.
  peopleCursor = people.find(); 
  
  //DISPLAY DOCUMENTS.
  peopleCursor.forEach(function(person){
    var id   = person._id;
    var name = person.name;
    var age  = person.age;
    console.log(id + ' : ' + name + ' - ' + age);    
  });  
  
}