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.
Each collection in the GraphQL schema has a filter argument which can be used to filter the results. You can filter by any custom field. Each field type supports different operators.
An input object type within GraphQL that represents structured inputs. class GraphQLList. A type wrapper around other types that represents a list of those types. class GraphQLNonNull. A type wrapper around other types that represents a non-null version of those types.
Implementing Search Functionality on the Client To implement search via the client, we'll be using the data. fetchMore function from React Apollo. We'll pass a function to our component, onSearch , that will pass in the search query. onSearch will call props.
Category
modelThinking 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
}
}
}
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
}
}
If you are interested in using a hosted GraphQL service, scaphold.io has had this feature for a while now. All connection fields in your API come with a WhereArgs
argument that exposes filters that let you really dig into your data. When you have a list of scalars like this, the WhereArgs include a contains
& notContains
field that allow you to filter results based off the values in your list. This allows you to make a query like this.
query MysteriousBooks($where:BookWhereArgs) {
viewer {
allBooks(where:$where) {
edges { node { title, ... } }
}
}
}
# Variables
{
"where": {
"categories": {
"contains": "mystery"
}
}
}
Just to be complete, you could also do a slight schema readjustment to make this work without having to filter on a scalar list. For example, you could make Category
a node implementing type and then create a connection between Category
and Book
. Although a Book
will likely not have many categories, this would allow you to issue a query like this:
query MysteriousBooks($where: CategoryWhereArgs) {
viewer {
allCategories(where: $where) {
books {
edges { node { title, ... } }
}
}
}
}
# Variables
{
"where": {
"name": {
"eq": "mystery"
}
}
}
If you structure your schema this way then you would also be able to do more filtering on the books in the category without having to loop through every book in your archive. E.G. you could efficiently ask for "all the mystery books written in the last year."
Full disclosure: I work at Scaphold and although I'd love you to try it out no hard feelings if you don't switch over. I'm excited to see people trying and loving GraphQL. If you're curious about how to implement this type of behavior on your own server let me know and I'd be happy to help there as well!
I hope this helps!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With