GROQ: Filter product attributes by category's attribute references

6 replies
Last updated: Aug 30, 2022
Hello. I'm in need of some (hopefully) quick GROQ help. My documents of type "category" have a list of references to documents of type "attribute". My documents of type "product" has a reference to a category and also have a list of "attributes". How can I make it so my query fetches only the product attributes that its category lists?To visualize, the query would theoretically be something like
*[_type == "product"] { "attrs": attributes[attrRef in @.category.attributeRefs] }
, but this doesn't work. Any ideas?
AI Update

Great question! You're looking to filter an array of references based on whether those references match certain criteria. The key is using the @ operator to reference the current element within an array filter.

Here's the solution for your use case:

*[_type == "product"] {
  "attrs": attributes[@._ref in ^.category->attributeRefs[]._ref]
}

However, if you want to filter based on the actual attribute values (not just the references), you'll want to use this pattern:

*[_type == "product"] {
  "attrs": attributes[(@->._id in ^.category->attributeRefs[]._ref)]->
}

The most robust solution based on this answer from the Sanity community would be:

*[_type == "product"] {
  "attrs": attributes[(@._ref in ^.category->attributeRefs[]._ref)]->,
  // ... other fields
}

Key concepts here:

  • @ refers to the current element being filtered in the array
  • ^ allows you to reference the parent scope (the product document)
  • -> dereferences the reference to get the full document
  • The in operator checks if a value exists in an array

If you want to go a step further and ensure you only get products that have at least one matching attribute, you can add a filter at the document level:

*[_type == "product"] {
  "attrs": attributes[(@._ref in ^.category->attributeRefs[]._ref)]->,
  _id
}[length(attrs) > 0]

Note: This pattern requires API version v2021-03-25 or later to work properly with the @ operator in array filters.

Show original thread
6 replies
So, if I'm understanding your structure correctly:
*[_type == "product"]{
  'attributes': categories->.attributes[]
}
user M
Thanks for replying. My question is basically how to get the list of attributes from the category and only get THOSE attributes from my product, which is why I tried the "in" operator
Ah, I see now. You did previously mention that. You need to implement something much more complicated then! Something more along the lines of this . Just to get it kickstarted, maybe something like:
'attributes': attributes[@ in ^.category->.attributes[]]
You may or may not need the
^
before category.
user M
This is what I have so far:
*[_type == "product" && metadata.available && metadata.featured]{
  'attributes': variations[0].attributes[attribute in @.mainCategory->.featuredAttributes[]],
}
The important part here is that "attributes[].atribute" is the actual attr reference, whereas mainCategory->featuredAttributes is an array of attr references. This is what I've been trying so far but it's just not working whatsoever and I don't understand fully why. The list of mainCategory->featuredAttributes[] is retrieved successfully no problem, but for some reason "attribute in mainCategory->featuredAttributes[]" just doesn't work at all
Ok, so attributes are references to an attribute reference, not strings?
Oh I finally got it. The issue was trying to "in" the object itself.
attributes[attribute._ref in ^.mainCategory->featuredAttributes[]._ref]
worked

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?