Filter referenced documents during expansion in Sanity GROQ query
You're not overthinking this at all! Filtering references in GROQ can be done in multiple ways, and the key is understanding where to apply your filter. For your use case, you want to filter the dereferenced volumes to only include those with a download_link.
The best approach is to filter the array after dereferencing, like this:
*[_type == "series"] {
_id,
title,
"volumes": volumes[]->[ defined(download_link) ] {
_id,
title,
download_link
}
}Here's what's happening:
volumes[]->dereferences all the volume references[ defined(download_link) ]filters the dereferenced volumes, keeping only those wheredownload_linkis not null/undefined- The final
{ }projection shapes what fields you want from those filtered volumes
You can also write this slightly differently by filtering before the projection:
*[_type == "series"] {
_id,
title,
"volumes": volumes[]->[ defined(download_link) ]
}This will return the complete volume documents (all fields) that have a defined download_link.
Why not filter at the top level? Your commented approach defined(volumes[]->download_link) wouldn't work as intended because it would filter entire series documents based on whether ANY volume has a download link, not filter the volumes themselves. You want to keep all series but filter their volumes arrays.
The pattern array[]->[ filter ] is the standard GROQ approach for filtering dereferenced arrays, and it's exactly what you need here. According to Sanity's GROQ documentation, GROQ queries work as pipelines where you can chain filtering and projection operations, which is what you're doing with the array dereference and filter.
Show original thread8 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.