GROQ query: Filter documents where array field intersects with parameter array

4 replies
Last updated: Jul 14, 2023
Hi,
I have this blog page, that displays links to other similar blogs. The similarity is based on category, where every blog can have multiple string categories. So basically, in a blog that has ["Design", "Research"] categories, I need to display links to blogs with either "Design" or "Research" category. Note that they can have other categories as well.
I have this small query that works only with 1 category. Can somebody help me achieve the intersection of categories between fetched blogs?


`*[_type == 'blog' && $category in categories] {
      _id,
      _updatedAt,
      categories,
      description,
      title,
      timeToRead,
      publishedAt,
    }`,
    {
      category,
    }
Instead of
_type == 'blog' && $category in categories
I need something like
_type == 'blog' && ($category *has any in* categories)
where
$category
is also an array.
Thanks.
AI Update

Perfect! I found the exact solution to your problem. The trick is to use the count() function combined with the @ filter operator to check if any elements from one array exist in another array.

Here's how to solve your problem:

*[_type == 'blog' && count(categories[@ in $categories]) > 0] {
  _id,
  _updatedAt,
  categories,
  description,
  title,
  timeToRead,
  publishedAt,
}

Where $categories is your array parameter (e.g., ["Design", "Research"]).

How it works:

The expression categories[@ in $categories] filters the categories array to only include elements that exist in your $categories parameter array. Then count() counts how many matches there are. If the count is greater than 0, it means at least one category matches, and that blog post is included in the results.

Example:

  • If a blog has categories: ["Design", "UX", "Frontend"]
  • And you pass $categories: ["Design", "Research"]
  • The expression categories[@ in $categories] evaluates to ["Design"]
  • count(["Design"]) returns 1
  • Since 1 > 0, this blog is included in results

This is exactly the "array intersection" check you need! The @ symbol in GROQ represents the current element being iterated over, and the in operator checks if that element exists in the comparison array.

This pattern comes from this community discussion about comparing arrays and checking for string overlap in GROQ queries, which is exactly your use case.

You can test this in Vision (Sanity's query testing tool) with your parameters to verify it works as expected!

Show original thread
4 replies
Note that
$category match categories
does not work as it expects all left side values to be included in right side values.
I just need
any
Hi
user R
. Can you please give the examples at the end of the Filters list a try (the ones with
[@ in "action", "thriller"]
)? I believe that syntax is what you’re after.
Did not work for me.
If somebody is still searching for a solution, this is what I did:


export const getBlogsBasedOnCategory = async (
  curentBlogCategories: BlogCategory[],
  currentBlogId: string,
) => {
  const queryCondition = curentBlogCategories.reduce(
    (acc: string, cv: BlogCategory) => {
      const addition = !acc
        ? `"${cv}" in categories`
        : ` || "${cv}" in categories`;
      return acc + addition;
    },
    '',
  );

  return SanityClient.fetch(
    `*[_type == 'blog' && _id != $id && (${queryCondition})][0..2] {
    _id,
    _updatedAt,
    slug,
    categories,
    description,
    title,
    timeToRead,
    publishedAt,
    image
  }`,
    {
      id: currentBlogId,
    },
  );
};

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?