Reputation: 462
I am using meteor and having difficulty solving this problem since 3 days. I searched over to stackoverflow but the answers didn't seem to satisfy my needs.I have a sales table with the following datas on them.
{
"_id" : 1,
"table" : "Table no 2",
"name" : "Hot Coffee",
"quantity" : 2,
"price" : "$10",
"seller" : "User",
"createdAt" : ISODate("2016-01-06T12:57:17.152Z")
},
{
"_id" : 2,
"table" : "Table no 3A",
"name" : "Hot Coffee",
"quantity" : 1,
"price" : "$10",
"seller" : "User",
"createdAt" : ISODate("2016-02-01T12:58:17.152Z")
},
{
"_id" : 3,
"table" : "Table no 3A",
"name" : "Pizza",
"quantity" : 2,
"price" : "$50",
"seller" : "User",
"createdAt" : ISODate("2016-01-06T12:58:17.152Z")
},
{
"_id" : 4,
"table" : "2A",
"name" : "Pizza",
"quantity" : 5,
"price" : "$50",
"seller" : "User",
"createdAt" : ISODate("2016-02-02T11:55:17.152Z")
},
I am expecting distinct table names with all the quantity added
{
"name":"Hot Coffee",
"quantity": 3
},
{
"name":"Pizza",
"quantity": 7
}
I tried the distinct function but it seems to show a single result only.
Upvotes: 2
Views: 741
Reputation: 103325
Use the aggregation framework for this, but because aggregate isn't supported yet in Meteor, you need to get the aggregation framework package installed - it doesn't do anything to fancy, just wraps up some Mongo methods for you.
Just meteor add meteorhacks:aggregate
and you should be in business. This will add proper aggregation support for Meteor.
Now you would need this pipeline to achieve the desired result. In mongo shell, run the following:
var pipeline = [
{
"$group": {
"_id": "$name",
"quantity": { "$sum": "$quantity" }
}
},
{
"$project": {
"name": "$_id", "_id": 0, "quantity": 1
}
}
];
db.sales.aggregate(pipeline);
Sample Output
{
"result" : [
{
"quantity" : 7,
"name" : "Pizza"
},
{
"quantity" : 3,
"name" : "Hot Coffee"
}
],
"ok" : 1
}
What this aggregation operation does is it uses the $group
pipeline step to group all the documents by the name
field, and with each distinct group you get the accumulated total quantity by using the accumulator operator $sum
on the numerical field quantity for each group. The next pipeline $project
will reshape the fields from the previous pipeline stream documents to the desired structure.
In Meteor, you can publish these results to the Sales
collection on the client side using the following pattern, provided you have added the aggregate package to your meteor app:
Meteor.publish('getDistinctSalesWithTotalQuantity', function (opts) {
var initializing = 1;
function run(action) {
// Define the aggregation pipeline ( aggregate(pipeline) )
var pipeline = [
{
"$group": {
"_id": "$name",
"quantity": { "$sum": "$quantity" }
}
},
{
"$project": {
"name": "$_id", "_id": 0, "quantity": 1
}
}
];
Sales.aggregate(pipeline).forEach(function(e){
this[action]('distinct-sales', e.name, e)
this.ready()
});
};
// Run the aggregation initially to add some data to your aggregation collection
run('added');
// Track any changes on the collection we are going to use for aggregation
var handle = Sales.find({}).observeChanges({
added(id) {
// observeChanges only returns after the initial `added` callbacks
// have run. Until then, we don't want to send a lot of
// `self.changed()` messages - hence tracking the
// `initializing` state.
if (initializing && initializing--) run('changed');
},
removed(id) {
run('changed');
},
changed(id) {
run('changed');
},
error(err) {
throw new Meteor.Error('Aaaaaaaaah! Grats! You broke it!', err.message)
}
});
// Stop observing the cursor when client unsubs.
// Stopping a subscription automatically takes
// care of sending the client any removed messages.
this.onStop(function () {
handle.stop();
});
});
The above observes changes and if necessary reruns the aggregation.
Upvotes: 1