Using GROQ to select from referenced values in Sanity.io

4 replies
Last updated: Jul 14, 2020
boiling this down to the simplest form, is there a way I can
select()
from referenced values using GROQ?I have a reference which could refer to two possible document types. This query totally works – but it doesn’t seem like the “best” way to do it, and I’m not sure if there would be some unexpected behaviour outside of this isolated example, or in a more complicated case?

*[_type == "story" && !(_id in path('drafts.**'))] | order(publicationDate desc) {
	"references": references[] {
  	"title": reference-> name,
  	"title": reference-> title,
  }
}
AI Update

Yes! The select() function is exactly what you're looking for. Your current approach of assigning the same "title" key twice works because GROQ overwrites the first value with the second, but you're right that it's not ideal and could lead to unexpected behavior.

Here's the cleaner way using select():

*[_type == "story" && !(_id in path('drafts.**'))] | order(publicationDate desc) {
  "references": references[] {
    "title": select(
      reference->_type == "artist" => reference->name,
      reference->_type == "otherType" => reference->title
    )
  }
}

The select() function works like a switch statement - it evaluates conditions in order and returns the value for the first condition that matches. You can add as many conditions as you need for different document types, and optionally include a fallback value at the end:

"title": select(
  reference->_type == "artist" => reference->name,
  reference->_type == "venue" => reference->title,
  reference->_type == "event" => reference->eventName,
  "Untitled" // fallback if none match
)

This approach is more explicit, easier to maintain, and won't have any unexpected behavior when you scale to more complex queries. The select() function also short-circuits (stops evaluating once it finds a match), making it more efficient than your current approach.

I was expecting to do something a bit like this:
*[_type == "story" && !(_id in path('drafts.**'))] | order(publicationDate desc) {
	"references": references[] {
  	"title": select(
  		reference-> name => reference-> name,
  		reference-> title => reference-> title
		)
  }
}
oh also … the shortcoming of doing it the first way, I guess is that unnecessary conditions are searched for, met or not met, then overwritten … whereas using select would cut at first case?
How about something like:
*[_type == "story" && !(_id in path('drafts.**'))] | order(publicationDate desc) {
	"references": references[]->{
    _type == "someType" => {
      "title": name
    },
    _type == "otherType" => {
      title
    },
  }
}
oh amazing! thanks
user Z
. This is great – the second piece of the puzzle was checking if a property existed using
defined()
– having written this, I now feel like (probably falsely) like a certifiable GROQ wizard …

    "references": references[] {
      reference-> _type == "artist" => {
    		"title": reference-> name,
        "slug": reference-> slug.current,
    		"image": select(
    			defined(reference-> artworks) => reference-> artworks[0]-> {
          	...image,
  					"type": "artwork",
        		"metadata": image.asset->metadata
          },
          defined(image) => image {
       			...,
            "type": "override",
        		"metadata": asset->metadata
          },
        )
  		},
    	reference-> _type == "exhibition" => {
    		"title": reference-> title,
        "slug": reference-> slug.current,
        "image": select(
          defined(image) => image {
       			...,
          	"type": "override",
        		"metadata": asset->metadata
          },
  	        defined(reference-> images) => reference-> images[0] {
          	...,
  					"type": "installation",
        		"metadata": asset->metadata
          },
        )
  		},
  		_type == "external" => {
    		"title": title,
        link,
        "image": image {
       		...,
        	"metadata": asset->metadata
  			}
  		},
      _type == "pdf" => {
        "title": title,
        "file": file.asset->url,
        "image": image {
          ...,
          "metadata": asset->metadata
        }
      }
    }

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?