Filter referenced documents during expansion in Sanity GROQ query

8 replies
Last updated: Apr 17, 2023
Hello everyone. Is it possible to filter as you expand a reference (in a sub doc)? I feel like I am overthinking this.
If I have something like :


# example: every series has a volume. Some volumes have a download link and some do not
# query every series, but for each series keep only the volumes whose download_link field is not empty/null/undefined

*[
  _type == "series" &&
  // defined(volumes[] -> download_link) // should the filtering be done here? Is this the right approach?
] {
  _id,
  title,
  volumes[] -> {     // or does the filtering have to be done here?
    _id,
    title,
    download_link,
  }
}
AI Update

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 where download_link is 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 thread
8 replies
Are you specifically looking only for series that contain volumes with a download link or do you want all of your series but then only filter the volume links array?
I would like the second one! Fine with getting all series and if the subsequent filtering of volumes yield no results that should be fine to have a series with volumes of array length zero. Is there a way to do that inside the query?
Got it! You want to do the filtering in the array then:
*[
  _type == "series" &&
] {
  _id,
  title,
  volumes[defined(@->.download_link)] -> { 
    _id,
    title,
    download_link,
  }
}
Wow thank you so much! Have not seen that syntax before. Much much appreciated !!
Yeah the @ operator us super handy!
user M
sorry to bug but I am getting a super weird issue where the query you provided does indeed work on Sanity Studio (vision) but not in my project. By not work, I mean that it returns an empty array (for the volumes). I’ve isolated the issue to the “@” symbol itself (i.e., if I just remove that part of the query, the rest of it appears to work). I have also copied/pasted the query from studio to my project. I’ve tried tinkering with the “@” operator in all sorts of ways but it doesn’t appear to do anything. I’ve tried:

# these work on sanity studio, but not in my project even if I copy/paste verbatim
volumes[defined(@->.download_link)] -> { /// rest of the query
volumes[defined(@["download_link"])] -> { /// rest of the query

# tried these as a hail mary solution but none worked.
volumes[defined(@[]->.download_link)] -> { /// rest of the query
volumes[defined(@.download_link)] -> { /// rest of the query
If it turns out that I am getting different behavior between Sanity Studio and my local project, is this a versioning issue? A known bug?

I am using
@sanity/client
and am updated to version 5.4.2, but to no avail.
Edit: Here is the code I am using to run my query. Identical to the query I am running in Sanity Studio. But I get different results for some reason
😭

export async function getDownloads(): Promise<Release[]> {
  const data: Release[] = await client.fetch(`
    *[
      _type == "series" && 
      length(volumes[]) > 0
    ] {
      _id, 
      title,
      volumes[] -> {
        _id,
        title,
        download_link,
      }
    }
  `);
  return data;
}
I think it has to do with the Groq version. I was using V1, but had to specify version
2021-10-21
for the query to work!
Glad you got it working!

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?