Grapher filtering

Hi everyone!
I was wondering if this kind of filtering is possible with Grapher.
Let’s say we have two collection with a 1-N relationship called “Companies” and “Employees” where the Employees have a boolean type field called for example “isHardworker”. Can I somehow fetch only the companies (without fetching any of it’s employees!) that have at least one hardworker employee?

Also if you could link me a working a meteor-grapher (doesn’t have to implement the above filtering) example app that would be really helpful because I’m a bit confused (altough I read the docs) about how to implement Grapher inside Meteor, where to store/call Grapher related things, do I still need publications etc.

Thanks in advance!

I will start by answering your last question: https://github.com/cult-of-coders/grapher-boilerplate this is where you can checkout a Meteor project with Grapher.

To answer your question: Yes!

You can fetch companies with at least one hardworker employees, everything is explained here: https://github.com/cult-of-coders/grapher/blob/master/docs/denormalization.md

It requires some additional work, but hey, we’re not in relational world anymore :slight_smile: things that were trivial with NoSQL they require a bit more work, but slowly it’s evolving and they will become trivial for sure.

Thank you for your fast answer!

This might be a newbie question but as I was browsing through the boilerplate code I found that in both the “/imports/startup/client/index.js” and “/imports/startup/server/index.js” files the ‘/imports/api/grapher’ is imported but on the latter ‘/imports/api/grapher/exposures’ is also imported.

I’m a bit confused, wouldn’t the first import be enough to get everything from the grapher folder or am I missing something?

Please read the documentation from start to finish, you will understand :slight_smile:

I have an other question regarding the filtering, nested denormalization so I don’t want to open a new thread.

Suppose that given the above example I have an other 1-N relationship between the “Employees” and “Pets” collections where the Pets have a String field called “type” which can be “dog” or “cat”.
Lets say that I’d like to find only the companies that have employees who have dogs.
Would it be possible?

I tried something like this but I didn’t get what I wanted:

//Companies->Employees link
Companies.addLinks({
    'employees': {
        collection: Employees,
        inversedBy: 'company',
        denormalize: {
            body: {
                pets: {
                   type: 1
                }
            },
            field: 'employeesCache',                        
        }
    }
})

//Companies<-Employees->Pets link
Employees.addLinks({
    'pets': {
        collection: Pets,
        inversedBy: 'owner',
    },
    'company': {
        type: 'one',
        collection: Companies,
        field: 'companyId'
    }
})

//Pets->Employees link
Pets.addLinks({
    'owner': {
        type: 'one',
        collection: Employees,
        field: 'ownerId'
    }
})

Whenever I try to do a query based on “employeesCache” it’s not working as I would expect it to be.
Is something like this possible with grapher or should I modify my database?

I also tried to do a regular query like this:

Companies.createQuery({
     employees: {
            pets: {
                $filters: {
                    type: 'dog',
                 },
                 name: 1,
                 type: 1
            }
     }
});

But this also gives me the id’s of the companies, pets (without the employees/name, type fields) that did not match the query, can I avoid this somehow?

The queried documents look like this:

[  //I want this
  { 
    _id: 'JFWbiRh9wG2ikEafr', 
   employees: [ [Object], [Object] ] 
  },
   //I don't need this
  {  
    _id: 'mJ5F93hMfmDgpXG8w' 
  }  
]

You need to apply filter on employeesCache object and make sure you migrated it.

I tested my query simply like this:

Companies.createQuery({
    employeesCache: 1
})

But all I get is objects like this:

[
  {
     _id: 'RYjwyx36nGiWw6G9z',
    employeesCache: [ { _id: '8shqu5B4Mrxt94kph' }, { _id: 'rZ8ozPwLdghAj47p5' } ]
  }
]

Where the inner ids are employee ids, so I don’t get any info on pets.

I tried migrating too with this package: percolate:migrations

// migartion.js which is imported to server/main.js
import { Meteor } from 'meteor/meteor';
import { Migrations } from 'meteor/percolate:migrations';
import { migrate } from 'meteor/herteby:denormalize'

Migrations.add({
  version: 1,
  name: 'Denormalization in the companies collection.',
  up() {
    migrate('companies', 'employeesCache');
  }
});

Meteor.startup(() => Migrations.migrateTo(1));  

But then I get this error message:

 Error: no migration found for companies - employeesCache

I’ve been reading the grapher/percolate:migrations/herteby:denormalize docs/issues but I couldn’t really find a solution.

@relicho I just now realise that you are trying to denormalize a link within a link. This is not supported. Denormalisation works one level link only. So you would have to denormalize first pets inside Employees, and then, if needed, to denormalization inside Companies for that cached field.

@diaconutheodor Thank you, I think this solves my problem, altough I might have to modify my database because in my project I actually I have 4 collections that are linked to eachother like this:
the first collection is linked to the second and the second to the third, lastly the third to the fourth collection (1-N links). I’d like to filter the first collection’s elements based on the elements in the fourth collection that are connected to them.

But let’s go back to my first post where I asked if it’s possible to fetch only the companies that have at least one hardworker employee.

Now thanks to your help I can do that query like this with denormalization:

Companies.createQuery({
    $filters: {
        'employeesCache.isHardworker': true
    },
    companyName: 1
})

The companies get filtered right but with every fetched company I also get the employeesCache and not only the name of the company. Is this behaviour normal?

1 Like

It’s not normal since you didn’t specify it in your fields, I think there might be a bug in Grapher’s clean-up process, maybe it doesn’t pick up on it because it’s a nested filter? I will check. Please file an issue on GitHub describing this problem.

Since your queries are like that, are you sure a relational database (postgres) isn’t better suited for you ? Or maybe running an aggregation pipeline instead of filling up denormalized caches. You’ll have a waterfall of updates on every employee update, this may result in time, to unpredictable number of changes and updates.

I prefer to only use denormalisation one level only, it’s the safest.