Aggregate with meteor? (aggregate is not a function)

I get this error:
TypeError: Events.aggregate is not a function
at VueComponent.getyears (frontpage.vue:777)

html (vue,Vuetify)

<v-select
          v-model="selected"
          :items="getyears"

          hide-details
          label="Select"
          single-line
        ></v-select>

script

export default {
  meteor: {
     getyears() { return Events.aggregate(
  { $match: { category: 'running' } },
    { $group : {
        _id: {
            year :  { $substr : ["$thedate", 0, 4 ] }  
               
        },
        count: { $sum: 1 }
    }
              },
    { $sort : { _id : -1}}
);

  }

  },

I’m new to aggregation in Meteor, so how to use it correct?

The mongodb aggregate works when I test in ROBO 3T

1 Like

Aggregation is only available on the server and only using rawCollection. You have a couple of options:

  1. Do the aggregation in a Meteor Method, which you Meteor.call from your Vue component (on-demand). The method would look something like:

    Meteor.methods({
      async doMyAggregation() {
        return await Events.rawCollection().aggregate(
          { $match: { category: 'running' } },
          {
            $group: {
              _id: {
                year: { $substr: ["$thedate", 0, 4] }
    
              },
              count: { $sum: 1 }
            }
          },
          { $sort: { _id: -1 } }
        ).toArray();
      }
    });
    
  2. Use something like tunguska:reactive-aggregate if you want to subscribe reactively to an aggregation.

7 Likes

Is there a better and easier way to count years in my collection?

I just need like:
2019: 19 events
2018: 15 events
2017: 10 events
e.g…

It depends :wink:

“Better” and “easier” are probably very subjective. I’ll start with some questions:

  1. How many documents are in your collection?
  2. How many category: 'running' documents are in your collection?
  3. Is category an index?
  4. Does the client see a subset of those documents?
  5. If so, how many does it see?
  1. How many documents are in your collection? (in time events collection will have100000+)
  2. How many category: 'running' documents are in your collection? (in time there will be 10000+)
  3. Is category an index? (category will get more info from evenscats -> image, name and more)
  4. Does the client see a subset of those documents? (Client will see only the categories where they have approved a activity)
  5. If so, how many does it see? (5-50)

That indicates you will probably be able to do the counting in code on the client itself. You should ensure you are working from an array, so make sure to fetch() your collection’s find(). Then, generate a new array of years and then count matching years into a new array. Something like this might work:

function getyears() {
  return Events.find({ category: 'running' })
    .fetch()
    .map(doc => doc.thedate.substr(0, 4))
    .reduce((years, year) => {
    if (year in years) {
      years[year]++;
    } else {
      years[year] = 1;
    }
    return years;
  }, {});
}
3 Likes

I think you misunderstood what I was asking. If you’re doing a find({category:'running'}) you should ensure the category field in your Events collection is indexed, or you’ll get potentially crippling performance issues - especially with 100000+ documents. That will also affect any publications you may do like that.

Perfekt, but now I have trouble with getting the data names from the getyears function:

I’m trying to get at v-select to work with some data, but I only get object,object https://codepen.io/nordthorus/pen/zXyewY (the getyears works, its just the v-selcted that dont)
I can se the data with: Selected: {{ selected }} when i click the only dropdown with object,object (it like I need to use a v-for with options)

Selected: { “2018”: 1, “2019”: 1 }

I haven’t used Vue and don’t really understand what may be happening, so I can’t offer any immediate help. Perhaps the result from getyears is not in the right format for Vue?

It worked with:

   <select v-model="selected">
     <option v-for='(value, key) in getyears' :value='key' >{{key}} - {{value}} gang(e)</option>
      
    </select>
1 Like

Hi again,

Howto sendt a (categoryId variable to function getyears?

category: ‘running’ -> running have to be the categoryId from the list i’m looping.

Birthday: id: 1
Running: id: 2
Mtb: id:3

I don’t understand what you’re asking.

Sorry, I will try again

Have to find Events where category is the same as a variable that comes from: (VARIABLE = thecat.name)

<v-card v-if="catId == thecat.name"  v-for="thecat in getcats" :key="thecat._id">
<span class="grey-light pt-3 mt-3">
   <select v-model="thisYear">
     <option v-for='(value, key) in getyears' :value='key'>{{key}} - {{value}} gang(e)</option>
      
    </select>
</span>
</v-card>

So howto send a VARIABLE to: Mtb = VARIABLE = thecat.name

export default {
  meteor: {
  getyears: () {
  return Events.find({ category: VARIABLE })
    .fetch()
    .map(doc => doc.thedate.substr(0, 4))
    .reduce((years, year) => {
    if (year in years) {
      years[year]++;
    } else {
      years[year] = 1;
    }
    return years;

  }, {});
}

Unless I still don’t understand:

<option v-for='(value, key) in getyears(thecat)' :value='key'>{{key}} - {{value}} gang(e)</option>

and

export default {
  meteor: {
  getyears(category) {
  return Events.find({ category })
    .fetch()
    .map(doc => doc.thedate.substr(0, 4))
    .reduce((years, year) => {
    if (year in years) {
      years[year]++;
    } else {
      years[year] = 1;
    }
    return years;

  }, {});
}
2 Likes

I get this: [Vue warn]: Error in render: “TypeError: _vm.getyears is not a function”

Is it becasue I use in:

export default {
  meteor: {

so it can not take variables?

Normally I can do waht you wtrite but not in
export default {
meteor: {

Well, as I said earlier, I haven’t used Vue. So, using my approach of “I don’t know what I’m doing, so try this”, you could try this:

<option v-for='(value, key) in meteor.getyears(thecat)' :value='key'>{{key}} - {{value}} gang(e)</option>

Get thsi error: getyears undefiend

Is it possible to insert the code other places then i

export default {
  meteor: {

I’m just not familiar enough with Vue or your code to suggest anything else. Sorry.

I’m missing some coding knowledge so its hard to se how it works.

but can you se howto use this filter with the code you made?
https://codepen.io/nordthorus/pen/mggNgZ

instead of:

  • 2019-04-25
  • 2019-04-25
  • 2018-04-25
  • 2019-04-25

it must write:
2019 3 time
2018 1 time

Hope you can help, its the last thing before I can release a beta of a meteor app :slight_smile: