Query bug with dereferencing in subquery count (tags[].tag->_id)

1 replies
Last updated: Jun 21, 2022
is there a known bug with regard to a query like this?

*[
  _type == 'celeb' &&
  count(*[
    _type == 'fact' &&
    '9171bdb4-1a9b-4cb0-a967-211790a44073' in tags[].tag->_id &&
    celeb._ref == ^._id
  ]) > 0
]
I'm trying to get
celebs
that have a
fact
, where one of the
facts
has a
tag
of a certain ID.
My "bug" is in this line
'9171bdb4-1a9b-4cb0-a967-211790a44073' in tags[].tag->_id
. If I replace that line with a simple condition that doesn't have further de-referencing, it works as expected. The query inside
count
also works as expected when it is a stand-alone query. It just doesn't work (i.e. returns an empty array) when it is used a subquery.
I'm wondering if I'm experiencing the same issue described
here .
AI Update

Yes, you're likely experiencing a known bug that was fixed in the GROQ specification. Based on the GROQ changelog, there was indeed a bug with the parent scope operator (^) that was corrected in API version v2021-03-25.

The Bug

The issue you're describing matches this known problem: The ^ operator (parent scope reference) didn't work correctly in all contexts before v2021-03-25.

According to the changelog:

"This fixes the known issue where the ^ operator only worked in subqueries. In all other scopes, it returned the root of the current scope, instead of the parent scope."

In your query, when you use celeb._ref == ^._id inside the count() subquery, the ^._id should refer to the parent celeb document's _id. However, if you're using an older API version (v1 or earlier), this reference isn't working correctly, which explains why your query returns an empty array.

The Solution

You have a few options:

1. Upgrade your API version to v2021-03-25 or later. You can do this by specifying the API version in your Sanity client configuration:

import {createClient} from '@sanity/client'

const client = createClient({
  // ... other config
  apiVersion: '2021-03-25' // or later, like '2023-05-03'
})

2. Use the references() function as an alternative approach that works more reliably:

*[
  _type == 'celeb' &&
  count(*[
    _type == 'fact' &&
    '9171bdb4-1a9b-4cb0-a967-211790a44073' in tags[].tag->_id &&
    references(^._id)
  ]) > 0
]

The references() function checks if any reference field in the document points to the given ID. This should work for checking if celeb._ref matches the parent document's _id.

3. Restructure to avoid nested subqueries with dereferencing:

*[_type == 'celeb'] {
  _id,
  "hasFact": count(*[
    _type == 'fact' &&
    celeb._ref == ^._id &&
    '9171bdb4-1a9b-4cb0-a967-211790a44073' in tags[].tag->_id
  ]) > 0
}[hasFact == true]

This moves the dereferencing logic into a projection first, then filters on the result.

The first option (upgrading your API version) is the recommended fix, as it will give you access to the corrected ^ operator behavior and many other improvements in the GROQ specification. The bug you're experiencing is exactly what was addressed in that v2021-03-25 release.

Show original thread
1 reply
I just found out that if I simply change
tag->_id
to
tag._ref
the query will work

*[
  _type == 'celeb' &&
  count(*[
    _type == 'fact' &&
    '9171bdb4-1a9b-4cb0-a967-211790a44073' in tags[].tag._ref &&
    celeb._ref == ^._id
  ]) > 0
]

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?