GROQ: Filter slug.current excluding multiple patterns with match operator

9 replies
Last updated: Dec 1, 2022
Good day šŸ™‚I’m trying to use GROQ to filter out a
list of pages to exclude if the
slug.current
contain either
/press
or
/blog
Using
match
because the
slug.current
can be
/press/XXX
or
/blog/XXX

The following works

.filter('_type == "page" && !(slug.current match "/press") && !(slug.current match "/blog")')

I’m looking into simplified the match to either but the following doesn’t work. Any advice?

.filter('_type == "page" && !(slug.current match ["/press", "/blog"])')
AI Update

The match operator in GROQ doesn't support matching against an array of patterns directly. Your working solution with separate conditions is actually the correct approach! However, there's a better way to handle this.

The match operator is designed for full-text search with tokenization, not exact string pattern matching. According to the match operator documentation, it tokenizes text and treats special characters like / as word separators, which makes it less ideal for slug matching.

The better solution: Use string::startsWith() instead, which is specifically designed for prefix matching:

*[_type == "page" && 
  !string::startsWith(slug.current, "/press") && 
  !string::startsWith(slug.current, "/blog")
]

Why this is better:

  • string::startsWith() does exact prefix matching without tokenization
  • It's more performant for this use case
  • The intent is clearer in your code
  • It's the recommended approach for prefix matching

Why your array syntax didn't work: The match operator doesn't accept arrays as the pattern argument. It expects a single string pattern with optional wildcards (*), not an array of patterns. There's no built-in OR functionality, pipe syntax, or alternation for multiple patterns in a single match expression - you have to use separate conditions like you did.

If you really want an array-based approach, you could do something like this, though it's more complex:

*[_type == "page" && 
  !(["/press", "/blog"] match string::split(slug.current, "/")[1])
]

But honestly, stick with string::startsWith() - it's cleaner, more readable, and semantically correct for checking URL prefixes. Your original working solution is also perfectly fine if you prefer to keep using match!

Show original thread
9 replies
!(slug.current in ["/press", "/blog"])
Using
match
because the
slug.current
can be
/press/XXX
or
/blog/XXX
Hmmm, not sure how it works with arrays, but you could check
path


[!(slug.current in path("/press.**"))] // slug.current matches anything that is not under the "/press" slug
Not much different from
match
in this instance, but maybe it has some array matching
Didn’t work šŸ™‚
The
path
function only works on the
_id
field.
I don’t think there is a way to make that query simpler right now I’m afraid.
Thanks for confirming
Hi
user P
,Have you solve your problem ?
I am stuck with the same problem
šŸ˜•
user M
match
can’t be simplified to support multiple array. End up having 2 matches with AND operator
.filter('_type == "page" && !(slug.current match "/press") && !(slug.current match "/blog")')
Thank you
user P
But problem is I have a .(dot) in the matching path and it does not work with .

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?