How to filter a GROQ query based on the presence of a specific field in the schema.
8 replies
Last updated: Jun 1, 2023
D
I have this groq query where I query all the pages and posts.
I only want the posts which
How could I only apply the filter if the doc has publishedAt?
*[ _type in ['page', 'post'] && !(_id in path('drafts.**')) && dateTime(publishedAt) < dateTime(now()) { .... } ]
publishedAt < now. However, the
pagetype doesn’t have the
publishedAtfield. If I add that condition, I will get no
page
How could I only apply the filter if the doc has publishedAt?
May 31, 2023, 11:19 AM
L
Could you not extend the third condition to be
Something like
type= page OR
publishedAt < now?
Something like
*[ _type in ['page', 'post'] && !(_id in path('drafts.**')) && (_type == 'page' || dateTime(publishedAt) < dateTime(now())) { .... } ]
May 31, 2023, 11:39 AM
D
user Z
I think it works, why can’t we use _type == 'post' && dateTime(publishedAt) < dateTime(now())
*[ _type in ['page', 'post'] && !(_id in path('drafts.**')) && (_type == 'post' && dateTime(publishedAt) < dateTime(now())) { .... } ]
May 31, 2023, 11:59 AM
D
Or is any way to express “only apply the condition when there is a publishedAt in the schema”
May 31, 2023, 12:00 PM
D
I’m using API version “v2021-10-21”
May 31, 2023, 12:01 PM
L
I don't know if there's a way to express "only apply the condition when there's a publishedAt in the schema" but there is the
So you could write the third condition as something like:
defined()property which returns
trueif the argument is non-
null, otherwise
false.
So you could write the third condition as something like:
... && (!defined(publishedAt) || defined(publishedAt) && dateTime(publishedAt) < dateTime(now())) ...
May 31, 2023, 12:07 PM
L
There might be a better way.. but that's what I can think of
May 31, 2023, 12:07 PM
Great suggestions, (Removed Name)! Off to a running start! ✨
Another option is to use one of GROQ’s functions to test for
This function works much like a conditional, where we test if
Perhaps a bit more concise is `coalesce`:
This function returns the first non-null value. Thankfully, GROQ will resolve
I might have something wrong here, but hopefully it can get you started.
user N
There are several different ways you could approach this. I like (Removed Name)’s approach because it keeps things very readable and easy to parse—and yours (using _type == 'post' &&instead of
_type == 'page' ||) should work just as well. The reason this may not be working for you is because in the examples above, the filter isn’t ended before the projection (
{ … }), so be sure to correct for that if needed.
Another option is to use one of GROQ’s functions to test for
publishedAt, which removes the need for the second
_typecheck. The
selectand
coalescefunctions are two possibilities, and both rely on the fact that a GROQ query’s filter essentially boils down to
trueor
false. We could use `select`:
*[ _type in ['page', 'post'] && !(_id in path('drafts.**')) && select( publishedAt != null => dateTime(publishedAt) < dateTime(now()), true ) ]{ ... }
publishedAtis not null, and if it’s not, we return
dateTime(publishedAt) < dateTime(now())to the filter to be executed. If
publishedAtis null, which you say will be the case for all
pagedocuments, then we just return
trueso that it doesn’t impact the filter.
Perhaps a bit more concise is `coalesce`:
*[ _type in ['page', 'post'] && !(_id in path('drafts.**')) && coalesce( dateTime(publishedAt) < dateTime(now()), true ) ]{ ... }
dateTime(null)to
null, and when that’s compared against
dateTime(now()), it returns null. Like in the
selectexample, this will return
true | falseif
publishedAtis non-null, or
trueif it is null.
I might have something wrong here, but hopefully it can get you started.
May 31, 2023, 8:23 PM
D
Thank you both for the answers 🎉 You made my day
Jun 1, 2023, 10:13 AM
Sanity– build remarkable experiences at scale
The Sanity Composable Content Cloud is the headless CMS that treats content as data to power your digital business. Free to get started, and pay-as-you-go on all plans.