Conditionally add filter in GROQ query when reference exists

10 replies
Last updated: Jan 27, 2026
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

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?