Filter array projection by date with fallback field in GROQ

13 replies
Last updated: Nov 29, 2025
Hey, hoping someone could give me a hand with filtering a projection?
I have an array of linked events that need to disappear after their date has passed, and I could do this in the frontend but would quite like to do it as part of my query instead.
I know this isn't valid groq, but I essentially want to do the following (where
selected
is my array of events)
*[_type == 'page' ]{
    whatsOn {'results': selected[endDate ? (endDate >= '2022-01-06') : (date >= '2022-01-06')]->},
  } | order(_updatedAt desc)[0]
Some events have an
endDate
but others just have a
date
, then I want to only return the ones where this date is in the future. Does that make sense?
AI Update

You can definitely do this in GROQ! The key is to filter the array after dereferencing. You'll want to use a projection with array filtering syntax on the dereferenced objects.

Here's how to structure your query:

*[_type == 'page'] {
  whatsOn {
    'results': selected[]->[@.endDate >= '2022-01-06' || @.date >= '2022-01-06']
  }
} | order(_updatedAt desc)[0]

Let me break down what's happening:

  1. selected[]-> dereferences all items in your selected array
  2. [@.endDate >= '2022-01-06' || @.date >= '2022-01-06'] filters the dereferenced array
  3. The @ refers to the current element in the array being filtered
  4. The || (OR) operator checks either condition

However, if you want to handle the conditional logic more explicitly (checking endDate first if it exists, otherwise falling back to date), you can use the defined() function:

*[_type == 'page'] {
  whatsOn {
    'results': selected[]->[@.endDate >= '2022-01-06' || (!defined(@.endDate) && @.date >= '2022-01-06')]
  }
} | order(_updatedAt desc)[0]

This ensures that if endDate exists and passes the date check, it includes the event. If endDate doesn't exist, it falls back to checking date.

Or, for a cleaner approach using the coalesce operator:

*[_type == 'page'] {
  whatsOn {
    'results': selected[]->[@.endDate >= '2022-01-06' || coalesce(@.endDate, @.date) >= '2022-01-06']
  }
} | order(_updatedAt desc)[0]

The coalesce() function returns the first non-null value, so it'll use endDate if it exists, otherwise date.

Pro tip: Instead of hardcoding the date, you can use the now() function to always compare against the current date:

*[_type == 'page'] {
  whatsOn {
    'results': selected[]->[@.endDate >= now() || coalesce(@.endDate, @.date) >= now()]
  }
} | order(_updatedAt desc)[0]

This way your query automatically filters out past events without needing to update the date string! The pipeline components documentation has more details on how filtering works within projections.

Show original thread
13 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?