GROQ function reference

Functions in GROQ take a set of arguments of specific types and return a single value of a specific type. They may be polymorphic, i.e., accept several argument type variations possibly returning different types, and may take a variable number of arguments. Function calls return null if arguments have invalid types, and an error if the function does not exist.

Function namespaces

Namespaces allow for a stronger grouping of functionality within the GROQ specification. They create dedicated scopes for global functions, as well as safer distinctions for specific implementations of GROQ.

When using GROQ to query Sanity Content Lake has a few key namespaces:

  • The global namespace – all the base GROQ functions
  • The pt namespace – specific functions pertaining to Portable Text
  • The geo namespace – functions for managing and querying against geolocation

Accessing functions in a namespace

All functions exist within a namespace and can be accessed via a call to the function with a prefix a string of the namespace name, followed by two colons and then the function name.

// The pt namespace contains functions related to Portable Text
// This function returns a plain text version of a Portable Text object

// The geo namespace contains functions related to geolocation
// This function returns true if the second argument is fully contained in the first
geo::contains(polygon, point)

Global functions

Functions that exist for all implementations of GROQ exist in the global namespace. They can be accessed without using the namespace string.

// Non-namespaced
// equates to


coalesce(<any>...) <any>

Takes a variable number of arguments of any type and returns the first non-null argument if any, otherwise null - e.g., coalesce(null, 1, "a") returns 1.

// If exists return
// Else return title.en
// If neither exist, return null
*[_type == "documentWithTranslations"]{
  "title": coalesce(, title.en)

// If rating exists, return rating,
// Else return string of 'unknown'
*[_type == 'movie']{
  'rating': coalesce(rating, 'unknown')


count(<array>) <integer>

Returns the number of elements in the passed array, e.g. count([1,2,3]) returns 3.

// Returns number of elements in array 'actors' on each movie
*[_type == 'movie']{"actorCount": count(actors)} 

// Returns number of R-rated movies
count(*[_type == 'movie' && rating == 'R']) 


dateTime(<string>) <datetime>

Accepts a string on the RFC3339 format (e.g. 1985-04-12T23:20:50.52Z) and returns a DateTime. This is also the format used in the _createdAt and _updatedAt fields. Typically used to let GROQ know to treat a string as a date, especially useful when you need to compare them or perform time arithmetic operations.

Subtracting two DateTimes returns the number of seconds between those time stamps. Adding a number to a DateTime returns the DateTime that amount of seconds later (or earlier if the number is negative).

*[_type == "post"]{
  "timeSincePublished": dateTime(now()) - dateTime(publishedAt)


You can create RFC3339-dateTime strings in JavaScript with the Date.prototype.toISOString() method.


defined(<any>) boolean

Returns true if the argument is non-null and not an empty array or object, otherwise false.

// Returns all documents if awardWinner has any value (of any type)


identity() string

Returns the identity (user ID) of the user performing the current action, or the special values <anonymous> for unauthenticated users and <system> for system-initiated actions.


length(<array|string>) <integer>

Returns the length of the argument, either the number of elements in an array or the number of Unicode characters in a string, e.g., length([1,2,3]) returns 3, and length("Hi! πŸ‘‹") returns 5.

// Return posts with more than 2 authors
*[_type == "post" && length(authors) > 2]{

lower / upper

lower() <string>

upper() <string>

The lower() and upper() functions take a string and return back the string in all lowercase characters or all uppercase characters.

  "upperString": upper("Some String"), // Returns "SOME STRING"
  "lowerString": lower("Some String")  // Returns "some string" 


now() <string>

Returns the current time in RFC3339-format with microsecond resolution in the UTC time zone, e.g., 2019-03-06T15:51:24.846513Z. The current time is stable within an operation such that multiple calls return identical values. This generally refers to the start time of the operation except for listener queries. This refers to the event's transaction time.


Mutations using query parameters trigger two separate operations internally: first execution of queries to determine which documents to update, then a transaction to actually update the documents. now() will return different times for these two operations, referring to the start time of each operation.

// Give me all posts with a publish date in the future
*[_type == "post" && dateTime(now()) < dateTime(publishedAt)]


path(<string>) <path>

Coerces the passed string to a path, e.g. "a.b" in path("a.*").

// _id matches a.b.c.d but not a.b.c.d.e
*[_id in path("a.b.c.*")] 

// _id matches a.b.c.d and a.b.c.d.e
*[_id in path("a.b.c.**")] 

// All draft documents
*[_id in path("drafts.**")]

// Only published documents
*[!(_id in path("drafts.**"))]


references(<path|string|array>) <boolean>

Implicitly takes the document at the root of the current scope and recursively checks whether it contains any references to the given document ID(s). It is typically used in query filters, e.g., *[ references("abc")] will return any documents that contain a reference to the document abc. If providing the function with an array of document ids, it will return true if any of the ids are referenced. Learn more about joins and references.

// Using the ^ operator to refer to the enclosing document. Here ^._id refers to the id
// of the enclosing person record.
  "relatedMovies": *[_type=='movie' && references(^._id)]{ title }


round(<integer|float>[, <integer>]) <integer|float>

Rounds the given number to the nearest integer, or to the number of decimal places given by the second, optional argument - e.g. round(3.14) yields 3 and round(3.14, 1) yields 3.1.


select(<pair|any>...) <any>

Used for conditionals, i.e. "if-else" expressions. Takes a variable number of arguments that are either pairs or any other type and iterates over them. When encountering a pair whose left-hand value evaluates to true, the right-hand value is returned immediately. When encountering a non-pair argument, that argument is returned immediately. Falls back to returning null.

// If age is 18+, return "adult" string
// Else if age is 13+, return "teen" string
// Else return "child" string
  age >= 18 => "adult",
  age >= 13 => "teen",

// If popularity integer is more than 20, return "high" string
// Else if popularity is more than 10, return "medium" string
// Else if popularity is less than or equal to 10, return "low"
  "popularity": select(
    popularity > 20 => "high",
    popularity > 10 => "medium",
    popularity <= 10 => "low"

// You can also use select in a shorter from
// Let' say we want to conditionally join references 
// inside a Portable Text field
*[_type == "article"]{
    _type == "product" => {

Geolocation functions

The geo namespace contains a number of useful functions for creating and querying against locations in your data. Each function must be prefixed with the geo:: namespace syntax.

Geo documents

The functions in this section require a data type of geo. These geo documents are represented JSON as GeoJSON. Functions that accept a geo type will attempt to coerce non-geo-type data into the proper format following the geo() constructor rules.


The geo() function accepts an object as a parameter and, if possible, coerces the value to a geo-type document by a set of rules. These objects are represented in JSON as GeoJSON.

  • If the object is already a geo document, return the object.
  • If the object has a set of lat and lng (or lon) keys, return a geo object for the given point. If additional data exists on the object it will be removed from the final geo document.
  • If the object contains the key type (note: not _type), and the value of type matches one of the following strings then return a geo object with those values:
    • Point
    • LineString
    • Polygon
    • MultiPoint
    • MultiLineString
    • MultiPolygon
    • GeometryCollection
  • If none of the conditions are met, return null.

geo::latLng(latFloat, lngFloat)

The latLng function is a short-hand for creating a new geo object for a singular point. Returns a geo object from the latitude and longitude floats provided.

// Returns a geo object corresponding to the center of Oslo
geo::latLng(59.911491, 10.757933)

geo::distance(geo-point, geo-point)

The distance() function takes points and returns a numeric value for the distance between in meters.


The function only works between points. If lines or polygons are provided, the function will return null.

// Returns the distance in meters between Oslo and San Francisco
// 7506713.963060733
  geo::latLng(59.911491, 10.757933),
  geo::latLng(37.7749, 122.4194)

// Returns all documents that are storefronts
// within 10 miles of the storefront geopoint
  _type == 'storefront' &&
  geo::distance(geoPoint, $currentLocation) < 16093.4

geo::contains(geoPolygon, geo)

The contains() function returns true when the first geographic geography value fully contains the geographic geometry value. If either parameter is not a geo object – or not able to be coerced to a geo object following the rules of the geo() constructor function – the function returns null.

// Returns true if the neighborhood region is fully contained by the city region
geo::contains(cityRegion, neighborhoodRegion)

// For a given $currentLocation geopoint and deliveryZone area
// Return stores that deliver to a user's location
  _type == "storefront" &&
  geo::contains(deliveryZone, $currentLocation)

geo::intersects(geo, geo)

The intersects() function returns true when the two areas overlap or intersect. If either parameter is not a geo object – or not able to be coerced to a geo object following the rules of the geo() constructor function the function returns null.

// Creates a "marathonRoutes" array that contains
// all marathons whose routes intersect with the current neighborhood
*[_type == "neighborhood"] {
  "marathonRoutes": *[_type == "marathon" && 
                        geo::intersects(^.neighborhoodRegion, routeLine)  

Portable Text functions

The pt namespace contains functions for parsing Portable Text. Each function must be prefixed with the pt:: namespace syntax.


The text() function is a Sanity Content Lake GROQ filter that takes in document fields which are either a Portable Text block or an array of blocks, and returns a string in which blocks are appended with a double newline character (\n\n). Text spans within a block are appended without space or newline.

The function exists within the pt namespace and must be prefixed with pt::.


The text() function only works on text spans in the root children, i.e., alt text in an Image block will not be in the final plain text.

// Returns the body Portable Text data as plain text
*[_type == "post"] 
  { "plaintextBody": pt::text(body) }
// Scores posts by the amount of times the string "GROQ"
// appears in a Portable Text field
*[_type == "post"]
  | score(pt::text(body) match "GROQ")

Query Scoring


The score() function takes an arbitrary number of valid GROQ expressions and returns a numerical score, which can be used to sort and filter items in an array.

The score is calculated depending on the expressions used. For a match expression, the number of matches affects the score. For each matched boolean expression, the score is incremented.

The score value returned is stored in the _score attribute on each document, and can be accessed by the following pipeline components.


Documents that don't match the score expression(s) return a _score value of 0, but are not automatically removed from the array.

// Adds points to the score value depending 
// on the use of the string "GROQ" in each post's description 
// The value is then used to order the posts 
*[_type == "post"] 
  | score(description match "GROQ") 
  | order(_score desc) 
  { _score, title }
// Adds a point for matches in the title OR description
*[_type == "post"] 
  | score(title match "GROQ" || description match "GROQ") 
  | order(_score desc) 
  { _score, title }

// Orders blog posts by GROQ matches
// Then filters the results for only items that matched
// by checking for _score values greater than 0
*[_type == "post"] 
  | score(description match "GROQ") 
  | order(_score desc) 
  { _score, title }
  [ _score > 0 ]


The boost() function can be used to create a sense of weight in a scoring algorithm. It accepts two arguments: an expression and the amount to boost the score if the expression returns true.

Like in the score() function, the match expression will boost the score by the boost value for each instance of the matched string.


The boost() function accepts only constant positive integers and floats.

// Adds 1 to the score for each time $term is matched in the title field
// Adds 3 to the score if (movie > 3) is true
*[_type == "movie" && movieRating > 3] | 
    title match $term,
    boost(movieRating > 8, 3)

Providing multiple boosts of different values in one score() can create robust sorting.

// Creates a scoring system where $term matching in the title
// is worth more than matching in the body
*[_type == "movie" && movieRating > 3] | score(
  boost(title match $term, 4),
  boost(body match $term, 1)
  boost(movieRating > 8, 3)

// Scores games by the "impressive" difference in goals
*[_type == "game"] | score(
		boost(pointDifference > 5, 5),
		boost(pointDifference > 10, 10)

Was this article helpful?