How to conditionally apply a GraphQL filter based on the value of a passed in argument? - filter

Update: for my particular use case, #langpavel's solution will work. However, it avoids answering the original question... is it possible to conditionally apply a GraphQL filter based on the value of a passed in argument?
I am passing in an argument into my GraphQL query called $env that I would like to use in conjunction with a draft boolean in the markdown frontmatter of my blog posts in order to filter out drafts during production.
My query, in an abbreviated form, is below. As you can see, I have a filter function that is filtering based on whether the blog post is a draft or not. What I can't figure out how to do is take advantage of the $env argument to only apply the filter when $env is equal to production.
export const pageQuery = graphql`
query BlogPosts($env: String!) {
markdownRemark(
filter: { frontmatter: { draft: { eq: true } } }
) {
frontmatter {
title
date(formatString: "MMMM DD, YYYY")
draft
}
}
}
`

In case of gatsby, you should probably write two queries and select one conditionally based on process.env.NODE_ENV:
export const pageQuery = (process.env.NODE_ENV === 'production') ?
graphql`
query BlogPosts {
markdownRemark(
filter: { frontmatter: { draft: { ne: true } } }
) { frontmatter { ... } }
}
` : graphql`
query BlogPosts {
markdownRemark { frontmatter { ... } }
}
`

The GraphQL query you provided is invalid because the $env variable is not used in any fields. I think for your purpose, $env would be better passed outside of the GraphQL query. If you're serving GraphQL over HTTP, you could send it as a query parameter or an HTTP header. Then you could pass the value to your GraphQL resolvers using the context concept that most GraphQL libraries provide.

Related

Creating dynamic graphql query using apollo/client

I am trying to create a graphql query where the query data type and filter parameters will be passed dynamically based on user input.
I have written the below query which filters using only one field shipdate.
const GET_SHIPDATA_WITH_FILTER = gql`
query GetShipData($shipdateStart: timestamptz, $shipdateEnd: timestamptz, $limit: Int) {
shipdata(where: {shipdate: { _gte: $shipdateStart, _lte: $shipdateEnd}},limit: $limit) {
status
import_time
shipdate
}
}
`;
const variables = {
shipdateStart: "some date",
shipdateEnd: "some date",
limit: 50,
};
If no filter is passed I'm using this one
const GET_SHIPDATA = gql`
query GetShipData($limit: Int) {
shipdata(limit: $limit) {
status
import_time
shipdate
}
}
`;
const variables = {
limit: 50,
};
You can see I have written two queries to handle two types of filters which won't work if I want to add more filters.
Now I am trying to write a single dynamic query where if the user wants to add more filters like status: {_eq: $status} or import_time: { _gt: $importTimeStart, _lt: $importTimeEnd} then I will pass the variables and the query will dynamically handle the filters. Something like
const GET_SHIPDATA = gql`
query GetShipData($allfilters: AllFilterTypes) {
shipdata(filter: $allfilters) {
status
import_time
shipdate
}
}
`;
const variables = {
//pass allFilters based on user input,
};
Btw I'm using react and hasura if it helps anyway.
Hasura already exposes types in your GraphQL schema that refer to "filter conditions". In Hasura, they're called Bool_Exp (short for boolean expression) and they map directly to the where clause.
If you just update your query to receive a shipdata_bool_exp you'll be able to build up a dynamic filter expression in your application code and it will work as expected.
const GET_SHIPDATA_WITH_FILTER = gql`
query GetShipData($filter: shipdata_bool_exp!) {
shipdata(where: $filter,limit: $limit) {
status
import_time
shipdate
}
}
`;

Use variables in Shopify Bulk Operations graphql mutation

I am trying to use graphql to query a store's orders in a bulk operation. I am using a tags variable in the graphql request to query orders with specific tags. I have been following Shopify's documentation in order to do this.
Here is what I've been trying:
const variables = {
"tags": "tag:foo AND tag:bar"
};
const query = gql`
mutation getOrders($tags: String!) {
bulkOperationRunQuery(
query:"""
query {
orders(query: $tags) {
edges {
node {
id
email
}
}
}
}
"""
) {
bulkOperation {
id
url
status
}
userErrors {
field
message
}
}
}
`;
const bulkOperation = await graphQLClient.request(query, variables);
However, I always get this error back from the api:
Variable $tags is declared by getOrders but not used
Does anyone know if there is a way to use a graphql variable in that bulk operation? Thanks!
""" is multiline string based on docs, which would means its just a string and no vars are allowed. You can use js variable substitution like ${tags} inside templated string and bypass this all together or try if concatenation works like """ 1st part """" + $tags + """ 2nd part """

Strapi GraphQL search by multiple attributes

I've got a very simple Nuxt app with Strapi GraphQL backend that I'm trying to use and learn more about GraphQL in the process.
One of my last features is to implement a search feature where a user enters a search query, and Strapi/GraphQL performs that search based on attributes such as image name and tag names that are associated with that image. I've been reading the Strapi documentation and there's a segment about performing a search.
So in my schema.graphql, I've added this line:
type Query {
...other generated queries
searchImages(searchQuery: String): [Image
}
Then in the /api/image/config/schema.graphql.js file, I've added this:
module.exports = {
query: `
searchImages(searchQuery: String): [Image]
`,
resolver: {
Query: {
searchImages: {
resolverOf: 'Image.find',
async resolver(_, { searchQuery }) {
if (searchQuery) {
const params = {
name_contains: searchQuery,
// tags_contains: searchQuery,
// location_contains: searchQuery,
}
const searchResults = await strapi.services.image.search(params);
console.log('searchResults: ', searchResults);
return searchResults;
}
}
}
},
},
};
At this point I'm just trying to return results in the GraphQL playground, however when I run something simple in the Playground like:
query($searchQuery: String!) {
searchImages(searchQuery:$searchQuery) {
id
name
}
}
I get the error: "TypeError: Cannot read property 'split' of undefined".
Any ideas what might be going on here?
UPDATE:
For now, I'm using deep filtering instead of the search like so:
query($searchQuery: String) {
images(
where: {
tags: { title_contains: $searchQuery }
name_contains: $searchQuery
}
) {
id
name
slug
src {
url
formats
}
}
}
This is not ideal because it's not an OR/WHERE operator, meaning it's not searching by tag title or image name. It seems to only hit the first where. Ideally I would like to use Strapi's search service.
I actually ran into this problem not to recently and took a different solution.
the where condition can be combined with using either _and or _or. as seen below.
_or
articles(where: {
_or: [
{ content_contains: $dataContains },
{ description_contains: $dataContains }
]})
_and
(where: {
_and: [
{slug_contains: $categoriesContains}
]})
Additionally, these operators can be combined given that where in this instance is an object.
For your solution I would presume you want an or condition in your where filter predicate like below
images(where: {
_or: [
{ title_contains: $searchQuery },
{ name_contains: $searchQuery }
]})
Lastly, you can perform a query that filters by a predicate by creating an event schema and adding the #search directive as seen here

how to match queries with apollo's refetchQuery

My fundimental question is do the variables for queries need to be exact for refetchQueries to work. Or can you give it a subset of variables and it will match similar queries.
Consider the following ....
<Query<NotesQuery, NotesQueryVariables>
query={notesQuery}
variables={{
input: {
notebookId: notebookContext.id,
first: 20
}
}}
>
</Query>
and the following mutation:
client
.mutate<NoteCreateOrUpdateMutation, NoteCreateOrUpdateMutationVariables>({
mutation: noteCreateOrUpdateMutation,
variables: {
input: {
noteId: note ? note.id : undefined,
subjectIds: noteSubjects,
notebookId: notebookContext.id,
authorId: userContext.id,
content: noteContent,
context: noteCaption,
}
},
refetchQueries: [
{
query: notesQuery,
variables: { input: { notebookId: notebookContext.id } } as NotesQueryVariables
}
]
})
when I do that mutation it is NOT refetching the note query with the pagination
If I add the first: 20 parameter -- it works.
I would like it to clear all noteQueries that match with the given parameters. Is that possible?
I believe you'll be wanting to add #connection directives to your gql definitions of notesQuery and measurementsQuery. You didn't post those, so unfortunately I can't show you exactly what that would look like for your use case.
Anyway, the #connection directive will allow Apollo to match on notebookId for example, while ignoring the value of first.
Unfortunately, you've bundled all your input into the object input, and I don't know how you would select just notebookId with the filter. Assuming that your gql definition looks something like this for notesQuery:
const notesQuery = gql`
query notes($input: InputType!) {
notes(input: $input) #connection(key: "notes", filter: ["input['notebookId']"]) {
id
...
}
}
`;
^^^ Unfortunately, that won't work because of the way that apollo-utilities/lib/storeUtils.js -> getStoreKeyName() function works. It'll just ignore the above attempt to get better resolution than an arg name, i.e. can't go beyond input. Any string in the filter array that doesn't match an arg name is silently ignored.
Looks like you'll have to modify your schema.
More info at: https://www.apollographql.com/docs/react/features/pagination.html#connection-directive

GraphQL disable filtering if filter variable is empty

I have a Gatsby GraphQL query for a list of posts ordered by date and filtered by category.
{
posts: allContentfulPost(
sort: {fields: [date], order: DESC},
filter: {category: {slug: {eq: $slug}}}
) {
edges {
node {
title {
title
}
date
}
}
}
}
Right now when $slug is the empty string "", I get
{
"data": {
"posts": null
}
}
Is there a way to get all posts instead?
You can use the regex filter to your advantage. If you pass an empty expression, then all posts will be returned because everything will match.
query Posts($slugRegex: String = "//"){
posts: allContentfulPost(
sort: {fields: [date], order: DESC},
filter: {category: {slug: {eq: $slugRegex}}}
) {
# Rest of the query.
}
}
By default, all posts will be returned (the $slugRegex is an empty regex if nothing was passed). When the $slugRegex becomes a meaningful expression, then only matching posts will show up.
As for passing the value, I'm assuming you're using gatsby-node.js to create pages. In that case, it's as simple as that:
// gatsby-node.js
exports.createPages = async ({ actions }) => {
const { createPage } = actions
// Create a page with only "some-slug" posts.
createPage({
// ...
context: {
slugRegex: "/some-slug/"
}
})
// Create a page with all posts.
createPage({
// ...
context: {
// Nothing here. Or at least no `slugRegex`.
}
})
}
It's not possible with this query, even #skip/#include directives won't help because you can't apply them on input fields.
I would suggest to either adjust the server side logic so that null in the 'eq' field will ignore this filter or either to edit the query being sent (less favorable imo).
It seems that the graphql schema that you work against lacks the filtering support you need..
If anyone requires a solution for other systems than Gatsby this can be accomplished using #skip and #include.
fragment EventSearchResult on EventsConnection {
edges {
cursor
node {
id
name
}
}
totalCount
}
query Events($organizationId: UUID!, $isSearch: Boolean!, $search: String!) {
events(condition: { organizationId: $organizationId }, first: 100)
#skip(if: $isSearch) {
...EventSearchResult
}
eventsSearch: events(
condition: { organizationId: $organizationId }
filter: { name: { likeInsensitive: $search } }
first: 100
) #include(if: $isSearch) {
...EventSearchResult
}
}
Then in your client code you would provide search and isSearch to the query and get your events like:
const events = data.eventsSearch || data.events

Resources