Subscribing to list of all fields to filter collection by

Hi!

I’m displaying a paginated collection of documents and respectively subscribe to just what’s on the current page.

However, I’d like for the user to be able to filter on fields on this collection. Since this is a filter, the user should be able to filter on ALL possible existing Documents, not just what’s on the page.

I was thinking of having a separate subscription and using projections to just get a list of ALL the possible filter options. However, I’d then be subscribing to the same collection twice (once for the full paginated Docs, and another for ALL of certain filter field(s)).

What’s the suggested pattern to handle something like this? Thanks!

You can create a method that gets the possible filter values from the server, so the actual subscription on the client is kept small, maybe even page through the results with skip and limit.

Or, if you don’t really need reactivity, you could use a method to get the values from the server, not using subscriptions at all. This method could return everything you need, the filter, initial results, maximum results (for paging), etc.

2 Likes

Yeah, I just need a 1-time load of all filters…no reactivity. Making a 1-time call with a Method sounds ideal. I’ll try this. Thanks!

Do you happen to know of an efficient way to get all unique values of a Collection. So say I have 100 Documents on a collection. And I want the User to be able to filter on all possible Color fields. Currently, I’m retrieving all Color fields from the Collection using a Projection and am then deduping that list.I’m just wondering if there’s a more efficient way to do this. Thanks!

MongDB supports a distinct operation on a field, which will return an array of all uniq values for a field. On the server you can get a handle to the raw mongo collection which provides access to the distinct function. You can look at this ticket or just google for meteor mongodb distinct

3 Likes

Thanks! Yeah, I looked into that. Was hoping distinct would be brought into the native Meteor Mongo lib already. Don’t wanna have to install a separate lib for it

I don’t think you need a separate library, you’re just accessing the raw collection.

CollectionName.rawCollection().distinct(key, query, options)

2 Likes

No separate library needed, as @alawi said. You can just monkey patch the functionality on the server, I got it to work like this

import { Mongo } from 'meteor/mongo';
//
// monkey-patch distinct function
//
if( Meteor.isServer && Mongo.Collection.prototype.distinct === undefined ) {
  Mongo.Collection.prototype.distinct = function(key = '', query = {}, options = {}){
    // the rawCollection returns a promise
    const distinctPromise = this.rawCollection().distinct(key, query, options);
    return distinctPromise.await();
  }
}
export const Content = new Mongo.Collection('content');

I can then call it on the the server like

console.log( Content.distinct('title') )

To also enable it on the client you need to implement it as a Meteor method

2 Likes

Thanks! This is really helpful. I’ve got a single Distinct to work, but want to use it on multiple keys. Seems you need to use an aggregate to do this.

I’m trying something like

const foos = Foo.rawCollection().aggregate([

    {

      $group: {

        _id: 0,

        names: {

          $addToSet: '$name'

        },

        ages: {

          $addToSet: '$age'

        }

      }

    }

  ]).await();

But I keep getting Trackers.rawCollection(...).aggregate(...).await is not a function

Why is it that chaining to .await() works for distinct(), but not aggregate? Is there a good design pattern for how to handle async/await from other npm packages in Meteor?

Query to do mult-key distinct was found https://www.tutorialspoint.com/performing-distinct-on-multiple-fields-in-mongodb

Thanks

Ah! Well looks like distinct returns a Promise but Aggregate returns an AggregationCursor

I solved this by using AggregationCursor.toArray().await(). toArray() . Thanks @peterfkruger! Docs indicate that toArray() is actually what returns the Promise

Meaning that AggregationCursor.toArray() itself already returns a Promise.

@see https://mongodb.github.io/node-mongodb-native/2.0/api/AggregationCursor.html#toArray

Thanks! Yeah, that’s what I meant to say in my prev post

Our fellow forum member @robfallows has written this great article on this subject in 2017: Meteor Promises

Perfect, thanks! I’ll digest this. @robfallows is wonderful!