gatsbyjs / gatsby

The best React-based framework with performance, scalability and security built in.
https://www.gatsbyjs.com
MIT License
55.28k stars 10.31k forks source link

Can I use GraphQL to filter child nodes? #8820

Closed cbfrance closed 6 years ago

cbfrance commented 6 years ago

Context: I'm using gatsby-source-airtable. I have a table of Organizations, and each organization has members listed in a related Person table. Some members are marked as public; there is a IsPublic field on the Person table.

Goal: I am trying to create a GraphQL query that lists all the organizations with members, but exclude the non-public members.

My current query works fine:

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

To filter the members, I'm expecting to change that to something like:

export const pageQuery = graphql`
  query organizationQuery($slug: String!) {
    organization: airtable(
      fields: { slug: { eq: $slug } }
    ) {
      data {
        Name
        Description
        Person {
              id
              data {
                Name
                Person(IsPublic: true) {                <--- pseudocode here
                  id
                  data {
                    Name
                    IsPublic  
                  }
                }
              }
          }
       }
    }
  }
`

So, I don't want to filter the organization, only the members. Any ideas how to approach this?

(I know I can filter the whole result in the template using regular js, but I don't want to have the private members data stored in JSON in /public.)

kakadiadarpan commented 6 years ago

@chrisblow can you share the relevant environment information by running `gatsby info --clipboard?

cbfrance commented 6 years ago

@kakadiadarpan sure thanks!

➜ gatsby info --clipboard

System: OS: macOS High Sierra 10.13.5 CPU: x64 Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz Shell: 5.3.1 - /usr/local/bin/zsh Binaries: Node: 9.8.0 - /usr/local/bin/node npm: 5.6.0 - /usr/local/bin/npm Browsers: Chrome: 69.0.3497.100 Firefox: 62.0.2 Safari: 11.1.1 npmPackages: gatsby: ^2.0.0-rc.25 => 2.0.0-rc.25 gatsby-image: ^2.0.0-rc.2 => 2.0.0-rc.2 gatsby-plugin-favicon: ^3.1.4 => 3.1.4 gatsby-plugin-google-analytics: ^1.0.31 => 1.0.31 gatsby-plugin-manifest: ^2.0.2-rc.1 => 2.0.2-rc.1 gatsby-plugin-netlify: ^2.0.0-rc.6 => 2.0.0-rc.6 gatsby-plugin-netlify-cms: ^3.0.0-rc.5 => 3.0.0-rc.5 gatsby-plugin-offline: ^2.0.0-rc.7 => 2.0.0-rc.8 gatsby-plugin-react-helmet: ^3.0.0-rc.1 => 3.0.0-rc.1 gatsby-plugin-remove-serviceworker: ^1.0.0 => 1.0.0 gatsby-plugin-robots-txt: ^1.3.0 => 1.3.0 gatsby-plugin-sharp: ^2.0.0-rc.7 => 2.0.0-rc.7 gatsby-plugin-sitemap: ^1.2.25 => 1.2.25 gatsby-plugin-styled-components: ^3.0.0-rc.5 => 3.0.0-rc.5 gatsby-source-airtable: ^2.0.1 => 2.0.1 gatsby-source-filesystem: ^2.0.0-alpha.f98688ee => 2.0.0-alpha.f98688ee gatsby-transformer-json: ^1.0.20 => 1.0.20 gatsby-transformer-remark: ^1.7.44 => 1.7.44 gatsby-transformer-sharp: ^2.0.1-13 => 2.0.1-16 gatsby-transformer-yaml: ^1.5.18 => 1.5.18 npmGlobalPackages: gatsby-cli: 2.0.0-beta.3

kakadiadarpan commented 6 years ago

@jbolda can you help out @chrisblow?

jbolda commented 6 years ago

@kakadiadarpan I have been here: https://github.com/jbolda/gatsby-source-airtable/issues/11

cbfrance commented 6 years ago

I did fix this thanks the comment from @jbolda — I was never able to figure out how to filter child nodes in the GraphQL query, so I wrote a short local plugin to manipulate the nodes.

For posterity, here are some more specific notes:

• Create a local plugin • In plugins/my-plugin-name/gatsby-node.js use the sourceNodes API, then getNodes and then you can .filter() them.

(Hat tip to @pieh for taking notes on #3129 which is unrelated but contained my essential clue to not use onCreateNode — thanks!)

Here's the start of my plugin:

// We use sourceNodes API instead of onCreateNode because at this time plugins
// will have created all nodes already
exports.sourceNodes = ({ actions, getNodes, getNode }) => {
  const { createNode } = actions

  // Get all the airtable-generated nodes
  const airtableNodes = getNodes().filter(
    node => node.internal.type === `Airtable`
  )

  // Gather all public organization nodes
  const publicOrganizationNodes = airtableNodes.filter(
    node =>
      (node.table === 'Organization' && node.data.Public)
  )

/// ... do more filtering on the child nodes using regular ES6 techniques.

And then write out the new nodes with createNode():

publicOrganizationNodes.forEach(node => {
    const sanitizedOrganizationData = {
      data: {
        Table: 'Organization',
        Slug: node.fields.slug,
        Fields: node.fields,
        Name: node.data.Name,
        Description: node.data.Description,
        Type: node.data.Type,
        Sector: node.data.Sector,
        Roles_sanitized: getSanitizedRoles(node.data.Roles___NODE)
      }
    }

    const sanitizedOrganizationNode = {
      ...sanitizedOrganizationData,
      id: uniqid(),
      parent: null,
      children: [],
      internal: {
        type: `SanitizeTheNet`,
        contentDigest: crypto
          .createHash(`md5`)
          .update(JSON.stringify(sanitizedOrganizationData))
          .digest(`hex`)
      }
    }
    createNode(sanitizedOrganizationNode)
  })

This seemed like a big workaround for something I thought was GraphQL one-liner, but understanding these APIs better has really opened a lot of lot of flexibility for me. Thanks @jbolda and @pieh!