Reference dereferencing fails after order() in array projection

12 replies
Last updated: Apr 26, 2021
Updating to
2021-03-25
and having some trouble with projection/join of a reference inside an array.

(the following is nested a few layers inside a query)

'terpenes': terpenes[]{
  percent,
  mg,
  'name': terpene->name
}
This returns what I expect, an array of objects with 3 keys each, with
name
being the proper name of the
terpene
doc.

'terpenes': terpenes[]{
  percent,
  mg,
  'name': terpene->name
},
'dominantTerpenes': terpenes|order(percent desc)[0..2]{
  percent,
  mg
},
This returns two arrays as expected, with
dominantTerpenes
only returning the 3 with the highest
percent
, however when I want the name in that array:

'terpenes': terpenes[]{
  percent,
  mg,
  'name': terpene->name
},
'dominantTerpenes': terpenes|order(percent desc)[0..2]{
  percent,
  mg,
  'name': terpene->name
},

terpenes
is still right but now
dominantTerpenes
is an empty array 🤔
What’s happening here?
AI Update

I can see why this is confusing! The issue you're running into is related to how GROQ evaluates the scope when you combine array slicing with projections that include reference dereferencing.

When you write:

'dominantTerpenes': terpenes|order(percent desc)[0..2]{
  percent,
  mg,
  'name': terpene->name
}

The problem is that the chained operations without explicit separators can cause GROQ to lose the proper context for the terpene->name reference lookup. When you slice the array with [0..2] and immediately follow it with a projection {...}, the reference resolution isn't working as expected.

The fix is to add explicit pipe operators between your operations:

'dominantTerpenes': terpenes | order(percent desc) | [0..2] | {
  percent,
  mg,
  'name': terpene->name
}

According to the pipeline components documentation, projections can optionally use the pipe operator for separation from other components. By explicitly piping between operations, you're making the data flow clear: take terpenes → order it → slice it → project it with dereferencing.

Why does the first query work but not the second?

In your first example, the projection happens directly on the original terpenes array without any intermediate operations, so the reference context is straightforward. When you add ordering and slicing, the way the operations chain together affects how GROQ evaluates the scope for each object during projection.

Alternative approach:

You could also project first (including the dereference), then order and slice:

'dominantTerpenes': terpenes[]{
  percent,
  mg,
  'name': terpene->name
} | order(percent desc)[0..2]

This works because you're resolving all the references before doing any ordering or slicing operations, so there's no ambiguity about the evaluation context.

Both solutions should resolve your empty array issue. The key takeaway is that when combining reference dereferencing with array operations like ordering and slicing, being explicit with pipe operators helps GROQ maintain the proper evaluation scope throughout the pipeline.

Show original thread
12 replies
dig up this breaking change in the docs, wonder if it’s relevant in your case?
order() only works on arrays, and returns null otherwise
https://www.sanity.io/changelog?platforms=GROQ#5df909ebba61
ah but it still returns an empty array so maybe not
Hoping that was it but yeah,
terpenes
is an array so shouldn’t be 🤔
That looks like a bug. I’ll see if we can reproduce it.
I’m not able to reproduce with
2021-03-25
. Are you absolutely sure that this is the API version you’re testing?
It would also be helpful to see an example of a document you’re querying
For sure, 1 sec I’ll put something together so you can reproduce
I can reproduce it, and it looks like a very rare bug indeed. We’ll triage and hopefully get you a fix very soon.
Thank-you!
I've been able to reduce it even further into this test: https://github.com/sanity-io/groq-test-suite/pull/47 .
The bug is related to an optimization pass not taking into account
order()
in this situation. I've been able to fix it locally by disabling the optimization in this case, but I'll also try to see if I can make it work with both order and optimization because we want things to be fast as well!
Alright, I have a fix ready that will be rolled out soon!
And it's now fully rolled out! Can you see if it works better now?

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?