GROQ: Filter by referenced document ID in nested array projection

6 replies
Last updated: May 24, 2021
Hey guys. I’ve see a lot of GROQ related questions here recently so apologies if I’m duplicating myself but… is there a way to get a referenced document id to be later used in filter?
I have a nested array for example with different types of objects with references, but when I try to use
references
filter inside it as such I get nothing:

_type == 'contentSubjectTag' => {
  _type,
  "title": subjectTag->title,
  "text": subjectTag->text,
  "subjects": *[_type=='subject' && references(subjectTag->_id)]{
    title,
    externalLink,
  },
},
AI Update

I can see the issue you're encountering. When you try to use references(subjectTag->_id) inside a projection, the dereferencing operation (->) happens in a different context, and you can't directly use it within the references() function like that.

The problem is that subjectTag->_id tries to dereference in the context of the outer document, but inside the nested query *[_type=='subject' && references(...)], you need the actual ID value, not a path expression.

Here are two solutions:

Solution 1: Store the ID in a variable first

_type == 'contentSubjectTag' => {
  _type,
  "title": subjectTag->title,
  "text": subjectTag->text,
  "subjectTagId": subjectTag->_id,
  "subjects": *[_type=='subject' && references(^.subjectTagId)]{
    title,
    externalLink,
  },
},

By first storing subjectTag->_id as "subjectTagId", you create a field in the projected object. Then you can reference it using the parent operator (^) in your nested query.

Solution 2: Use the reference directly

If your subjectTag field is a reference (contains {_ref: "..."} structure), you can use the reference value directly:

_type == 'contentSubjectTag' => {
  _type,
  "title": subjectTag->title,
  "text": subjectTag->text,
  "subjects": *[_type=='subject' && references(^.subjectTag._ref)]{
    title,
    externalLink,
  },
},

Here, ^.subjectTag._ref accesses the _ref property of the reference object in the parent document.

The key insight is that the parent operator ^ lets you access fields from the outer document context when you're inside a nested query. You need to either:

  1. Pre-compute the dereferenced ID as a field, then reference it with ^ (Solution 1)
  2. Access the raw _ref value from the reference object using ^ (Solution 2)

Both approaches work around the limitation that you can't perform dereferencing operations directly inside the references() function parameter. The parent operator ^ is essential here because it allows your nested query to reach back up to the parent document's context and grab the value you need.

Show original thread
6 replies
Hi
user H
not sure where the scope of
subjectTag
is being returned but you can try this.

_type == 'contentSubjectTag' => {
  _type,
  "title": subjectTag->title,
  "text": subjectTag->text,
  "subjects": *[_type=='subject' && references(subjectTag[]._id)]{
    title,
    externalLink,
  },
},
However you can reference the
subject
type by passing in it's container/parent
_id
like so.

_type == 'contentSubjectTag' => {
  _type,
  "title": subjectTag->title,
  "text": subjectTag->text,
  "subjects": *[_type=='subject' && references(^._id)]{
    title,
    externalLink,
  },
},
Let me know if these help.
Hey
user B
, thanks for getting back to me!

subjectTag
is a reference within
contentSubjectTag
so I need to use its ID really. I’ve tried the examples you’ve listed but unfortunately they didn’t work : (
Here’s a fuller example:

*[_type == "home"][0]{
  contentColumns[]{
    title,
    contentBlocks[]{
      _type == 'contentSubjectTag' => {
      	_type,
				"_id": subjectTag->_id,
				"title": subjectTag->title,
        "text": subjectTag->text,
        "subjects": *[_type=='subject' && references(subjectTag[]._id)]{
          title,
          externalLink,
        },
      },
    }
  },
}
Maybe you can try this.

*[_type == "home"][0]{
  contentColumns[]{
    title,
    contentBlocks[]{
      _type == 'contentSubjectTag' => {
      	_type,
		"subjectId": subjectTag->_id,
				"title": subjectTag->title,
        "text": subjectTag->text,
        "subjects": *[_type=='subject' && references(subjectTag[]._id)]{
          title,
          externalLink,
        },
      },
    }
  },
}
Alright, think I’ve got it with the following:
_type == 'contentSubjectTag' => {
  _type,
  _id,
  "_id": subjectTag->_id,
  "title": subjectTag->title,
  "text": subjectTag->text,
  "subjectTag": subjectTag,
  "subjects": *[_type=='subject' && references(^.subjectTag._ref)]{
    title,
    externalLink,
  },
},
Maybe you can try this.

*[_type == "home"][0]{
  contentColumns[]{
    title,
    contentBlocks[]{
      _type == 'contentSubjectTag' => {
      	_type,
// Best not to use '_' in your projection as it is saved for reserved properties
		"subjectTagId": subjectTag->_id,
		"subjectTitle": subjectTag->title,
        "subjectText": subjectTag->text,
          "subjects": *[_type=='subject' && _id == ^.subjectTagId ]{
// OR
//        "subjects": *[_type=='subject' && references(^.subjectTagId)]{
          title,
          externalLink,
        },
      },
    }
  },
}
Might not be understanding what you are trying to achieve but here is what I was thinking. Since you already have parent scope reference to the
subjectTagId
you can maybe use that to get access to the data you need.
Maybe you can try this.

*[_type == "home"][0]{
  contentColumns[]{
    title,
    contentBlocks[]{
      _type == 'contentSubjectTag' => {
      	_type,
// Best not to use '_' in your projection as it is saved for reserved properties
		"subjectTagId": subjectTag->_id,
		"subjectTitle": subjectTag->title,
        "subjectText": subjectTag->text,
          "subjects": *[_type=='subject' && _id == ^.subjectTagId ]{
// OR
//        "subjects": *[_type=='subject' && references(^.subjectTagId)]{
          title,
          externalLink,
        },
      },
    }
  },
}
Might not be understanding what you are trying to achieve but here is what I was thinking. Since you already have parent scope reference to the
subjectTagId
you can maybe use that to get access to the data you need.

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?