Filter GROQ query by array of referenced category names

36 replies
Last updated: Oct 18, 2021
Hello!
I have some blog posts I am trying to fetch

but I want to filter them by their category.

Categories is an array of references.

I am not sure how to construct the GROQ query to filter an item by a reference matching a name within the reference.

I am constructing some sort of conditional filter on a GROQ query and was wondering if this makes sense? I do not think it works


export const getPublishedBlogPosts = (
  categories: Array<String>
) => `*[_type == "post" && published == true ${
  categories ? `&& categories[]->title [@ in ${categories.join(", ")}]` : ""
}] | order(updatedNewestFirst) {
  categories[]-> {
    title
  },
  author-> {
    name,
    title,
    image,
    slug,
  },
  type,
  slug,
  title,
  description,
  mainImage,
}`;
Which I am calling like


export const getServerSideProps = async () => ({
  props: {
    posts: await sanity.fetch(getPublishedBlogPosts(["Freelance Writing"])),
  },
});
this seems like it would work but I get some errors.


expected ']' following expression
which does not seem right as i add the
]
following the ternary
I tried basing it off the cheatsheet examples.
AI Update

I can help you fix that GROQ query! The issue is with the syntax of your filter condition. You're close, but the way you're checking if dereferenced category titles match your array needs adjustment.

Here's the corrected version:

export const getPublishedBlogPosts = (
  categories: Array<String>
) => `*[_type == "post" && published == true ${
  categories.length > 0 ? `&& categories[]->title in [${categories.map(c => `"${c}"`).join(", ")}]` : ""
}] | order(updatedNewestFirst) {
  categories[]-> {
    title
  },
  author-> {
    name,
    title,
    image,
    slug,
  },
  type,
  slug,
  title,
  description,
  mainImage,
}`;

The problems with your original query:

  1. The syntax categories[]->title [@ in ${categories.join(", ")}] is malformed
  2. You're not wrapping the string values in quotes - GROQ needs ["Freelance Writing"] not [Freelance Writing]
  3. The @ operator isn't needed here for a simple in check

What the fix does:

  • categories[]->title - dereferences all category references in the array and gets their title field
  • in [...] - checks if any of those titles exist in your provided array
  • ${categories.map(c => "${c}").join(", ")} - properly formats your JavaScript array into a GROQ array with quoted strings

So when you call:

posts: await sanity.fetch(getPublishedBlogPosts(["Freelance Writing"]))

The resulting GROQ filter becomes:

&& categories[]->title in ["Freelance Writing"]

This will match any post where at least one of its category references has a title of "Freelance Writing".

Alternative approach using count:

If you prefer being more explicit, you can also use:

const categoryFilter = categories.length > 0
  ? `&& count((categories[]->title)[@ in [${categories.map(c => `"${c}"`).join(", ")}]]) > 0`
  : "";

This filters the dereferenced titles to only those in your array, counts them, and checks if the count is greater than 0. Both approaches work, but the in operator is simpler and more readable.

Show original thread
36 replies
Hi User! You’re almost there. Here’s an example of querying posts that reference all tag titles in an array:

sanity.fetch(`
  *[
     _type == 'post' 
     && count(tags[@->title in $tagNames]) == count($tagNames)
  ]
`, { tagNames: ["Cat 1", "Cat 2"] })

https://groq.dev/Ax2SQe8Xcu0KonUCQKsHee
awesome thank you so much!
You can ‘expand’ an array of item like this:
categories[@->title] // ["name1", "name2"]
Then compare the title:

categories[@->title in ["name1", "name2"]] // [true, true]
This returns an array of true &amp; false, so it’s not helpful yet. You can use
count()
to get a number of
true
value:
count(categories[@->title in ["name1", "name2"]]) // 0 or 1 or 2
Here you can find posts that either reference at least 1 of the categories:

count(/*the whole thing above*/) > 1
Or all of the categories:

count(/*the whole thing above*/) == count(["name1", "name2"])
Hope that helps!
okay so
user G
here is what I have come up with now
*[_type == "post" && published == true && categories[@->title in $categories]] | order(updatedNewestFirst)
called like


await sanity.fetch(getPublishedBlogPosts, {
      categories: ["Freelance Writing"],
    }),
is this correct to get the blog posts with the category "Freelance Writing"?
I am at least getting an empty array now
and then I do the count
Nice, does that work? I think you’d need to wrap the
categories[@->title in $categories]
in
count(categories[@->title in $categories]) > 1
so you at least get posts that reference 1 category
Is still empty
I think it might be on my end
am not sure
gonna check my schema
I am gonna download the groq editor for my sanity and play w it
Hey User! Just to confirm, are you using
v1
or
v2021-03-25
of the API? You can see https://www.sanity.io/help/js-client-api-version and https://www.sanity.io/docs/api-versioning for details.
hey
user A
thanks for reaching out
am getting to this again now
gonna test in the vision
Yes sir
user A
it is still not working as expected
Yes sir
user A
it is still not working as expected
this is the link to the query i am trying
*[_type == "post" && published == true && count(categories[@->title in $categories])] {
  title
}

{
  categories: ["Freelance Writing"]
}
is it perhaps due to some case matching or string not being exact?
I could try doing a slug or something perhaps for each of the categories?
Oh I've just realized in the example I wrote count &gt; 1 which will never return true in your case -- have you happened to try count(...) &gt; 0?
Oh I've just realized in the example I wrote count &gt; 1 which will never return true in your case -- have you happened to try count(...) &gt; 0?
😂
i should look at it myself a bit closer i guess
my bad… let me know if that works!
my bad… let me know if that works!
ITS WORKING!!
😂
we did it
😂
thank you my man
Awesome!!

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?