jbolda / gatsby-source-airtable

MIT License
216 stars 43 forks source link

How to filter child nodes in query? #11

Closed cbfrance closed 6 years ago

cbfrance commented 6 years ago

Hi, thanks again for the work on this very helpful plugin. This is not a bug, just a question about GraphQL filtering strategy. I can ask elsewhere if that seems appropriate.

I have a table of organizations, and each organization has members. Some members are marked as public, but some are private.

I am trying to create a query that lists all the organizations with members, excluding the private members.

(I can't do this on the client side since the private members would still be in the JSON cache.)

So in my organization page template I'm trying to do something like this, which works (slightly modified for brevity):

export const pageQuery = graphql`
  query organizationQuery($slug: String!) {
    organization: airtable(
      fields: { slug: { eq: $slug } }
    ) {
      data {
        Name
        Description
        Person {
              id
              data {
                Name
                Public
              }
          }
      }
    }
  }
`

So I'm hoping to do something like Person(Public: true) and filter out the private members. Any ideas how to approach this — does the plugin support it, or can I somehow configure the schema to accept?

jbolda commented 6 years ago

Hey @chrisblow, glad to help!

I can think of two ways you may be able to address this. In Airtable, you could make a new view that is used just for gatsby and filters out private members. Then in your config, update your query to this new view and gatsby will never see these private members.

If you want to deal with it by the time you see it in Gatsby, I might expect the query to look like the following (untested):

export const pageQuery = graphql`
  query organizationQuery($slug: String!) {
    organization: airtable(
      fields: { slug: { eq: $slug } },
      data: { Name: { Person: { data: { Public: "true" } } } }
    ) {
      data {
        Name
        Description
        Person {
              id
              data {
                Name
                Public
              }
          }
      }
    }
  }
`
cbfrance commented 6 years ago

Ah, thanks for the help.

In Airtable, you could make a new view that is used just for gatsby

I would like to filter at the query level because I have Organizations and People and they relate to each other through a Role table ... I am trying to avoid having to maintain a Public field on the intermediary table, it becomes a chore to make sure that every organization has public people AND roles. (It gives a pretty vague exception when one of the rows is missing from the view, can be hard to track down.)

Also I have a situation where I list both people and organizations — and a person can sometimes have private organizations, and vice versa, and organization can have private people. So I am trying to avoid a situation where a public organization must have only public people. If that makes sense.

data: { Name: { Person: { data: { Public: "true" } } } }`

This is in the top level of the query, so wouldn't this filter out the organizations? I'm trying to get all the organizations but filter all of their people.

Also if I try this, I get an error GraphQL Error Field "data" is not defined by type airtableDataRolesDataPersonQueryList_2.

(It almost works it but it seems like it's too deeply nested for the query to work somehow ... I see it can't get inside Person without an error in GraphiQL.)

Thanks for any ideas!

For completeness, here's the actual query:

export const pageQuery = graphql`
  query organizationQuery($slug: String!) {
    organization: airtable(
      fields: { slug: { eq: $slug } }
    ) {
      data {
        Public
        Name
        Description
        Type
        Sector
        Start_Date
        End_Date
        Roles {
          id
          data {
            Name
            Person {
              id
              fields {
                slug
              }
              data {
                Name
                Public
              }
            }
          }
        }
      }
    }
  }
`

And my configuration:

{
      resolve: `gatsby-source-airtable`,
      options: {
        apiKey: process.env.AIRTABLE_API_KEY,
        tables: [
          {
            baseId: process.env.AIRTABLE_BASE,
            tableName: `Person`,
            tableView: `Grid view`,
            tableLinks: [`Roles`, `Links`]
          },
          {
            baseId: process.env.AIRTABLE_BASE,
            tableName: `Link`,
            tableView: `Grid view`
          },
          {
            baseId: process.env.AIRTABLE_BASE,
            tableName: `Project`,
            tableView: `Grid view`,
            tableLinks: [`Roles`]
          },
          {
            baseId: process.env.AIRTABLE_BASE,
            tableName: `Organization`,
            tableView: `Grid view`,
            tableLinks: [`Roles`]
          },
          {
            baseId: process.env.AIRTABLE_BASE,
            tableName: `Role`,
            tableView: `Grid view`,
            tableLinks: [`Organization`, `Person`, `Project`]
          },
          {
            baseId: process.env.AIRTABLE_BASE,
            tableName: `Area`,
            tableView: `Grid view`,
            tableLinks: [`Organization`]
          },
          {
            baseId: process.env.AIRTABLE_BASE,
            tableName: `Partner`,
            tableView: `Grid view`
          },
          {
            baseId: process.env.AIRTABLE_BASE,
            tableName: `Process`,
            tableView: `Grid view`
          },
          {
            baseId: process.env.AIRTABLE_BASE,
            tableName: `Job`,
            tableView: `Public`
          }
        ]
      }
    },
jbolda commented 6 years ago

Ah yea I see where you are coming from, hmmm.... well, you can really only filter the nodes at the top level. Yea this is a tough one.

The only thing that I can think of is to make a local transformer plugin. That plugin can watch for airtable nodes that are created and generate a new node based on that data. So airtable can serve in your raw data, and your transformer can generate a new set of santized nodes just for your org <---> role <---> person queries. Then you don't need to bother with filters at all.

With so many levels of nesting that need to be sanitized, I don't know if you have a better option to be honest.

cbfrance commented 6 years ago

Ok thanks this is really helpful, at least I know there is not a trivial way to solve it — I'll keep thinking about it, and try to experiment with the transformer plugin.

cbfrance commented 6 years ago

For the record, this approach worked well, and if anyone finds this looking for ways to manipulate nodes with a local plugin using onCreateNode or sourceNodes check out my comment on https://github.com/gatsbyjs/gatsby/issues/8820

jbolda commented 6 years ago

As soon as this is sorted out, this may be another option to use: https://github.com/gatsbyjs/gatsby/pull/8076