How to do query aggregation in Meteor?

I have this problem and i don’t know how can i solve this.

I have a “UIMenus” collection in my database which holds all my application menu items:

[ {"name": "Option1", "orderTypeIds": [1,2,3]},{"name": "Option2", "orderTypeIds": [3,4]} ]

As you can see there is a field “orderTypeIds” which holds the order type ids associated to the orders in the “Orders” collection that should be retrieved when the menu item is clicked. This is the “Orders” collection:

[{"name": "Order1", "orderTypeId": 1}, {"name": "Order2", "orderTypeId": 4}, {"name": "Order3", "orderTypeId": 1}]

Note that “orderTypeId” is just an id for the type of order, not the actual order id, so the same order type id can be repeated in multiple orders.

What i am not being able to solve is how can i reactively get the number of orders in total for each menu option (so i can show a badge with the orders count on every menu item).

Which is the correct way to solve this? I’ve tried using publish-composite but i can’t wrap my head around this problem.

Many thanks in advance!

If I understand correctly, you want to get the number of orders in total for each menu item (represented by the ‘orderTypeIds’ field). It wasn’t clear from your description if these ids are unique, or if that array will be appended with duplicate ids each time there is a new order placed. If you are appending a new id to that array each time an order is placed, the easiest way I can think of to do that would be to simply count the length of the orderTypeIds array and display that in your menu next to the name. It should reactively update whenever the UIMenus collection changes and items are added or removed from that array. Here’s what the code for that might look like.

menu.js

    Template.menu.onCreated(function(){
      this.subscribe('menuItems');
    });

    Template.menu.helpers({
      menuItems: function(){
        return UIMenus.find({});
      },
      menuItemCount: function(){
        return this.orderTypeIds && this.orderTypeIds.length;
      }
    });

menu.html

    <template name="menu">
      {{#if Template.subscriptionsReady}}
        <nav id="menu">
          {{#each menuItems}}
             <div class="menu-item">
                <span class="name">{{name}}</span>
                <span class="badge">{{menuItemCount}}</span>
             </a>
          {{/each}}
        </nav>
      {{/if}}
    </template>

If you want to do all of the counting server side instead and have it be reactive, this package makes it pretty easy to do.
https://atmospherejs.com/tmeasday/publish-counts

Hi, and thanks for the reply!

You are right i wasn’t clear explaining what the “orderTypeIds” field is. Basically the orders in my application can have a type. Let’s say, an order can be of orderTypeId: 3. Other orders can have the same type id, or other different type id.

The main idea is that the user be able to create his own menu of options which filters orders for one or more of this types, with the flexibility of dynamically creating his own filters.

So i can’t just simply count the “orderTypeIds” array in the UIMenus collection, because there can be a larger number of total orders than actual order types.

I’ve managed to get the actual orders given a menu item, which i need to have anyway since when the menu option is clicked, the application has to show the order list for the menu option selected. I’ve used publish-composite and looks more or less like this:

Meteor.publishComposite('orders', function(menuId){
	return {

		find: function(){
			return UIMenus.find({'_id': menuId});
		},

		children: [
			{
				find: function(uimenu){
					return Orders.find({ { orderTypeId: {$in:  uimenu.orderTypesId } } } )
				}
			}
		]
	}
})

Of course this works for a given menuId, so i still need to have the count (or at least the complete list of orders so i can count them on the client) for all menu options, to display them in the badges.

Might be worth checking this out, meteor chef just recently posted it: https://themeteorchef.com/snippets/aggregations-in-mongodb/

Thanks.

Ultimately i solved the problem without using aggregation, as i needed to do multiple joins on some collections, and i couldn’t find out how to do it in the link you provided.

What i did is just simply publish all the orders which are of any of the orderTypes defined in the UIMenus collection (which i need to have them published anyway), and along them, all the menu items as well:

Meteor.publishComposite('orders', function(){
	return {

		find: function(){
			return UIMenus.find();
		},

		children: [
			{
				find: function(uimenu){
					return Orders.find({ { orderTypeId: {$in:  uimenu.orderTypesId } } } )
				}
			}
		]
	}
})

Lastly, in my template i do something similar to what levid suggested, but adding a query on the client side to count all orders for every respective menu, which was just a bit more complicated than just counting the orderTypeIds array:

menu.html

<template name="menu">
      {{#if Template.subscriptionsReady}}
        <nav id="menu">
          {{#each menuItems}}
             <div class="menu-item">
                <span class="name">{{name}}</span>
                <span class="badge">{{menuItemCount}}</span>
             </a>
          {{/each}}
        </nav>
      {{/if}}
    </template>

menu.js

 Template.menu.helpers({
      menuItems: function(){
        return UIMenus.find({});
      },
      menuItemCount: function(){
        return Orders.find({$in: {orderTypeId: this.orderTypeIds}}).fetch().length
      }
    });

Thanks everyone for the help, much appreciated! :slight_smile: