24

Suppose I write:

new Meteor.Collection("foos");
new Meteor.Collection("bars");

Is there an API for accessing those collections by name? Something like Meteor.Collection.get(name), where name is "foos" or "bars"? I know I could write something like

var MyCollections = {
    foos: new Meteor.Collection("foos");
    bars: new Meteor.Collection("bars");
}

and then use MyCollections[name], but I'd prefer to use an existing API if one exists.

Trevor Burnham
  • 76,828
  • 33
  • 160
  • 196

10 Answers10

15

Based on Shane Donelley's mongoinspector https://github.com/shanedonnelly1/mongoinspector

getCollection = function (string) {
for (var globalObject in window) {
    if (window[globalObject] instanceof Meteor.Collection) {
        if (globalObject === string) {
            return (window[globalObject]);
            break;
        };        
    }
}
return undefined; // if none of the collections match
};
user2014160
  • 193
  • 1
  • 5
  • 2
    You can do the same in the server, using ***this*** instead of ***window***. The code is the same, just replace ***window*** with ***this*** to get the collection objects from the global namespace. – Alberto Mar 04 '14 at 14:15
  • 2
    Outside the global context and under strict mode, use `var globals = Function('return this')();` to get a reference to the Global object. So replace ***window*** with ***globals*** in the code. – Alberto Mar 04 '14 at 14:56
  • This pattern is used by [meteor-autocomplete](https://github.com/mizzao/meteor-autocomplete/blob/master/autocomplete-server.coffee#L8). – Dan Dascalescu Nov 18 '14 at 19:53
  • 4
    I didn't understand why you loop `window` object instead of doing: `getCollection = function (string) { return (window && window[string] && window[string] instanceof Meteor.Collection) ? window[string] : undefined; }` – Arthur Aug 03 '16 at 20:35
12

I've just found that package : https://github.com/dburles/mongo-collection-instances/

It allow you to

Foo1 = new Mongo.Collection('foo'); // local
Foo2 = new Mongo.Collection('foo', { connection: connection });

Mongo.Collection.get('foo') // returns instance of Foo1

Mongo.Collection.get('foo', { connection: connection }); 
// returns instance of Foo2

Hope it will help

c.censier
  • 781
  • 7
  • 23
12

This feature was added to Meteor in Feb 2016: "Provide a way to access collections from stores on the client"

It works like this:

Meteor.connection._stores['tasks']._getCollection();

And I was using it as follows to test inserts using the javascript console:

Meteor.connection._stores['tasks']._getCollection().insert({text:'test'});

For the insert it required the insecure package to still be installed otherwise got an access denied message.

malhal
  • 26,330
  • 7
  • 115
  • 133
  • 3
    Is it possible to call this from server and client side ? I tried this code and always get **TypeError: Cannot read property '_stores' of undefined** on the server side. – grahan Sep 01 '16 at 13:56
3

As far as I can see in the collection.js source there currently is no way in the api to get an existing Collection by name, once it has already been initialized on the server. It probably wouldn't be hard to add that feature.

So, why not fork Meteor and submit a patch or create a smart package and share it I'm sure there are others out there who'd like the same feature.

sloth
  • 99,095
  • 21
  • 171
  • 219
limeyd
  • 210
  • 1
  • 3
  • Accepting this answer, as it's based on a reading of [the source](https://github.com/meteor/meteor/blob/master/packages/mongo-livedata/collection.js). – Trevor Burnham Jun 12 '12 at 15:13
  • 8
    found it on `Meteor.connection._mongo_livedata_collections` – Yasin Uslu Dec 08 '14 at 12:12
  • 1
    @nepjua that isn't the correct collection, try inserting to it in the javascript console and you'll see the data does insert into the client but doesn't get sent to the server. – malhal Jun 20 '16 at 22:59
  • 3
    This answer is outdated: there is a way now, at least on the client. See this answer: http://stackoverflow.com/a/37938322/14637 – Thomas Feb 13 '17 at 11:47
2

With https://github.com/dburles/mongo-collection-instances you can use Mongo.Collection.get('collectionname')

Note that the parameter you're inserting is the same one you use when creating the collection. So if you're using const Products = new Mongo.Collection('products') then you should use get('products') (lowercase).

Lukasvan3L
  • 693
  • 6
  • 10
1

Note that they have a return value, so you can just do

var Patterns = new Meteor.Collection("patterns");

and use Patterns everywhere.

And when you need to subscribe to server updates, provide "patterns" to Meteor.subscribe().


If you have the same code for multiple collections, the chance is high that you're doing something wrong from a software engineering viewpoint; why not use a single collection with a type field (or something else that differentiates the documents) and use that instead of using multiple collections?

Tamara Wijsman
  • 12,198
  • 8
  • 53
  • 82
  • 1
    Good to point out what seems to be an obvious omission from the OP, that the `new Mongo.Collection` call returns a collection object. On the other hand, providing simply the collection name to `Meteor.subscribe()` may not work if the publish function uses a different name. See [Understanding Meteor publish/subscribe](http://stackoverflow.com/a/21853298/1269037) for details. – Dan Dascalescu Dec 11 '14 at 20:06
0

Rather than looking, I've just been doing:

Foos = new Meteor.Collection("foos");

or possibly put it inside another object. I haven't really been making a Collections collection object.

Josh
  • 144
  • 1
  • Right, I know I can do that. But I have a large number of collections that share the same code, so I'd like to reference them by name. – Trevor Burnham Jun 11 '12 at 21:59
0

It seems there is no way to get at the wrapped Meteor.Collection object without saving it at creation time, as others have mentioned.

But there is at least a way to list all created collections, and actually access the corresponding Mongo LocalCollection object. They are available from any Meteor Collection object, so to keep it generalistic you can create a dummy collection just for this. Use a method as such (CoffeeScript):

dummy = new Meteor.Collection 'dummy'
getCollection = (name) ->
  dummy._driver.collections[name]

These objects do have all the find, findOne, update et al methods, and even some that Meteor doesn't seem to expose, like pauseObservers and resumeObservers which seem interesting. But I haven't tried fiddling with this mongo LocalCollection reference directly to knowif it will update the server collection accordingly.

Vic Goldfeld
  • 1,179
  • 12
  • 15
  • 2
    Note: .collections is only available on the client, as part of the LocalCollectionDriver (https://github.com/meteor/meteor/blob/master/packages/mongo-livedata/local_collection_driver.js). So this approach would give you all of the collections published to the client, but not necessarily all of the collections overall. – AlexeyMK Jun 30 '13 at 01:40
-1
var bars = new Meteor.Collection("foos");

Judging by what the collection.js does, the line we use to instantiate the collection object opens a connection to the database and looks for the collection matching the name we give. So in this case a connection is made and the collection 'foos' is bound to the Meteor.Collection object 'bars'. See collection.js AND remote_collection_driver.js within the mongo-livedata package.

As is the way with MongoDB, whilst you can, you don't have to explicitly create collections. As stated in the MongoDB documentation:

A collection is created when the first document is inserted.

So, I think what you're after is what you already have - unless I've totally misunderstood what you're intentions are.

Phil
  • 76
  • 1
  • 6
-2

You can always roll your own automatic collection getter.

Say you have a couple of collections called "Businesses" and "Clients". Put a reference each into some "collections" object and register a Handlebars helper to access those "collections" by collections["name"].

i.e. put something like this on the client-side main.js:

collections = collections || {};
collections.Businesses = Businesses;
collections.Clients = Clients;

Handlebars.registerHelper("getCollection", function(coll) {
  return  collections[coll].find();
});

Then in your HTML, just refer to the collection by name:

{{#each getCollection 'Businesses'}}
  <div> Business: {{_id}} </div>
{{/each}}

{{#each getCollection 'Clients'}}
  <div> Client: {{_id}} </div>
{{/each}}

Look ma, no more generic "list all records" boilerplate js required!

crsssl
  • 9
  • 5