Conditionally add filter in GROQ query when reference exists

10 replies
Last updated: Feb 15, 2021
hey y'all! I'm wondering if it's possible to conditionally / optionally add a filter in a GROQ query. In this case, I want to add an operator if a certain reference exists on the document. My attempt looks like this, but doesn't appear to be working.

*[
  _type == "post" &&
  slug.current == $slug &&
  (defined(primaryCategory->slug.current) &&
    primaryCategory->slug.current == $primaryCategorySlug
  )
]
I basically want to say, if this post has a primaryCategory reference that has a slug, query that this slug equals the $primaryCategorySlug variable.
If the post doesn't have a primaryCategory reference, don't add the primaryCategory slug query.
AI Update

Good question! Yes, you can definitely do conditional filtering in GROQ. The issue with your original query is that when primaryCategory doesn't exist, the entire condition becomes false, filtering out those posts entirely.

There are a couple of elegant solutions:

The cleanest approach is using the coalesce() function, which returns the first non-null value:

*[
  _type == "post" &&
  slug.current == $slug &&
  $primaryCategorySlug == coalesce(primaryCategory->slug.current, $primaryCategorySlug)
]

This works because if primaryCategory->slug.current doesn't exist (returns null), coalesce() returns $primaryCategorySlug instead, making the comparison $primaryCategorySlug == $primaryCategorySlug, which is always true.

Using logical OR

Alternatively, you can use explicit logic with the OR operator:

*[
  _type == "post" &&
  slug.current == $slug &&
  (!defined(primaryCategory->slug.current) || 
    primaryCategory->slug.current == $primaryCategorySlug)
]

This says "include the post if it doesn't have a primaryCategory OR if it does and matches the parameter."

Bidirectional check (if needed)

If you need to prevent additional path parts from being added to URLs, you might need both directions:

*[
  _type == "post" &&
  slug.current == $slug &&
  $primaryCategorySlug == coalesce(primaryCategory->slug.current, $primaryCategorySlug) &&
  primaryCategory->slug.current == coalesce($primaryCategorySlug, primaryCategory->slug.current)
]

The coalesce() approach is generally cleaner and more readable once you understand how it works (it's a standard SQL function too). You can also check out GROQ conditionals for more advanced conditional logic in projections.

Show original thread
10 replies
Hi Sander, have you tried something like this?
*[
  _type == "post" &&
  slug.current == $slug &&
  (!defined(primaryCategory->slug.current) || 
    (defined(primaryCategory->slug.current) &&
    primaryCategory->slug.current == $primaryCategorySlug)
  )
]
FYI: inside projections you can achieve this more easily using so-called conditionals, in case you'd consider using nested queries -
https://www.sanity.io/docs/query-cheat-sheet#conditionals-64a36d80be73
Hey
user M
, I just tried using
coalesce
, which works:

*[
  _type == "post"	&&
  slug.current == $slug &&
  $primaryCategorySlug == coalesce(primaryCategory->slug.current, $primaryCategorySlug)
]
I think this works because if
primaryCategory->slug.current
doesn't exist, it will compare
$primaryCategorySlug
to itself, which would be true?
Your suggestion works as well. What would you consider the best approach?
Ah, definitely, I'd say
coalesce()
is more elegant here, nice one 🙂
Cool, thank you! It looks a little cleaner for sure, with the drawback that you'd need to know how
coalesce
operates.
But I guess it's a pretty standard function in SQL land 🙂
I had to make it work both ways, as otherwise additional path parts could be added in to the URL:

*[
  _type == "post"	&&
  slug.current == $slug &&
  $primaryCategorySlug == coalesce(primaryCategory->slug.current, $primaryCategorySlug) &&
  primaryCategory->slug.current == coalesce($primaryCategorySlug, primaryCategory->slug.current)
]
user M
For this in particular, Sanity would really benefit from having some sort of virtual field support that can consist of values of the document. In my case I would want to add a
path
field, that's built up from:
• predefined text string (e.g.
/blog
)• reference slugs (and parent reference slugs) from categories (e.g.
/clothing
&
/shirts
• the document slug (e.g.
/basic-shirt
)
Then if I have a URL
/blog/clothing/shirts/basic-shirt
, I would be able to simply query documents that have this path, instead of making complicated queries like above.

Sanity – Build the way you think, not the way your CMS thinks

Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.

Was this answer helpful?