GROQ: Exclude already referenced documents from array reference field options

5 replies
Last updated: Jan 28, 2026
Hey y'all, looking some some GROQ guidance,I have a type named 'service', and want to be able to list sub-services as an array of references to other 'service' documents.

When adding a new sub service ("Type to search" or going through the drop down after clicking "Add item") I want to exclude any services already added to the sub service array from the available options.

I've checked the
cheat sheet & the GROQ docs, here's what I have so far - am I even close with this?
Note I'm using a dynamic filter her as I am also using other fields to reduce the number of options - this is simply a minimal example.

export const Service = defineType({
    name: 'service',
    title: 'Service',
    type: 'document',
    fields: [
        defineField({
            name: 'subServices',
            title: 'Sub Services',
            type: 'array',
            of: [
                {
                    type: 'reference',
                    to: [{ type: 'service' }],
                    options: {
                        // How do I exclude documents already added to the 'subServices' array?
                        filter: ({ document }) => {
                            return {
                                filter: '!(_id in subServices[]._ref)',
                            }
                        }
                    }
                }
            ],
        })
    ]
})
AI Update

You're very close! The issue is that you need to pass the actual subServices array values to the GROQ filter as parameters. Here's the corrected version:

export const Service = defineType({
    name: 'service',
    title: 'Service',
    type: 'document',
    fields: [
        defineField({
            name: 'subServices',
            title: 'Sub Services',
            type: 'array',
            of: [
                {
                    type: 'reference',
                    to: [{ type: 'service' }],
                    options: {
                        filter: ({ document }) => {
                            const subServiceIds = (document.subServices || [])
                                .map(ref => ref._ref)
                                .filter(Boolean);
                            
                            return {
                                filter: '!(_id in $subServiceIds)',
                                params: { subServiceIds }
                            }
                        }
                    }
                }
            ],
        })
    ]
})

The key differences from your code:

  1. Extract the IDs: You need to map through document.subServices to get an array of _ref values from the reference objects
  2. Use parameters: Pass the array as a GROQ parameter ($subServiceIds) rather than trying to reference the field directly in the query string
  3. Handle undefined: Add the || [] fallback and filter(Boolean) to handle cases where the array doesn't exist yet or contains undefined values

The GROQ filter string !(_id in $subServiceIds) means "exclude documents whose _id is in the subServiceIds array", which is exactly what you want.

This pattern works because the filter function receives the current document state, allowing you to dynamically adjust what references are available based on what's already been selected. The dynamic filter will re-evaluate each time the subServices array changes, keeping the available options up to date.

Show original thread
5 replies

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?