How to filter Sanity documents by referenced array item properties?

20 replies
Last updated: Jan 28, 2026
Is there a way to filter by elements of an array of references? I have a ProductListing type which references many Products. I want to get all listings that have at least one product in stock. I can’t seem to be able to figure this one out.
I tried a few options along these lines, but it doesn’t seem to work:

*[_type=='ProductListing' && items[]->stock > 0]{
  _id
}
I was looking for something like a
sum
function or any way to filter items without doing a select and filtering client side.I will have more conditions as well based on whether items are currently disabled and such.
AI Update

You're very close! The key issue is understanding how GROQ handles array dereferencing and filtering. Let me break down the correct approach based on the GROQ documentation.

The correct way to filter ProductListings that have at least one product in stock is:

*[_type == 'ProductListing' && count(items[]->[stock > 0]) > 0] {
  _id
}

Here's what's happening step-by-step:

  1. items[] - Gets all items in the array
  2. -> - Dereferences each item (follows the reference to get the actual Product document)
  3. [stock > 0] - Filters the dereferenced products to only those with stock > 0
  4. count(...) - Counts how many products match the condition
  5. > 0 - Checks if at least one product matches

Important: The filter condition goes after the dereference operator (->) and inside brackets. This is crucial because you're filtering the dereferenced documents themselves, not the references.

For multiple conditions (like checking both stock and disabled status):

*[_type == 'ProductListing' && count(items[]->[stock > 0 && !disabled]) > 0] {
  _id
}

You can combine multiple conditions with && or || inside that filter bracket after the dereference.

Alternative using the @ operator (current element):

*[_type == 'ProductListing' && count(items[]->[@.stock > 0]) > 0] {
  _id
}

The @ explicitly refers to the current dereferenced document. While it's optional in simple cases, it can make complex queries clearer.

A practical example with more details:

*[_type == 'ProductListing' && count(items[]->[stock > 0 && !disabled]) > 0] {
  _id,
  title,
  "inStockProducts": items[]->[stock > 0 && !disabled]{
    _id,
    name,
    stock
  }
}

This query not only filters the listings but also returns the actual in-stock products for each listing, which is often useful.

According to the count() function documentation and examples in the query cheat sheet, the pattern you'll use repeatedly is: array[]->[filter conditions], then wrap in count() to check if any exist. This works because GROQ evaluates the dereference first, then applies the filter to those dereferenced documents, then counts the results.

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