1

I'd like to create a method that returns the count of a generic collection.

Calling the method would look something like this:

Meteor.call('getCollectionCount', 'COLLECTION_NAME');

And the result would be the collection count.

The server method code would look something like this:

getCollectionCount: function (collectionName) {
    return window[collectionName].find().count();
}

This won't work because window isn't defined on the server, but is something similar possible?

Eliezer Steinbock
  • 4,728
  • 5
  • 31
  • 43

4 Answers4

3

Use global instead of window.

Note that this uses the variable name assigned to the collection object, not the name given to the collection. For this to work with Meteor.users you need to assign another variable name.

if (Meteor.isServer) {
  users = Meteor.users;
}

if (Meteor.isClient) {
  Meteor.call('count', 'users', function (err, res) {
    // do something with number of users
  });
}

Also probably a good idea to check that global[collectionName] is actually a collection.

Neil
  • 2,137
  • 16
  • 24
2

I came up with this code which makes the following assumptions :

  • collections are declared in the global scope as top level objects.
  • collections are searched by collection name, not the collection variable identifier.

So client code should declare their collections like this :

MyCollection=new Meteor.Collection("my-collection");

And use the function like this :

var clientResult=Meteor.call("getCollectionCount","my-collection",function(error,result){
  if(error){
    console.log(error);
    return;
  }
  console.log("actual server-side count is : ",result);
});
console.log("published subset count is : ",clientResult);

The method supports execution on the client (this is known as method stub or method simulation) but will only yield the count of the collection subset replicated client-side, to get the real count wait for server-side response using a callback.

/packages/my-package/lib/my-package.js

getCollection=function(collectionName){
  if(collectionName=="users"){
    return Meteor.users;
  }
  var globalScope=Meteor.isClient?window:global;
  for(var property in globalScope){
    var object=globalScope[property];
    if(object instanceof Meteor.Collection && object._name==collectionName){
      return object;
    }
  }
  throw Meteor.Error(500,"No collection named "+collectionName);
};

Meteor.methods({
  getCollectionCount:function(collectionName){
    return getCollection(collectionName).find().count();
  }
});

As Meteor.users is not declared as a top level variable you have to account for the special case (yes, this is ugly).

Digging into Meteor's collection handling code could provide a better alternative (getting access to a collection handle by collection name).

Final words on this : using a method call to count a collection documents is unfortunately non-reactive, so given the Meteor paradigm this might be of little use.

Most of the time you will want to fetch the number of documents in a collection for pagination purpose (something like a "Load more" button in a posts list for example), and as the rest of the Meteor architecture you'll want this to be reactive.

To count documents in a collection reactively you'll have to setup a slightly more complicated publication as showcased in the "counts-by-room" example in the docs.

http://docs.meteor.com/#meteor_publish

This is something you definitely want to read and understand.

This smart package is actually doing it right :

http://atmospherejs.com/package/publish-counts

It provides a helper function that is publishing the counts of any cursor.

saimeunt
  • 22,666
  • 2
  • 56
  • 61
0

Keep track of the collections on some other property that the server has access too. You could even call it window if you really wanted to.

var wow = new Meteor.Collection("wow");
collections["wow"] = wow;

getCollectionCount: function (collectionName) {
    return collections[collectionName].find().count();
}
Explosion Pills
  • 188,624
  • 52
  • 326
  • 405
  • Thanks. So the motivation behind my question is being able to make a package that does this. Users should be able to use the package even if they haven't set up their application as you've described. – Eliezer Steinbock Aug 12 '14 at 17:08
0

If you don't want the package users to change how they work with collections in the app then I think you should use MongoInternals to get collections by name from the db. Not tested but here is an example:

//on server
Meteor.methods({
  count: function( name ){
    var db = MongoInternals.defaultRemoteCollectionDriver().mongo.db;
    var collection = db.collection( name );
    return collection && collection.count({});
  }
});

Another example of MongoInternals use is here. Documentation of the count() function available from the mongo driver is here.

Community
  • 1
  • 1
user728291
  • 4,138
  • 1
  • 23
  • 26