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

6 replies
Last updated: Jul 22, 2022
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
Closest I've come by my own understanding is:
*[_type=="post"] {

"slugs": *[contentBlock[].mainText[].children[].text match 'corporis']{slug}

}
Hey
user Q
! You're on the right track. You will need to drill down into arrays using the
[]
operator. Does the query you put together work for you or do you still need help putting it together?
Hey
user M
no it doesnt work im afraid. Does any immediate errors jump out to you?
Thanks!
No! But I can dig deeper! Can you share your schema so I can replicate it?
Ill get to it first hing in the morning 😊
I solved it.
`groq`{*[_type=="post" && contentBlocks[].mainText[].children[].text match ${author.fullname} || lead match ${author.fullname}]{`

"slug":slug.current

}
`}``

I do get a string literal error if I try to do:

{

slugs: *[_type=="post" && contentBlocks[].mainText[].children[].text match "horst" || lead match "horst"]{

"slug":slug.current

}

}
Which doesn't make much sense to me

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?