GraphQL: Filter data in an array

gCardinal picture gCardinal · Jan 6, 2017 · Viewed 25.6k times · Source

I'm sure it's a simple thing to do, but I couldn't find anything in either GraphQL's doc or Graphcool's.

Say I have an entity with this schema (new GraphQL user, sorry if I make mistake in the schema representation):

Book {
  name: String!
  author: String!
  categories: [String!]
}

How would I do a query for all books that are part of the "mystery" category? I know I can filter with allBooks(filter: {}), but categories_in: ["mystery"] and categories_contains: "mystery" didn't do the trick.

Answer

marktani picture marktani · Jan 10, 2017

Category model

Thinking a bit more about this situation, creating a Category model is definitely the way to go.

For example, imagine you want to allow readers to subscribe to their favorite categories later. Or, what if you want a list of all existing categories? Using string lists, you would need to query all books and somehow postprocess all obtained categories. Handling this on a model level rather than using string lists feels much more natural.

Instead, you can create a new Category model and add a many-to-many relation between Category and Book. In situations like this, I like to add a unique enum field tag and a string field text. (A unique string field tag alone would also be suitable, probably a matter of taste.)

With this setup, you can easily fulfill data requirements like

Which books are assigned to a given category?

query {
  # query books by unique category tag
  Category(tag: MYSTERY) {
    books {
      id
    }
  }
  # query books by specific category text
  Category(filter: {
    text: "mystery"
  }) {
    books {
      id
    }
  }
}

Which books are assigned to at least one category of a given list?

query {
  allCategories(filter: {
    OR: [{
      tag: MYSTERY
    }, {
      tag: MAGIC
    }]
  }) {
    books {
      id
    }
  }
}

Which books are assigned to all categories of a given list?

query {
  allCategories(filter: {
    AND: [{
      tag: MYSTERY
    }, {
      tag: MAGIC
    }]
  }) {
    books {
      id
    }
  }
}

Related filters

Even though the above queries fulfill the specified data requirements, books are grouped by Category in the response, meaning that we would have to flatten the groups on the client.

With so called related filters, we can turn that around to only obtain books based on conditions defined its related categories.

For example, to query books assigned to at least one category of a given list:

query {
  allBooks(filter: {
    OR: [{
      categories_some: {
        tag: MYSTERY
      },
      categories_some: {
        tag: MAGIC
      }
    }]
  }) {
    id
  }
}