FullStack
FullStack

Reputation: 6020

meteor query for all documents with unique field

I want to do exactly what this SO question gets at but with Meteor on the server side:

How do I retrieve all of the documents which HAVE a unique value of a field?

> db.foo.insert([{age: 21, name: 'bob'}, {age: 21, name: 'sally'}, {age: 30, name: 'Jim'}])
> db.foo.count()
3
> db.foo.aggregate({ $group: { _id: '$age', name: { $max: '$name' } } }).result
[
    {
        "_id" : 30,
        "name" : "Jim"
    },
    {
        "_id" : 21,
        "name" : "sally"
    }
]

My understanding is that aggregate is not available for Meteor. If that is correct, how can I achieve the above? Performing post-filtering on a query after-the-fact is not an ideal solution, as I want to use limit. I'm also happy to get documents with a unique field some other way as long as I can use limit.

Upvotes: 0

Views: 253

Answers (1)

Blakes Seven
Blakes Seven

Reputation: 50406

There is a general setup you can use to access the underlying driver collection object and therefore .aggregate() without installing any other plugins.

The basic process goes like this:

FooAges = new Meteor.Collection("fooAges");

Meteor.publish("fooAgeQuery", function(args) {
    var sub = this;

    var db = MongoInternals.defaultRemoteCollectionDriver().mongo.db;

    var pipeline = [
        { "$group": {
            "_id": "$age", 
            "name": { "$max": "$name" }
        }}
    ];

    db.collection("foo").aggregate(        
        pipeline,
        // Need to wrap the callback so it gets called in a Fiber.
        Meteor.bindEnvironment(
            function(err, result) {
                // Add each of the results to the subscription.
                _.each(result, function(e) {
                    // Generate a random disposable id for aggregated documents
                    sub.added("fooAges", Random.id(), {
                        "age": e._id,
                        "name": e.name
                    });
                });
                sub.ready();
            },
            function(error) {
                Meteor._debug( "Error doing aggregation: " + error);
            }
        )
    );

});

So you define a collection for the output of the aggregation and within a routine like this you then publish the service that you are also going to subscribe to in your client.

Inside this, the aggregation is run and populated into the the other collection ( logically as it doesn't actually write anything ). So you then use that collection on the client with the same definition and all the aggregated results are just returned.

I actually have a full working example application of a similar processs within this question, as well as usage of the meteor hacks aggregate package on this question here as well, if you need further reference.

Upvotes: 1

Related Questions