Getting array subset in GROQ using start and end _key values

33 replies
Last updated: Sep 12, 2021
Hey folks, does anybody know if it’s possible to get via GROQ a subset of an array by having the
_key
of the initial and the final element?
I know I can do something like
*[_type == "post"]{body[_key match "KEY_HERE"]}
to get a single element, but does GROQ provide with a way to do something similar for a subset only?
AI Update

I understand you're looking to get a subset of an array between two specific _key values in GROQ. Unfortunately, GROQ doesn't have a built-in way to slice arrays based on _key positions like you might find in some programming languages with indexOf() or similar functions.

The challenge is that GROQ's slice component only works with numeric indices (like [0..5] or [2...10]), not with dynamic positions based on element properties like _key. There are no functions like array::position() or array::indexOf() that would let you find the numeric index of an element by its _key.

What GROQ can do:

Filter by a single _key:

*[_type == "post"]{
  body[_key == "KEY_HERE"]
}

Filter by multiple _key values:

*[_type == "post"]{
  body[_key in ["key1", "key2", "key3"]]
}

Workarounds for your use case:

Since GROQ doesn't support finding the index of an array element by _key, you'll need to handle this logic outside of GROQ:

1. Fetch the full array and slice client-side (recommended):

*[_type == "post"]{
  _id,
  body[]
}

Then in your JavaScript/TypeScript:

const startIndex = body.findIndex(item => item._key === startKey);
const endIndex = body.findIndex(item => item._key === endKey);
const subset = body.slice(startIndex, endIndex + 1);

2. Use explicit _key filtering if you know all the keys: If you know all the _key values between your start and end points, you can explicitly list them:

*[_type == "post"]{
  body[_key in ["startKey", "middleKey1", "middleKey2", "endKey"]]
}

3. Consider restructuring your data: If you frequently need to query subsets of arrays this way, you might want to store metadata about array positions or create separate documents for array items that need this kind of querying.

The GROQ language is intentionally designed to be simple and declarative, which means some operations that are common in imperative programming languages need to be handled in your application code instead. This slice-by-key operation is one of those cases where it's better suited for client-side processing after fetching the data from the Content Lake.

Show original thread
33 replies
Does it need to be based on a
_key
? You can use element access traversals (
[0]
and
[-1]
) to get the first and last items of the array.

*[_type == 'post']{
  'first': body[0],
  'last': body[-1]
}
user A
thanks for the quick response, pal! I want to be able to extract a subset of portable text blocks for certain cases, so I think
_key
is the only way to identify those, right?
This might not be possible with GROQ, in which case I would defer to just filter it in my code. Should be fine, was just looking to avoid the overhead on client side.
Your snippet is great (I could even use
_key
there to identify proper first and last), but is there any way to get the in between body blocks?
'others': body[_key != ^.body[0]._key && _key != ^.body[-1]._key]
Right, but imagine that my subset goes from the second element of the array to the second-last element. With that query, I would get the first and the last in
others
too, right?
I’m kinda looking for something that works as a magical within those two keys, but I recognize that I might be asking for too much 😅
I’m kinda looking for something that works as a magical within those two keys, but I recognize that I might be asking for too much 😅
Actually, the method above isn’t needed. You would just do
body[1..-2]
.
Ohh, so you mean you have two keys and want to know what’s between them?
yup
Okay, thanks. I follow you now. I’ll have to think about that.
I might hack it by storing position rather than key and using what you proposed.
but I thought
_key
might be a bit more convenient in case I change the content later 🙂
I think there might be a limitation due to the fact that slices need to take integers (they can’t use params, etc.).
is there any way I can get the position based on
_key
before doing
body[1..-2]
?
Yeah, I think you’re on to something. And yes, you’re also correct that it may be a bit inconvenient when content changes.
As I said at the beginning of the thread, I recognize that I might be asking for GROQ to be too smart, and that might mean too expensive queries.
Might be cheaper to just bring the entire body and filter it on JS, don’t you think? It’s just a JSON object after all.
Might be cheaper to just bring the entire body and filter it on JS, don’t you think? It’s just a JSON object after all.
user A
I just made a test and added a block of text above the one I was referencing to, and now the old
_key
is referencing that new element. So I don’t think storing
_key
s would be any different than just storing positions.
user A
I just made a test and added a block of text above the one I was referencing to, and it changed the
_key
to that first element. So I don’t think storing
_key
would be any different than just storing positions.
Ahh, so the first array item got its content replaced?
I added a paragraph on top of the paragraph I was pointing to. New paragraph
_key
has the value of the old one. So
_key
is not stable enough for me to trust in that way.
I added a paragraph on top of the paragraph I was pointing to. New paragraph
_key
has the value of the old one. So
_key
is not stable enough for me to trust in that way.
I’m thinking about adding a special block type that could act as a marker while doing the JSON to content transformation.
still, figuring out how to get the in between content with GROQ remains as an unsolved challenge. 😅
Don’t worry – I’ll be lying in bed thinking about it. 😆
hahaha sorry for doing that to you, thanks so much for helping out, you rock.
Don’t be! I love questions like this.
The point of the entire thing is that I want to be able to dynamically get subsets of a post from other posts.
And since Sanity is the only headless CMS that provides a sort of ID for each block (the
_key
), I figured it was a good experiment to try out!
As an alternative, and since I’m using Next.js, I think I’m gonna fetch linked posts at build time and build those subsets there.

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?