Query related content by tags in Sanity

5 replies
Last updated: Dec 21, 2024
Looking for any help as I am attempting to query for "related content" based on the tags of a resource. Details in 🧵
AI Update

I'll help you query for related content based on tags! This is a common use case, and there are several effective patterns you can use depending on your schema structure.

Basic approach for array of tag references

If your resource has a tags field that's an array of references to tag documents, here's the core pattern:

*[
  _type == "resource" 
  && _id != ^._id  // exclude current resource
  && count(tags[@._ref in ^.^.tags[]._ref]) > 0
]

Complete query with projections

Here's a more complete example that gets a resource and its related content:

*[_type == "resource" && _id == "your-resource-id"][0] {
  _id,
  title,
  tags[]->{title},
  "related": *[
    _type == "resource"
    && _id != ^._id
    && count(tags[@._ref in ^.^.tags[]._ref]) > 0
  ][0...5] {  // limit to 5 related items
    _id,
    title,
    slug
  }
}

Key concepts explained

The trick here uses count() to check if any array elements match:

  • tags[@._ref in ^.^.tags[]._ref] filters tags where the _ref matches any _ref in the parent resource's tags
  • The double caret ^.^ is needed because tags[query] creates its own closure - the first ^ escapes the tag filter closure, and the second ^ escapes the subquery closure
  • count(...) > 0 returns true if there's any overlap

If tags are strings instead of references

If your tags are just an array of strings like ["tag1", "tag2"], the query is simpler:

*[
  _type == "resource"
  && _id != ^._id
  && count(tags[@ in ^.^.tags]) > 0
]

Alternative: Using tag titles

If you want to match based on tag titles rather than _ref:

*[
  _type == "resource"
  && _id != ^._id
  && count(tags[@->title in ^.^.tags[]->title]) > 0
]

Sorting by relevance

You can also sort related content by how many tags they share:

*[_type == "resource" && _id == "your-resource-id"][0] {
  _id,
  title,
  "related": *[
    _type == "resource"
    && _id != ^._id
    && count(tags[@._ref in ^.^.tags[]._ref]) > 0
  ] {
    _id,
    title,
    "matchCount": count(tags[@._ref in ^.^.tags[]._ref])
  } | order(matchCount desc)[0...5]
}

You can test these queries interactively at groq.dev. There's also a great community discussion with additional examples and explanations about this exact pattern.

If you share more details about your schema structure (whether tags are references, strings, or something else), I can help refine the query further!

Show original thread
5 replies
When I view a resource I also want to use that resource's Tags to search other resources that have tags in common. Currently trying this query
    related_papers = await client.fetch(
      `*[_type in [
          "whitePaper",
          "researchPaper",
          "dataSheet",
          "eBooks",
          "presentations"
        ] && count(Tags[@in ${searchQuery}]) > 0] {
        _id,
        title,
        abstract,
        _type,
        slug,
        date,
        Tags,
        _score
      } | order(_score desc)[0...3]`,
      {
        searchQuery // Pass the array of tags directly
      }
    );
Where
searchQuery
would be the current tag array. I cannot seem to get this dialed in so that it returns any result (current return count is 0). And yes, the tags resource is uppercase, not
tags
but
Tags
, it just is cant change it.
I am currently just hard coding the
searchQuery
to match a tag array that I know should have matching results, but cannot get anything returned, keep logging out a .count of 0.
If anyone can tell me where I am off it would be much appreciated
Hi there👋
I made some changes, would you mind trying this?

const related_papers = await client.fetch(`
  *[_type in [
    "whitePaper",
    "researchPaper",
    "dataSheet",
    "eBooks",
    "presentations"
  ] && count(Tags[@ in $tags]) > 0] {
    _id,
    title,
    abstract,
    _type,
    slug,
    date,
    Tags,
    _score
  } | order(_score desc)[0...3]`,
  {
    tags: searchQuery
  }
)
Thanks for the try, but still 0 results
Think it needed to be adjusted, I was not understanding how Tags was structured I believe, this appears to have dialed it in, thanks again for the help
] && count(Tags[defined(@) && @.value in $tags]) > 0] {
maybe some refining can help this too but just got the results I was expecting so wanted to post for anyone who comes here and looks

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?