GraphQL: Filter data in an array - graphql

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.

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
}
}

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!

Related

Strapi not able to filter relations value

I have two collection. post and common. I have defined common has many Post relation. The relation is called contains.
Post as component called place with country field, which is enumeration with country codes ( two character)
So relations will have posts which might have different countries.
I trying to filter the relations values. What I want is,the relation field should only have posts with specific country code.
What i have tried
/api/commons?populate[0]=contains.basic&populate[1]=basic&filters[contains][place][country][$eq]=AF&populate[2]=contains.place
But for this the relation will have posts that have different country codes.
Is it possible to filter the relations values? Or is there better way to structure this?
not sure i fully understand your data layout, however it seems that instead filtering the relation of Common you would get the data more easily if you just filter the posts:
/api/posts?filters[$and][0][common][$eq]=commonId&filters[$and][1][country][$eq]=AF
with query:
const str = qs.stringify(
{
filters: {
$and: [{ common: { $eq: 'commonId' } }, { country: { $eq: 'AF' } }],
},
},
{ encodeValuesOnly: true }
);

WPGraphQL/GraphQL custom post type filter

I have created custom post type with 2 taxonomies, which I want to filter first by country taxonomy and then by practice area taxonomy. The problem is that I'm not allowed (can't find how to do it in docs) to do filtering in one step. The goal is to achieve all post types "experience" from "experience_countries" - "estonia" taxonomy, and that have "practice_area_category" - "all" taxonomy.
So graphQL schema allows me to do like this:
What this query returns:
Instead of my goal, I'm getting post types for all countries (estonia and latvia, instead of only estonia) that match "practice_area_category" - "all" taxonomy.
I would greatly appreciate, any advice!
From what I understand you want to filter posts by experienceCountries then further filter with experiencePracticeAreas.
You don't need two separate custom taxonomies, the first custom taxonomy is enough "experienceCountries".
In WordPress do the following:
In experienceCountries taxonomy/category you have "Lativa", "Estonia" ...etc
In your custom post type go to the experienceCountries category and add a new name for example "all" or "town name" whatever and from the dropdown option set parent category "estonia"
So that you have "estonia" as a parent and "all" as a child
Your GraphQL Schema:
experienceCountries(where: {slug: "estonia"}) {
nodes {
children(where: {slug: "all"}) {
nodes {
experiences {
nodes {
title
}
}
}
}
}
}
I have a custom post type call books and taxonomy call author. In order to filter books by author I use this plugin:
https://github.com/wp-graphql/wp-graphql-tax-query
And my query is like this one.
{
books(
where: {
taxQuery: {
taxArray: {
taxonomy: AUTHORTAG,
operator: AND,
terms: "john-doe",
field: SLUG
}
}
}
) {
nodes {
databaseId
title
authorTags {
nodes {
databaseId
slug
}
}
}
}
}
I think it could work with your custom post type and both taxonomies. You need to filter in the taxQuery with multiple taxonomy values using AND operator

Graphql schema design problem that I can't solve

I am trying to design a schema for an application and I have a problem that I can't solve. Here is a detail of the application
User A LIKES User B
User B MATCHS User B
Now User A and User B can start chatting with each others.
I also keep track of who visited each profile
User A VISITED BY User B
User A Visited BY User C
In the application, I have Me type with details of the user running the app. I have a me query that looks like this:
me {
id
name
email
...
...
likes { ## users who liked me
nextToken
edges {
node { ## user
id
name
...
}
}
}
matchs { ## users who matched with me
nextToken
edges {
node { ## user
id
name
...
...
}
}
}
Vists { ## users who visited me
nextToken
edges {
node { ##
id
name
...
...
}
}
}
}
In addition to that, I have listUsers query that list users nearby to Me and looks something like this:
listUsers {
nextToken
total
edges {
distance
node { ## user
id
name
...
...
}
}
}
MY QUESTION
Since there is a relationship between users (LIKED_BY, MATCHED_WITH) where do I use this relationship in my schema such that it is cashable. Keep in mind the relationship can change at the client from NO_RELATIONSHIP to LIKED_BY to MATCHED_WITH so if the relationship is duplicated in multiple places, this will be a problem.
I would really appreciate any help as I am out of ideas.
Thanks in advance.
In the GraphQL schema you'd generally make links like this explicit references to the other object.
type User {
id: ID!
...
matches: [User!] # actual users, not their IDs
likes: [User!]
visitedBy: [User!]
}
In your top-level Query type you can return the current user
type Query {
me: User
}
Now if I want the names and email addresses of people who have visited me, I can find out
query WhoVisitedMe {
me {
visitedBy { name, email }
}
}
You can traverse this graph, for example, to get recommendations: for people who visit you, who do they like?
query WhoDoVisitorsLike {
me {
visitedBy {
likes {
id
name
}
}
}
}
Your application's resolver code would need to fill in these object references.
You clarified in a comment:
While listing users in the search query, I need to be able to know the relationship between Me and the users
That is, you're trying to ask about some user
type User {
relationToMe: [RelationshipType!]
}
The relationToMe field can have any number (including zero) of relationship types; you can define those as a GraphQL enum
enum RelationshipType {
LIKES, LIKED_BY,
VISITED, VISITED_BY,
MATCHES # bidirectional
}
Then in your query you can ask
query FindSomeUsers($input: SearchUsersInput!) {
searchUsers(input: $input) {
users {
id, name, email
relationToMe
}
}
}
and get back a response like
{
"data": {
"searchUsers": {
"users": [
"id": "12345",
"name": "John Smith",
"email": "jsmith#example.com",
"relationToMe": ["VISITED", "MATCHES"]
]
}
}
You'd need to implement a custom resolver in your implementation to populate this field. If you had something like a many-to-many SQL join table, you could query that to fill in this field, if requested.

How can i define arguments for nested fields on server side using grapqhl?

How can I define arguments for nested fields? Suppose I want to consult all my posts but limit and sort the comments. Thank you for your help.
{
allPosts {
title,
comments(limit: 5) {
content
}
}
}
What you are referring to, is often designated as Pagination, and is something that is covered by the GraphQL's specification.
There are different possible ways of constructing the query to allow retrieving multiple records of a certain object type (comments in our situation).
The simplest option, can be achieved by defining the GraphQL's query string with the object type you want to transverse in the plural form, meaning that your query would look like this:
{
allPosts {
title,
comments {
content
}
}
}
But with this implementation you would end up fetching all the data instead of simply retrieving a chunk of it. Obviously this approach can have many drawbacks depending on the volume of the data that is being fetched and should only be used on specific situations.
The easiest approach to achieve what you want is to request the comments as a "slice", meaning that you would be requesting a specific initial portion of the data set.
In this case, you would be requesting the initial 5 comments.
{
allPosts {
title,
comments(first: 5) {
content
}
}
}
But what if you want to paginate through the rest of the list?
{
allPosts {
title,
comments(first: 5, offset:5) {
content
}
}
}
Doing this, you could ask for the following next 5 comments.
But the approach that is recommend to use when implementing Pagination is the cursor-based pagination, which would translate to something like this:
{
allPosts {
title,
comments(first: 5) {
edges {
node {
content
}
cursor
}
}
}
}
The hard part consists on implementing the resolvers functionality (is slightly easier with frameworks like Apollo).

Gatsby.js: Filter GraphQL query by nested object property

I'm working on a news site which will have articles, each with one or multiple respective authors. If you click on an author's name, it will take you to a page displaying their information and a list of articles they have contributed to.
So each article has an authors property, which in turn is an array of author objects with properties: full name, slug (lowercase version of full name with spaces replaced by dashes), etc.
Is it possible to filter the articles by a particular author's slug when defining the query?
query authorQuery($slug: String!) {
allContentfulArticle(filter: { //not sure what to do here }) {
edges {
node {
title
slug
authors {
name
slug
}
}
}
}
}
My other option would be to load all of the articles, then setup a filter in the component like so:
const articles = data.allContentfulArticle.edges.filter(({ node }) => {
return node.authors.some((author) => author.slug === data.contentfulAuthor.slug);
});
This wouldn't be the end of the world, however it goes against the GraphQL principle of only loading the data you need.
What you want to achieve here if I understood correctly, you want to group articles by author.
You can achieve that if you query and apply filter to allContentfulAuthor
and request the article field, like so:
{
allContentfulAuthor(filter: {slug: {eq: "myslug"}}) {
edges {
node {
name
article {
title
slug
}
}
}
}
}
Note that the article name is your contentTypeId for your articles.

Resources