Current Working Draft

This is the specification for GROQ (Graph-Relational Object Queries), a query language and execution engine made at Sanity, Inc, for filtering and projecting JSON documents. The work started in 2015. The development of this open standard started in 2019.

GROQ is authored by Alexander Staubo and Simen Svale Skogsrud. Additional follow up work by Erik Grinaker and Magnus Holm.

This specification should be considered work in progress until the first release.


A conforming implementation of GROQ must fulfill all normative requirements. Conformance requirements are described in this document via both descriptive assertions and key words with clearly defined meanings.

The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in the normative portions of this document are to be interpreted as described in IETF RFC 2119. These key words may appear in lowercase and still retain their meaning unless explicitly declared as non‐normative.

A conforming implementation of GROQ may provide additional functionality, but must not where explicitly disallowed or would otherwise result in non‐conformance.

Conforming Algorithms

Algorithm steps phrased in imperative grammar (e.g. “Return the result”) are to be interpreted with the same level of requirement as the algorithm it is contained within. Any algorithm referenced within an algorithm step (e.g. “Let completedResult be the result of calling CompleteValue()”) is to be interpreted as having at least the same level of requirement as the algorithm containing that step.

Conformance requirements expressed as algorithms can be fulfilled by an implementation of this specification in any way as long as the perceived result is equivalent. Algorithms described in this document are written to be easy to understand. Implementers are encouraged to include equivalent but optimized implementations.


GROQ (Graph‐Relational Object Queries) is a declarative language designed to query collections of largely schema‐less JSON documents. Its primary design goals are expressive filtering, joining of several documents into a single response, and shaping the response to fit the client application.

The idea behind GROQ is to be able to describe exactly what information your application needs, potentially joining together information from several sets of documents, then stitching together a very specific response with only the exact fields you need.

A query in GROQ typically starts with *. This asterisk represents every document in your dataset. It is typically followed by a filter in brackets. The filter take terms, operators and functions. A projection is wrapped in curly braces and describe the data as we want it returned.

Given these JSON documents:

{ "id": 1, "name": "Peter"}
{ "id": 2, "name": "Gamora"}
{ "id": 3, "name": "Drax"}
{ "id": 4, "name": "Groot"}
{ "id": 5, "name": "Rocket"}

The following query:

*[id > 2]{name}

Will result in the following JSON document:

  { "name": "Drax"},
  { "name": "Groot"},
  { "name": "Rocket"}



A query in GROQ has typical this form:


* returns all documents in the dataset that the current user has permissions to read. The documents are passed to a filter, which retains documents for which the expression evaluates to true. The remaining documents are passed to a projection, which determines how the result should be formatted.

A GROQ query of this form operates as a query pipeline, where the results from each expression is passed as inputs to the next. The filter and projection are both optional, and a query can have any number of them, in any order, as well as other pipeline expressions. There are implicit | (pipe) operators between these components, so the query above could equivalently be written as:

* | [<filter>] | {<projection>}

In pipeline expressions, document attributes can be accessed by name. For example, this query would fetch directors born since 1970, and return their name, year of birth, and a list of their movies:

*[_type == "director" && birthYear >= 1970]{ 
  "movies": *[ _type == "movie" && director._ref == ^._id]

2.2Source Text



Byte Order Mark (U+FEFF)

Non‐ASCII Unicode characters may freely appear within StringValue and Comment portions of GROQ.

The “Byte Order Mark” is a special Unicode character which may appear at the beginning of a file containing Unicode which programs may use to determine the fact that the text stream is Unicode, what endianness the text stream is in, and which of several Unicode encodings to interpret.

2.2.2White Space

Whitespace is not significant in GROQ, except for acting as a token separator and comment terminator. Any sequence of the following characters is considered whitespace, with Unicode code points in parenthesis.

Tab U+0009
Newline U+000A
Vertical tab U+000B
Form feed U+000C
Carriage return U+000D
Space U+0020
Next line U+0085
Non-breaking space U+00A0

Whitespace inside a string literal is interpreted as‐is.



Comments serve as query documentation, and are ignored by the parser. They start with // and run to the end of the line:

  // Comments can be on a separate line 
  "key": "value" // Or at the end of a line 

Comments cannot start inside a string literal.

  "key // This isn't a comment": "value"

2.2.4JSON Superset

GROQ’s syntax is a superset of JSON, so any valid JSON value is a valid GROQ query (that simply returns the given value). Below are a few examples of JSON values:

"Hi! 👋"
["An", "array", "of", "strings"]
  "array": ["string", 3.14, true, null],
  "boolean": true,
  "number": 3.14,
  "null": null,
  "object": {"key": "value"},
  "string": "Hi! 👋"


An expression is one of the following:

  • A literal, attribute lookup, parameter, or constant.
  • An operator invocation (and by extension a pipeline).
  • A function call.

Expressions can be used anywhere that a value is expected, such as in object values, array elements, operator operands, or function arguments. The expression is in effect replaced by the value which it evaluates to.

Note Due to parser ambiguity with filters, the following access operators can only take literals, not arbitrary expressions: array element access (e.g. array[0]), array slices (e.g. array[1..3]), and object attribute access (e.g. object["attribute"]) .


Literals are inline representations of constant values, e.g. "string" or 3.14. GROQ supports all JSON literals, with a few enhancements and additional data types.

2.4.1Boolean and Null Literals

The constants true, false, and null.

2.4.2Float Literals

Floats have an integer part, a fractional part, and an exponent part. The integer part is required, and at least one of the fractional or exponent parts must be given.

The integer part is equivalent to an integer literal. The fractional part is a decimal point . followed by a sequence of digits. The exponent part is e or E followed by an optional + or - sign followed by an integer specifying base‐10 exponentiation.

The following are examples of float literals:

3.143e6 // Equivalent to 3000000.0
3.14eE0 // Equivalent to 3.14
3.14e-2 // Equivalent to 0.0314

2.4.3String Literals

A sequence of zero or more UTF‐8 encoded characters surrounded by single or double quotes, e.g. "Hello world! 👋". The following escape sequences are supported (mirroring JSON), all of which are valid in both single- and double‐quoted string literals:

  • \\: backslash
  • \/: slash
  • \': single quote
  • \": double quote
  • \b: backspace
  • \f: form feed
  • \n: newline
  • \r: carriage return
  • \t: tab
  • \uXXXX: UTF‐16 code point, where XXXX is the hexadecimal character code
  • \uXXXX\uXXXX: UTF‐16 surrogate pair

2.4.4Array Literals

A comma‐separated list of values enclosed by [], e.g. [1, 2, 3]. The final element may be followed by an optional trailing comma.

2.4.5Object Literals

A comma‐separated list of key‐value pairs enclosed by {}, where the key and value of each pair is separated by :, e.g. {"a": 1, "b": 2}. Keys must be strings. The final pair may be followed by an optional trailing comma.

2.4.6Pair Literals

Two values separated by =>, e.g. "a" => 1.

2.4.7Range Literals

Two values separated by .. (right‐inclusive) or ... (right‐exclusive), e.g. 1..3.


Identifiers name query entities such as attributes, parameters, functions, and some operators. Identifiers must begin with a-zA-Z_, followed by any number of characters matching a-zA-Z0-9_. Parameters are prefixed with $.

2.5.1Reserved Keywords

The following keywords are reserved and cannot be used as identifiers:

  • false
  • null
  • true

2.6Attribute Lookup

A bare identifier looks up the value of the corresponding attribute in the document or object at the root of the current scope. For example, in the following query categoryreturns the value of the category attribute of the document currently being considered by the filter:

*[category == "news"]

If the attribute does not exist, or if the root value of the scope is not a document or object, then the identifier will return null.

Note JSON allows attribute keys to be any arbitrary UTF‐8 string. In cases where the key is not a valid GROQ identifier, it can instead be accessed by using the @ operator (typically returning the current document) and the [] attribute access operator, e.g. @["1 illegal name 🚫"].

2.6.1Attribute Scope

Attribute lookups are scoped, such that the same identifier may refer to different attributes in different contexts. New scopes are created by pipeline expressions, typically by iterating over the piped array elements and evaluating an expression in the scope of each element.

The @ operator can be used to access the root value of the scope (also known as “this”), like in the following example where it refers to each number contained in the numbers array:

numbers[ @ >= 10 ]

Scopes can also be nested, in which case the ^ operator can be used to access the root value of the parent scope. Consider the following query:

*[ _type == "movie" && releaseYear >= 2000 ]{
  crew{name, title},
  "related": *[ _type == "movie" && genre == ^.genre ]

In the filter, _type and releaseYear access the corresponding attributes of each doument passed from *. Similarly, in the projection, title, releaseYear, and crewaccess the corresponding attributes from each document passed from the filter. However, in the nested crew projection, name and title access the attributes of each object passed from the crew array – notice how the outer and inner titleidentifiers refer to different attributes (one is from the movie, the other is from the crew member).

The related pipeline components also create new scopes where _type and genrerefer to the attributes of each document fetched from the preceding * operator, not those of the surrounding projected document. Notice how the ^ operator is used to access the document at the root of the parent (outer) scope and fetch its genreattribute.


GROQ supports nullary, unary, and binary operators which return a single value when invoked. Unary operators can be either prefix or postfix (e.g. !true or ref->), while binary operators are always infix (e.g. 1 + 2). Operators are made up of the characters =<>!|&+-*/%@^, but identifiers can also be used to name certain binary operators (e.g. match), in which case they are considered reserved keywords.


GROQ function calls are expressed as a function identifier immediately followed by a comma‐separated argument list in parentheses, e.g. function(arg1, arg2). The final argument may be followed by an optional trailing comma. Functions can take any number of arguments (including zero), and return a single value.

2.9Pipeline Expressions

Pipelines are constructed by the binary | operator, which takes a left‐hand value and passes it to a right‐hand pipeline component which processes it and returns a new value. For example, the following pipeline fetches all documents, pipes them to a filter which returns an array of documents that satisfy the filter, then pipes them to an order component which sorts them:

* | [ _type == "person" && age >= 18 ] | order(name)

Pipeline expressions typically take a piped array and iterate over each element, evaluating an expression in the scope of the element which determines its output. The | operator is implicit (optional) in front of certain components, and they may also have a range of syntactic sugar to make them more pleasant to work with (projections in particular).

3Data Types

GROQ is strongly typed, meaning there is no implicit type conversion. Type conflicts (e.g. 1 + "a") will yield null.

3.1Basic Data Types


Logical truth values, i.e. true and false.


Signed 64‐bit double‐precision floating point numbers, e.g. 3.14, using the IEEE 754 binary64 format. These have a magnitude of roughly 10⁻³⁰⁷ to 10³⁰⁸, and can represent 15 significant figures with exact precision – beyond this, significant figures are rounded to 53‐bit precision. The special IEEE 754 values of infinity and NaN (not a number) are not supported, and are coerced to null.


An unknown value, expressed as null. This is the SQL definition of null, which differs from the typical definition of “no value” in programming languages, and implies among other things that 1 + null yields null (1 plus an unknown number yields an unknown number).


A UTF‐8 encoded string of characters. The maximum string length is undefined.

3.2Composite Data Types


An ordered collection of values, e.g. [1, 2, 3]. Can contain any combination of other types, including other arrays.


An unordered collection of key/value pairs (referred to as attributes) with unique keys, e.g. {"a": 1, "b": 2}. Keys must be strings, while values can be any combination of other types, including other objects. If duplicate keys are specified, the last key is used.


A pair of values, e.g. "a" => 1. Pairs can contain any combination of other types, including other pairs, and are mainly used internally with e.g. projection conditionals andselect(). In returned JSON, pairs are represented as arrays with two values.


An interval containing all values that are ordered between the start and end values. The starting value is always included, while the end may be either included or excluded. A right‐inclusive range is expressed as two values separated by .., e.g. 1..3, while a right‐exclusive range is separated by ..., e.g. 1...3.

Ranges can have endpoints of any basic data type, but both endpoints must be of the same type (except integers and floats which can be used interchangeably). Ranges with incompatible or invalid endpoints types will yield null.

Ranges are mainly used internally, e.g. with the in operator and array slice access operator. The endpoints may have context‐dependant semantics, e.g. in array slices the range [2..-1] will cover the range from the third array element to the last element, while the same range is considered empty when used with in. For more details, see the documentation for the relevant operators.


Subtypes are subsets of basic or composite types. Operators and functions that can act on the supertype can always act on the subtype as well, but the behavior may be modified, and some operators and functions can only act on the subtype.


Datetimes are strings with ISO 8601‐formatted date/time combinations.


References are objects that represent a reference to a different document. They have the following special attributes (in addition to other arbitrary attributes):

  • _ref (path, required): The ID of the referenced document.


4.1Logical Operators

GROQ supports the usual logical operators: logical and, logical or, and logical not. Operands must be booleans or null. If evaluation of an operand yields an invalid type it will be coerced to null.

4.1.1Logical And

&& returns true if both operands are true, otherwise it returns false or null. The complete truth table is:

  • true && truetrue
  • true && falsefalse
  • false && truefalse
  • false && falsefalse
  • true && nullnull
  • false && nullfalse
  • null && truenull
  • null && falsefalse
  • null && nullnull

4.1.2Logical Or

|| returns true if either operand is true, otherwise it returns false or null. The complete truth table is:

  • true || truetrue
  • true || falsetrue
  • false || truetrue
  • false || falsefalse
  • true || nulltrue
  • false || nullnull
  • null || truetrue
  • null || falsenull
  • null || nullnull

4.1.3Logical Not

! returns the logical negation of the unary right‐hand operand. The complete truth table is:

  • !truefalse
  • !falsetrue
  • !nullnull

4.2Comparison Operators

Comparison operators compare two values, returning true if the comparison holds or false otherwise. The operands must be of the same type (except for integers and floats which are interchangeable), otherwise the comparison returns null as usual for type conflicts. If any operand is null, the comparison returns null.

Comparisons are currently only supported for basic data types (booleans, integers, floats, and strings), comparison of other data types is currently undefined.

Equality comparisons (== and !=) have the following semantics:

  • Booleans: identical logical truth values.
  • Integers and floats: identical real number values.
  • Strings: identical lengths and Unicode code points (case sensitive).
  • Nulls: always yield null.

Ordered comparisons (>, <, ≥, and ≤) use the following order:

  • Booleans: true is greater than false.
  • Integers and floats: numerical order.
  • Strings: numerical Unicode code point order (i.e. case‐sensitive), compared character‐by‐character. For overlapping strings, shorter strings are ordered before longer strings.
  • Nulls: always yield null.


Returns true if the operands are considered equal.


Returns true if the operands are considered not equal.

4.2.3Greater Than

Returns true if the left‐hand operand is greater than (ordered after) the right‐hand operand.

4.2.4Lesser Than

Returns true if the left‐hand operand is lesser than (ordered before) the right‐hand operand.

4.2.5Greater Than or Equal

Returns true if the left‐hand operand is considered greater than or equal to the right‐hand operand.

4.2.6Lesser Than or Equal

Returns true if the left‐hand operand is considered lesser than or equal to the right‐hand operand.

4.2.7Compound Type Membership

Returns true if the left‐hand operand is contained within the right‐hand operand. The right‐hand operand may be an array, range, or path.

If the right‐hand operand is an array, the left‐hand operand may be of any type. The left‐hand operand is compared for equality (==) with each element of the array, returning true as soon as a match is found. If no match is found, it returns null if the array contains a null value, otherwise false.

If the right‐hand operand is a range, the left‐hand operand must be of the same type as the range endpoints. Returns true if the left‐hand operand is ordered between the range endpoints, otherwise false.

If the right‐hand operand is a path, the left‐hand operand must be a string or path. Returns true if the left‐hand operand is matched by the right‐hand path pattern, otherwise false.

4.3Access Operators

Access operators are used to access members of compound types, e.g. object attributes or array elements, returning null if the member does not exist or the operator is incompatible with the left‐hand operand’s type. Access operators can be chained, where each operator accesses the result of the preceding chain, e.g. object.ref->array[1].

A handful of access operators, namely *, ^, and @, do not take any operands and instead simply return the appropriate value. As such, they can only be used at the beginning of access expressions.


Takes no operands, and returns an array of all stored documents that the current user has access to. It is typically used at the beginning of a GROQ query pipeline, such as *[_type == "movie"]{title, releaseYear}.

Note Not to be confused with the * multiplication operator, which takes two operands (the factors to be multiplied).


Takes no operands, and returns the root value of the current scope, or null if no root value exists. For example, in the document filter *[@.attribute == "value"] it refers to the currently filtered document, and in the expression numbers[@ >= 10] it refers to the currently filtered number of the numbers array.


Takes no operands, and returns the root value of the parent scope, or null if no parent scope exists. For example, in the following query it refers to the projected document, not the document passed from the inner *:

*{ "referencedBy": *[ references(^._id) ]}

4.3.4Object Attribute Access

Returns the value of the object attribute given by the right‐hand identifier, e.g. object.attribute.

4.3.5Object Attribute Access

Returns the object attribute with the given key, e.g. object["attribute"]. This is equivalent to the . access operator, but useful when the attribute name is not a legal GROQ identifier.

Note The attribute name must be a string literal due to parser ambiguity with filters.

4.3.6Reference Access

Returns the document referenced by the left‐hand reference, e.g. ref->. It may optionally be followed by an attribute identifier, in which case it returns the value of the given attribute of the referenced document, e.g. ref->attribute. If the reference points to a non‐existent document (for a weak reference) it returns null.

4.3.7Array Element Access

Returns the array element at the given zero‐based index, e.g. array[2] yields the third array element. Negative indices are based at the end of the array, e.g. array[-2] yields the second‐to‐last element.

Note The element index must be an integer literal due to parser ambiguity with filters.

4.3.8Array Slice

Returns a new array containing the elements whose indices fall within the range, e.g. array[2..4] yields the new array [array[2], array[3], array[4]]. Ranges may extend beyond array bounds.

Negative range endpoints are based at the end of the array, e.g. array[2..-1] yields all elements from the third to the last. If the right endpoint falls before the left endpoint the result is an empty array.

Note The range must be a range literal due to parser ambiguity with filters.

4.3.9Array Filter

Returns a new array with the elements for which the filter expression evaluates to true, e.g. people[birthYear >= 1980].The filter is evaluated in the scope of each array element.

4.3.10Array Traversal

Traverses the left‐hand array, applying the optional right‐hand access operator to each element and collecting the resulting values in a flat array – e.g. array[].attributeyields a flat array of the attribute attribute value of each array element, and array[]->name yields a flat array of the name attribute values of each document referenced by array. If no right‐hand access operator is given, it defaults to returning a flat array containing each traversed element.

4.3.11Array/Object Expansion

Expands the right‐hand array or object into the surrounding literal array or object, e.g. [...[1,2], 3, ...[4,5]] yields [1,2,3,4,5], and {...{"a":1},"b":2,{"c":3}}yields {"a":1,"b":2,"c":3}.

4.4Arithmetic Operators

Arithmetic operators accept any combination of float and integer operands. If any operand is a float, or if the result has a non‐zero fractional part, the result is a float; otherwise it is an integer.

The + operator is also used to concatenate two strings, arrays, or objects.

Note Floating point arithmetic is fundamentally imprecise, so operations on floats may produce results with very small rounding errors, and the results may vary on different CPU architectures. For example, 3.14+1 yields 4.140000000000001. The round() function can be used to round results.

4.4.1Addition and Concatenation

Adds two numbers, e.g. 3+2 yields 5. Also acts as a prefix operator for positive numbers, e.g. +3 yields 3.

Also concatenates two strings, arrays, or objects, e.g. "ab"+"cd" yields "abcd". If two objects have duplicate keys, the key from the right‐hand object replaces the key from the left‐hand one.


Subtracts two numbers, e.g. 3-2 yields 1. Also acts as a prefix operator for negative numbers, e.g. -3.


Multiplies two numbers, e.g. 3*2 yields 6.


Divides two numbers, e.g. 3/2 yields 1.5. Division by 0 yields null.


Raises the left‐hand operand to the power of the right‐hand operand, e.g. 2**3 yields 8.

Fractional and negative exponents follow the normal rules of roots and inverse exponentiation, so e.g. the square root of 4 is taken with 4**(1/2) (yielding 2), and the inverse square root of 4 is taken with 4**-(1/2) (yielding 0.5).


Returns the remainder of the division of its operands, e.g. 5%2 yields 1. The remainder has the sign of the dividend and a magnitude less than the divisor.

4.5Full-Text Search Operators

Full‐text search operators perform searches of text content using inverted search indexes. Content is tokenized as words (i.e. split on whitespace and punctuation), with no stemming or other processing.

4.6Pipeline Operators

Pipelines are a calling convention where a left‐hand array is passed to a right‐hand pipeline expression. Pipelines are constructed via the | operator, which may be implicit.

4.6.1Pipe Operator

Passes the left‐hand array to the right‐hand pipeline expression and returns the result, e.g. * | order(_id).

4.7Operator Precedence

Operator precedence is listed below, in descending order and with associativity in parenthesis:

  • . (left), | (left)
  • -> (left)
  • ** (right)
  • * (left), / (left), % (left)
  • + (left), - (left), ! (right)
  • ... (right)
  • == , !=, >, ≥, <, ≤ (all left), in (left), match (left)
  • && (left)
  • || (left)
  • * (none), @ (none), ^ (none)

Precedence can be overridden by grouping expressions with (), e.g. (1+2)*3.


Parameters are client‐provided values that are substituted into queries before execution. Their names must begin with $ followed by a valid identifier, and their values must be JSON literals of any type (take care to quote strings). Since they are JSON literals they can only contain values, not arbitrary GROQ expressions, and are safe to pass from user input.

For example, the following query may be given parameters such as $type="myType" and $object={"title": "myTitle", "value": 3}:

*[ _type == $type && title == $object.title && value > $object.value ]

6Pipeline Expressions

A pipeline connects a set of expressions via the | (pipe) operator, by passing the array given by the left‐hand operand to the pipeline expressions given by the right‐hand operand and yields its return value. Some expressions have an implicit pipe operator, and thus do not need to be explicitly prefixed by |.

For example, the following query pipeline fetches all documents from the data store, passes them to a filter, then a projection, and finally orders the results:

* | [ _type == "movie" && releaseYear >= 1980] | {
} | order(releaseYear desc, title asc) |

A pipeline expression typically iterates over the array elements and evaluates an expression in the scope of each iterated value (as described in “Attribute Scope” in the Syntax section

6.1Filter Expression

Iterates over the elements of the piped array, evaluates the given boolean expression in the scope of each element, and returns an array of elements for which the expression evaluated to true. The preceding | operator is optional for filters.

For example, the following filter will retain documents of type moviewhere the releaseYear attribute is greater than or equal to 1980:

*[ _type == "movie" && releaseYear >= 1980]

6.2Slice Expression

Returns an array containing the elements of the piped array whose zero‐based indices fall within the range, e.g. * | [2..4] yields elements 3, 4, and 5 from the piped array. The preceding | operator is optional for slices. The range may extend beyond the array bounds.

Negative range endpoints are based at the end of the piped array, so e.g. array[2..-1] yields all elements from the third to the last. If the right endpoint falls before the left endpoint the result is an empty array.

6.3Subscript Expression

Returns the element at the given zero‐based index of the piped array, e.g. * | [2] returns the third element of the piped array, or null if not found. Negative indices are based at the end of the array, e.g. array[-2] yields the second‐to‐last element. The preceding |operator is optional for slices.

6.4Projection Expression

Projections iterate over the elements of the piped array, generate an object of the given form evaluated in the scope of each element, and append it to the output array. The preceding | operator is optional for projections.

For example, the projection * | {"key": value} iterates over all documents, and for each document generates an object with a single key key whose value is set to the value of the value attribute of the document, if any.

Attribute values in projections can be arbitrary GROQ expressions, e.g. * | {"name": firstName + " " + lastName}. Any attribute whose value evaluates to null is not included in the projection.

If bare attributes are given in a projection, this inserts the corresponding attribute from the input element in the output object – e.g. the projection {_id, name} is exactly equivalent to {"_id": _id, "name": name}. Bare attributes can also be used with access operators, where the left‐most attribute name is used as the key, e.g. {ref->attribute.array[0]} will result in an object with a ref key containing the specified array element. Other objects can be expanded into the projection with the ...operator. For example, {name,} will take all attributes from the properties object and place them in the root of the projected object along with the root name attribute.

A bare ... is syntactic sugar for ...@, i.e. it inserts all attributes from the currently iterated element into the projection. For example, {..., "key": "value} generates an object with all of the object’s original attributes in addition to the generated key attribute.

If multiple keys with the same name are given, then the latest key wins. The only exception is with the bare ... syntactic sugar mentioned above, which is always evaluated first regardless of its position in the projection. For example, the projection {"name": "someName", ...} will replace the original name attribute of the object, if any.

Since projection values are arbitrary GROQ expressions, nested projections are supported (and encouraged), e.g.:

*[ _type == "book" ]{
  "authors": authors{
    "name": firstName + " " + lastName,

In this case, the nested authors projection takes its input from the authors array, and generates an output array of projected objects as usual. This projection is evaluated in a new scope (as described in “Attribute Scope” in the Syntax section^ operator.

Projections also have syntactic sugar for conditionals, expressed as condition => {}. If condition evaluates to true, the object on the right‐hand side of => is expanded into the projection. For example, the following projection will include the movies attribute containing a list of related movies if the person is a director, otherwise the attribute will be omitted:

  role == "director" => {
    "movies": *[ _type == "movie" && director._ref == ^._id ]

This syntax is exactly equivalent to => {}). Each conditional in a projection is evaluated separately – for cases where multiple conditions overlap and only a single result (the first) should be included then the full select() syntax must be used instead.

6.5Map Expression

Mapping evaluates an arbitrary expression in the scope of each piped element, and appends the result to the output array – e.g. * | (firstName + " " + lastName) returns an array of names. This can also be used e.g. for array projections, such as * | ([ firstName, lastName, age ]) returning an array of 3‐element arrays that each contains the person’s first name, last name, and age.

6.6Order Expression

order() sorts the piped array according to the given expression and returns an array of sorted elements, e.g. * | order(name asc, age desc). The direction can be either asc or desc, defaulting to ascif not given. Any number of sort expressions can be given, which specify sorting last‐to‐first.


Functions 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. GROQ has some standard functions described in this section, but can be extended with vendor‐specific functio

Note Functions may throw errors rather than return null on type conflicts.


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

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


count(<array>) <integer>

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


defined(<any>) boolean

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


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.


now() datetime

Returns the current time in ISO 8601‐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, and this generally refers to the start time of the operation.


references(<path|string>) <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. It is typically used in query filters, e.g. *[ references("abc") ] will return any documents that contains a reference to the document abc.


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.

For example, the following call returns "adult" if age >= 18, otherwise "teen" if age >= 13, otherwise "child":

select( age >= 18 => "adult", age >= 13 => "teen", "child", )


Joins are supported via the reference access operator ->, via subqueries that use the parent scope operator ^, and via the references() function. The reference data type can be used in documents to explicitly reference other documents and enforce referential integrity, but joins can be made using arbitrary join conditions not involving reference fields at all.

Consider the following documents representing an employee and a department:

  "_id": "alice",
  "_type": "employee",
  "name": "Alice Anderson",
  "department": {"_ref": "engineering"}
  "_id": "engineering",
  "_type": "department",
  "name": "Engineering"

The employee can be joined with the department by applying the ->reference access operator to the department field, which fetches the referenced engineering document and inserts it into the projected document under the department attribute:

*[ _type == "employee" ]{ ..., department-> }

  "_id": "alice",
  "_type": "employee",
  "name": "Alice Anderson",
  "department": {
    "_id": "engineering",
    "_type": "department",
    "name": "Engineering"

The department can also be joined with its employees by using a subquery that refers to the department’s ID via the ^ parent scope operator:

*[ _type == "department" ]{
  "employees": *[ _type == "employee" && department._ref == ^._id ]

  "_id": "engineering",
  "_type": "department",
  "name": "Engineering",
  "employees": [
      "_id": "alice",
      "_type": "employee",
      "name": "Alice Anderson",
      "department": {"_ref": "engineering"}

Or the references() function can be used to fetch any employees that contain a reference to the department anywhere in their content:

*[ _type == "department" ]{
  "employees": *[ _type == "employee" && references(^._id) ]

  "_id": "engineering",
  "_type": "department",
  "name": "Engineering",
  "employees": [
      "_id": "alice",
      "_type": "employee",
      "name": "Alice Anderson",
      "department": {"_ref": "engineering"}

These mechanisms can be used to perform arbitrarily complex joins, see their respective reference documentation for more details.

The rest of this section will demonstrate how GROQ can be used to implement joins equivalent to those in relational algebra and SQL databases. In the following examples, the “left relation” will refer to the current document, while the “right relation” will refer to the joined documents. test

8.1Outer Joins

8.1.1Left Outer Join

A left outer join combines each document in the left relation with any documents in the right relation that match the join condition. Documents in the left relation that do not have any corresponding matches in the right relation are still included in the result, typically with a null value or similar.

The simplest left outer join is made with the reference access operator ->, which joins the referenced document from the right relation into the left relation, or null if the referenced document does not exist. For example, the following query fetches all employees and joins them with their referenced department, if any:

*[ _type == "employee" ]{ ..., department-> }

Left outer joins can also use arbitrary join conditions through subqueries. For example, the following query joins all departments which reference an employee in its employees array:

*[ _type == "employee" ]{
  "departments": *[ _type == "department" && ^._id in employees[]._ref ],

8.1.2Right Outer Joins

Right outer joins are identical to left outer joins, except that the left and right relations are swapped. Right outer joins are not directly supported in GROQ, but the same effect is easily accomplished by using a left outer join with the left and right relations swapped.

8.1.3Full Outer Joins

Full outer joins fetch all documents in both the left and right relations, and combine them on matching join conditions. Full outer joins do not directly translate to a document database, since the result is not a two‐dimensional set of tuples. However, a similar effect can be obtained by fetching all documents in both relations and joining any matches using projection conditionals.

For example, the following query fetches all employee documents and all department documents, then joins referenced departments into employees via the -> operator, and employees into departments via a subquery using ^:

*[ _type in ["employee", "department" ]{
  _type == "employee" => {
  _type == "department" => {
    "employees": *[ _type == "employee" && department._ref == ^._id ],

8.2Inner Joins


Equijoins combine documents in the left relation with documents in the right relation that match on an equality condition. Documents in the left relation that do not match any documents in the right relation are not included. This is accomplished in GROQ by using a left outer join and then filtering out any documents which did not have any matches – for example:

*[ _type == "employee" ]{ ..., department-> }[ !defined(department) ]

The equality can be made explicit by using a subquery instead of the -> access operator:

*[ _type == "employee" ]{
  "department": *[ _type == "department" && _id == ^.department._ref ][0],
}[ !defined(department) ]


Non‐equijoins are similar to equijoins, except they join on inequalities rather than equalities (e.g. >, <, or !=). For example, the following query joins employees with all departments whose baseSalary field is greater than the employee’s salary field, and removes employees which do not match the join condition:

*[ _type == "employee" ]{
  "betterDepartments": *[ _type == "department" && baseSalary > ^.salary ],
}[ count(betterDepartments) > 0 ]


θ-joins are similar to equijoins and non‐equijoins, except they join documents on any arbitrary join condition. For example, the following query joins employees with any departments whose baseSalary is higher than the employee’s salary, whose manager is not equal to the employee’s boss, and whose city field is equal to the employee’s city field:

*[ _type == "employee" ]{
  "betterDepartments": *[
    _type == "department"
    && baseSalary > ^.salary
    && manager != ^.boss
    && city == ^.city
}[ count(betterDepartments) > 0 ]


A semijoin checks whether a document in the left relation matches one in the right relation on the join condition, but does not actually include any contents from the right relation in the result. Semijoins are performed by including joins in a GROQ filter, for example in the following query that fetches all employees that belong to the finance department:

*[ _type == "employee" && department->_id == "finance" ]

Semijoins can also be done with subqueries, like the following example which fetches employees that are contained within the employees array of at least one department.

*[ _type == "employee" && count(*[ _type == "department" && ^._id in employees[]._ref ]) > 0 ]


Antijoins are similar to semijoins, except they check whether the document in the left relation does not match any documents in the right relation on the join condition. For example, the following query fetches all employees that do not belong to any department:

*[ _type == "employee" && !defined(department->) ]

Antijoins can also be done with subqueries, like the following example which fetches employees that are not contained within the employees array of any department.

*[ _type == "employee" && count(*[ _type == "department" && ^._id in employees[]._ref ]) == 0 ]

8.2.6Natural Joins

Natural joins combine documents that have common fields with equal values. Natural joins as traditionally defined are not supported by GROQ, but a similar effect can be accomplished using the references() function which checks whether a document contains a reference to a given document anywhere within it.

For example, the following query fetches any employees that contain any references to the department anywhere in their structure, and removes departments with no employees:

*[ _type == "department" ]{
  "employees": *[ _type == "employee" && references(^._id) ],
}[ count(employees) > 0 ]

8.3Other Joins


Self‐joins join documents against other documents in the same relation. For example, the following query fetches employees whose salary values are greater than the currently considered employee:

*[ _type == "employee" ]{
  "betterPaid": *[ _type == "employee" && salary > ^.salary ],

Self‐joins can even join against the same document, like in the following contrived example:

*[ _type == "employee" ]{ ..., "self": *[ _id == ^._id ][0] }

8.3.2Cross Joins

Cross joins (or Carthesian products) join all documents in the left relation with all documents in the right relation. For example, the following query fetches all employees and joins them with all departments:

*[ _type == "employee" ]{..., "allDepartments": *[ _type == "department" ]}


  1. Comment
  2. SourceCharacter
  3. UnicodeBOM
  4. WhiteSpace
  1. 1Overview
  2. 2Language
    1. 2.1Syntax
    2. 2.2Source Text
      1. 2.2.1Unicode
      2. 2.2.2White Space
      3. 2.2.3Comments
      4. 2.2.4JSON Superset
    3. 2.3Expressions
    4. 2.4Literals
      1. 2.4.1Boolean and Null Literals
      2. 2.4.2Float Literals
      3. 2.4.3String Literals
      4. 2.4.4Array Literals
      5. 2.4.5Object Literals
      6. 2.4.6Pair Literals
      7. 2.4.7Range Literals
    5. 2.5Identifiers
      1. 2.5.1Reserved Keywords
    6. 2.6Attribute Lookup
      1. 2.6.1Attribute Scope
    7. 2.7Operators
    8. 2.8Functions
    9. 2.9Pipeline Expressions
  3. 3Data Types
    1. 3.1Basic Data Types
      1. 3.1.1Boolean
      2. 3.1.2Float
      3. 3.1.3Null
      4. 3.1.4String
    2. 3.2Composite Data Types
      1. 3.2.1Array
      2. 3.2.2Object
      3. 3.2.3Pair
      4. 3.2.4Range
    3. 3.3Subtypes
      1. 3.3.1Datetime
      2. 3.3.2Reference
  4. 4Operators
    1. 4.1Logical Operators
      1. 4.1.1Logical And
      2. 4.1.2Logical Or
      3. 4.1.3Logical Not
    2. 4.2Comparison Operators
      1. 4.2.1Equality
      2. 4.2.2Inequality
      3. 4.2.3Greater Than
      4. 4.2.4Lesser Than
      5. 4.2.5Greater Than or Equal
      6. 4.2.6Lesser Than or Equal
      7. 4.2.7Compound Type Membership
    3. 4.3Access Operators
      1. 4.3.1Everything
      2. 4.3.2This
      3. 4.3.3Parent
      4. 4.3.4Object Attribute Access
      5. 4.3.5Object Attribute Access
      6. 4.3.6Reference Access
      7. 4.3.7Array Element Access
      8. 4.3.8Array Slice
      9. 4.3.9Array Filter
      10. 4.3.10Array Traversal
      11. 4.3.11Array/Object Expansion
    4. 4.4Arithmetic Operators
      1. 4.4.1Addition and Concatenation
      2. 4.4.2Subtraction
      3. 4.4.3Multiplication
      4. 4.4.4Division
      5. 4.4.5Exponentiation
      6. 4.4.6Modulo
    5. 4.5Full-Text Search Operators
      1. 4.5.1Full-text Search
    6. 4.6Pipeline Operators
      1. 4.6.1Pipe Operator
    7. 4.7Operator Precedence
  5. 5Parameters
  6. 6Pipeline Expressions
    1. 6.1Filter Expression
    2. 6.2Slice Expression
    3. 6.3Subscript Expression
    4. 6.4Projection Expression
    5. 6.5Map Expression
    6. 6.6Order Expression
  7. 7Functions
    1. 7.1coalesce
    2. 7.2count
    3. 7.3defined
    4. 7.4length
    5. 7.5now
    6. 7.6references
    7. 7.7round
    8. 7.8select
  8. 8Joins
    1. 8.1Outer Joins
      1. 8.1.1Left Outer Join
      2. 8.1.2Right Outer Joins
      3. 8.1.3Full Outer Joins
    2. 8.2Inner Joins
      1. 8.2.1Equijoins
      2. 8.2.2Non-Equijoins
      3. 8.2.3θ-joins
      4. 8.2.4Semijoins
      5. 8.2.5Antijoins
      6. 8.2.6Natural Joins
    3. 8.3Other Joins
      1. 8.3.1Self-joins
      2. 8.3.2Cross Joins
  9. §Index