🔮 Sanity Create is here. Writing is reinvented. Try now, no developer setup

Example of a groq query to return related posts based on tags for a blog post.

10 replies
Last updated: Sep 26, 2021
Hi all, I am trying to find an example of a groq query that will return related posts based on tags for a blog post. Each post in my blog can have one or more tags. I would like to query for other posts that share one or more of the same tags so I can display links to them.
Sep 26, 2021, 9:32 AM
Checkout this example to match any array element in another array https://twitter.com/DerekNguyen10/status/1415498127927222273
Assuming your document has a
field that is an array of string
*[count(tags[@ in ["tag1", "tag2"]]) > 0]
IIRC it can be done if your tags are references as well, but it’s a little more complicated
Sep 26, 2021, 9:57 AM
user G
! My tags are references but this is a good start.
Sep 26, 2021, 10:07 AM
Struggling. I can do this so easily in WordPress. Completely stumped on how to return related posts with groq. Seems like I must be missing something.
Sep 26, 2021, 11:02 AM
user X
Here’s a better example, with references:

// Get all posts that references "Cat 1", "Cat 2", or both

  _type == "post"
  && count(tags[@->title in ["Cat 1", "Cat 2"]]) > 0
I can give you a more detail query if you share your data structure. This is one of the few areas where GROQ get a little challenging, but I think it’s worth it!

Also I have to mention that while the example I share is pure GROQ, you can just use javascript to form a query that’s (kind of) more readable:

const cats = ["Cat 1", "Cat 2"]
const conditions = cats.map(catName => `references(*[_type == "cat" && title == "${catName}"]._id)`).join(" && ")

  *[_type == "post" && ${conditions}]
Sep 26, 2021, 11:21 AM
user G
I will see if I can wrap my head around this, much appreciated...
Sep 26, 2021, 11:23 AM
I have a post schema that has a 'tags' field which is an array of references to a tag document schema. The tag document has title and description fields defined.
I have added tags 'Tag 1', 'Tag 2', 'Tag 3' and assigned them to my test posts.

When I run the following query, related has 0 results.

*[_type == "post"]{






"related":  *[_type == "post" && count(tags[@->title in ["Tag 1", "Tag 2", "Tag 3"]]) > 0]{





any further help gratefully received. This is fundamental stuff for me and if I can it working I can ditch WordPress.
Sep 26, 2021, 12:00 PM
Ohhh I forgot that you’re gonna do a sub query — In that case it’s a little trickier, however we get access to _ref for free so at the same time a little easier:

  _type == "post" && _id == "p5"
 ][0] {
   "related": *[
     _type == "post"
     && _id != ^._id
     && count(tags[@._ref in ^.^.tags[]._ref]) > 0
A breakdown:
• So same trick as before -
count(arrayOne[@ in arrayTwo]) > 0
to check any items in an array for another• Instead of
, use the related posts’
• Instead of
, use the parent posts’
. We can usually access the parent closure in sub queries by using
*[_type == "post"][0] { "related": *[ _type == "post" && tags[0]._ref in ^.tags[]._ref] } // get posts that has the same first tag
• However,
seems to create another closure, so we have to double up:
• Then we project the tags to get a simple array of strings:
["catId1", "catId2"]
Let me know if that works out!

edit: looking at your query, it should actually works, so perhaps the issue is somewhere else. Which API version are you on? and are you querying the same dataset as the one you use to set up the test?
Sep 26, 2021, 12:37 PM
user G
you are very kind. I would like to buy you a beer (or a coffee). Thanks so much 🙏👍😁
Sep 26, 2021, 12:49 PM
user X
NP at all! I needed this as well & someone else here helped me figure this out. Just passing along. Did you get it working?
Sep 26, 2021, 1:08 PM
Yes I did. It works perfectly. You really helped me out. Thanks again Derek!
Sep 26, 2021, 1:13 PM

Sanity– build remarkable experiences at scale

Sanity is a modern headless CMS that treats content as data to power your digital business. Free to get started, and pay-as-you-go on all plans.

Was this answer helpful?