Filtering nested arrays in GROQ without global scope

20 replies
Last updated: Jan 21, 2022
Is there a way to limit the scope of a filter within a groq query to the returned scope? Every query seems to be global (*[]), but I want to search within the context of a returned array of objects

*[_type == 'module' && slug.current == $module]{
  "chapters": chapters[]->{...}
}
I want to match against slug.current inside the returned
chapters
AI Update

You're absolutely right that filtering within the context of returned arrays requires a different approach than the global *[] pattern. The key is understanding how to filter after dereferencing.

Filtering dereferenced arrays

To filter chapters based on their slug.current field after dereferencing, you can apply a filter directly to the dereferenced array:

*[_type == 'module' && slug.current == $module]{
  "chapters": chapters[]->[slug.current == $chapterSlug]
}

This works because:

  1. chapters[]-> dereferences all chapter references, giving you an array of chapter documents
  2. [slug.current == $chapterSlug] filters that dereferenced array based on the slug field in the actual chapter documents

According to the pipeline components documentation, the filter component [<boolean>] iterates over elements of an array and returns only those where the expression evaluates to true. The documentation notes that "this operator is actually the filter pipeline component in disguise, since it has an implicit | operator before it."

Alternative: Filter before dereferencing

If you need to filter based on properties of the reference itself (like _key or _ref), you can filter before dereferencing:

*[_type == 'module' && slug.current == $module]{
  "chapters": chapters[_key == $someKey]->
}

However, this won't work for filtering by fields in the referenced documents (like slug.current), since those fields don't exist on the reference object itself—they only exist in the referenced document.

Combining operations

You can chain multiple operations together on dereferenced arrays:

*[_type == 'module' && slug.current == $module]{
  "chapters": chapters[]->[slug.current match $searchTerm] | order(orderRank asc)[0..4]
}

This filters the dereferenced chapters, then uses the pipe operator for ordering and slicing—all within the projection scope, exactly what you're looking for! The pipe operator is required for functions like order() but the filter syntax works directly on arrays.

Show original thread
20 replies
I wonder if something like this could work?
*[_type == 'module' && slug.current == $module]{
  "chapters": chapters[@->slug.current == "something"]->{...}
}
Yes!! thank you
user G
I could not for the life of me understand how to filter array results using groq
The full working query
*[_type == 'module' && slug.current == $module]{
  "chapter": chapters[@->slug.current == $chapter]->{
    "section": section[@->slug.current == $section]-> {...}
  }
}
Super simple compared to what I was doing before (and what wasn’t working)
Nice, glad you got it! Yeah GROQ is pretty awesome
It really is
I’m curious, since there is only one result, but it’s in an array, can the projection drop the array and only return the object
I wonder if something like this could work?
*[_type == 'module' && slug.current == $module]{
  "chapters": chapters[@->slug.current == "something"]->{...}
}
I haven't checked, but maybe something like this?
chapters[@->slug.current == $chapter][0]->
I'm not sure if deref would work in this case
That one doesn’t work as it ends up only looking at the first item it’s trying to match against
I guess what I want is to drop the array in the projection
Not critical though, at least this gets me a result 🙂
Ah I see.. I think you can always bring in the big boy
*[]
to get you exactly what you want:
*[_type == 'module' && slug.current == $module]{
  "chapter": *[_type == 'chapter' && _id in ^.chapters[]._ref && slug.current == $chapter][0]
}
But it may make the query a bit harder to read, I wonder if there's an easier way to slice &amp; deref an array of ref at the same time
Actually your first example did work… not sure what I did the first time that didn’t
*[_type == 'module' && slug.current == $module][0]{
  "chapter": chapters[@->slug.current == $chapter][0]->{
    "section": section[@->slug.current == $section][0]->{ ... }
  }
}
Thanks for your help User!
oh sweet, thanks for letting me know User! 🙌
I am late to the party and I may have misunderstood but this is what I arrived at (it's clumsy and maybe off of the use case but I learned something haha )
Love the GROQ expertise in this thread! 🙂

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?