GROQ: Query nested object text content and filter by matching word

6 replies
Last updated: Jan 28, 2026
Hey! i'm new to groq and I'm struggling with understanding how I can make a query on a nested object.Here's what I'm trying to do: I'm trying to get a list of all the slugs where the text (lorem example below) matches a certain word. Any help would be appreciated, I'm a bit inexperienced with groq
🙂

{

"_createdAt": "2022-07-01T06:15:15Z",

"_id": "ipsum",

"_rev": "lorem",

"_type": "post",

"_updatedAt": "2022-07-07T04:00:02Z",

"contentBlocks": [

{

"_key": "somenu12312",

"_type": "textContent",

"mainText": [

{

"_key": "somenum123123",

"_type": "block",

"children": [

{

"_key": "othernum123213",

"_type": "span",

"marks": [],

"text": "omnis-aut-voluptatem Culpa beatae omnis corporis in enim. Sit quo mollitia et omnis ea sit praesentium rerum."

}

],

"markDefs": [],

"style": "normal"

}

]

}

],

"slug": {

"_type": "slug",

"current": "ipsum"

},

"title": "lorem"

}
AI Update

Looking at your data structure, you need to search through nested arrays to find text within your Portable Text blocks. Here's how to do it:

*[_type == "post" && contentBlocks[].mainText[].children[].text match "omnis-aut-voluptatem"]{
  slug
}

This will return all posts where any text in your nested structure matches the word you're searching for. The key is understanding how GROQ handles nested arrays - you use [] to "flatten" each level of the array structure.

Breaking down the path:

  • contentBlocks[] - traverse the contentBlocks array
  • .mainText[] - traverse the mainText array inside each contentBlock
  • .children[] - traverse the children array inside each block
  • .text - access the actual text content

The match operator performs tokenized text search, so it'll find whole words within your text. It breaks text into searchable terms, so searching for "omnis" would match "omnis-aut-voluptatem" since the dash acts as a word separator.

To get just the slug values as a simple array:

*[_type == "post" && contentBlocks[].mainText[].children[].text match "omnis"]{
  "slug": slug.current
}

Pro tip: If you need to search for multiple words or do more complex searches, you can combine conditions:

*[_type == "post" && (
  contentBlocks[].mainText[].children[].text match "omnis" ||
  contentBlocks[].mainText[].children[].text match "beatae"
)]{
  slug
}

The match operator is great for full-text search, but if you need exact string matching or prefix matching, you'd want to use string functions like string::startsWith() instead.

Show original thread
6 replies

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?