GROQ

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.

Conformance

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.

1Overview

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"}
]

2Syntax

A GROQ query is a string consisting of Unicode characters. The encoding of the query string is implementation‐defined, but UTF‐8 is be the preferred choice. A query consist of a single Expression, with WhiteSpaceand Comment allowed anywhere with no effect on the interpratation.

SourceCharacter
any Unicode character

2.1JSON Superset

GROQ’s syntax is a superset of JSON, so any valid JSON value is a valid GROQ expression (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! 👋"
}

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.

WhiteSpace
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.

2.3Comments

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.4Identifier

Identifiers are used to name entities such as parameters, attributes and functions. An identifier is a sequence of one or more letters and digits. The first character in an identifier must be a letter.

Identifier
/[A-Za-z_][A-Za-z_0-9]*/

2.5Digits

GROQ uses decimal digits (0‐9) and hexadecimal digits (0‐9, a‐f) in various places.

Digit
0123456789
HexLetter
aAbBcCdDeEfF

2.6Expression

An Expression is either a literal (e.g. 15), a simple expression (e.g. @), or a compound expression (e.g. *[name == "Michael"]) or an operator call (e.g. name == "Michael"). The syntax and semantics of the different expressions are documented in their respective sections.

3Execution

3.1Overview

Note The following sub‐section is a non‐normative overview of the execution model. See the rest of the section for the exact semantics.

A GROQ query is executed inside a query context, which contains the dataset and parameters, and returns a result. Typically the result is serialized to JSON. During the execution of a query different parts of the query are evaluated in different scopes. Each scope has a this value and can be nested. Simple attributes like name always refers to an attribute on the this value.

*[_type == "person"]{name, friends[country == "NO"]}

In the preceding example we have several scopes:

  • The first filter ([_type == "person"]) creates a new scope for every document in the dataset. An equivalent scope is created inside the projection ({name, …}).
  • The country filter ([country == "NO"]) creates a new scope for each element in the friends array.

The parent expression (^) let’s you refer to parent scopes, and this enables what is typically solved with joins in many databases.

*[_type == "person"]{
  id,
  name,
  "children": *[_type == "person" && parentId == ^.id]
}

While executing the inner filter ([_type == "person" && parentId == ^.id]) the expression ^.id returns the id attribute of the parent scope’s this value. The parent scope is here the scope created by the projection ({id, name, …}).

3.2Query context

A query context consists of:

  • the dataset
  • parameter values (map from string to value)

3.3Scope

A scope consists of:

  • a this value
  • an optional parent scope
  • a query context

A root scope can be constructed from a query context, and a nested scope can be constructed from an existing scope.

NewNestedScope(value, scope)
  1. Let newScope be a new scope.
  2. Set the this value of newScope to value.
  3. Set the parent scope of newScope to scope.
  4. Set the query context of newScope to the query context of scope.
  5. Return newScope.
NewRootScope(context)
  1. Let newScope be a new scope.
  2. Set the this value of newScope to null.
  3. Set the parent scope of newScope to null.
  4. Set the query context of newScope to context.
  5. Return newScope.

3.4Expression evaluation

An expression is evaluated in a scope. Every expression type has their own evaluator function in their respective section in this specification (e.g. the evaluator of ParenthesisExpression is EvaluateParenthesis()).

Evaluate(expr, scope)
  1. Let evaluator be the evaluator of expr.
  2. Return the result of evaluator(scope).

3.5Query execution

To execute a query you must first construct a query context, and then evaluate the query expression inside a root scope.

ExecuteQuery(query, context)
  1. Let scope be the result of NewRootScope(context).
  2. Let expr be the expression of query.
  3. Let result be the result of Evalute(expr, scope).
  4. Return result.

4Data types

4.1Null

An unknown value, expressed as null. This follows 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).

Null
null

4.2Boolean

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

Boolean
true
false

4.3Number

Signed 64‐bit double‐precision floating point numbers, e.g. 3.14, following the IEEE 754 standard. 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 are not supported, and are coerced to null.

Sign
+-

4.4String

A string stores an UTF‐8 encoded list of characters.

The syntax of a string literal is a subset of JSON with the following extensions:

  • Any control characters (including newlines) are allowed to appear inside a string.
  • Extended support for refering to Unicode characters above 16‐bit: "\u{1F600}".

Escape sequences are interpreted as follows:

  • \' represents U+0027.
  • \" represents U+0022.
  • \\ represents U+005C.
  • \/ represents U+002F.
  • \b represents U+0008.
  • \f represents U+000C.
  • \n represents U+000A.
  • \r represents U+000D.
  • \t represents U+0009.
  • \uXXXX represents the Unicode code point U+XXXX.
  • \uXXXX\uYYYY, where XXXX is a high surrogate (W1, 0xD800–0xDBFF) and YYYY is a low surrogate (W2, 0xDC00–0xDFFF) is interpreted as a UTF‐16 surrogate pair and encoded into a single code point.

It’s a syntactical error when a Unicode escape sequence represents an invalid Unicode code point.

4.5Array

An ordered collection of values, e.g. [1, 2, 3]. Can contain any combination of other types, including other arrays and mixed types. An element inside an array literal can be preceeded by ... which causes it to be flattened into the array.

EvaluateArray(scope)
  1. Let result be a new empty array.
  2. For each ArrayElement:
    1. Let elementNodebe the Expression of the ArrayElement.
    2. Let elementbe the result of Evaluate(elementNode, scope).
    3. If the ArrayElement contains ...:
      1. If element is an array:
        1. Concatenate element to result.
    4. Otherwise:
      1. Append element to result.
  3. Return result.

4.6Object

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. An object can not store nullvalues, and setting an attribute value to null is equivalent to removing it from the object.

The values of an object literal can use the full power of expressions:

*[_type == "rect"]{"area": width * height}
Note A Projection expression is just an expression with an object literal to the right of it.

Object literal supports syntactical sugar when the attribute name and value is equivalent:

// These two are equivalent
*[_type == "person"]{name}
*[_type == "person"]{"name": name}
EvaluateObject(scope)
  1. Let result be a new empty object.
  2. For each ObjectAttribute:
    1. If the ObjectAttribute contains ...:
      1. If the ObjectAttribute constains an Expression:
        1. Let baseNode be the Expression.
      2. Let base be the result of Evaluate(baseNode, scope).
    2. Otherwise:
      1. Let base be the this value of scope.
    3. For each name and value of base:
      1. Set the attribute name to value in result.
    4. Otherwise:
      1. Let valueNode be the Expression of the ObjectAttribute.
    5. Let value be the result of Evaluate(valueNode, scope).
      1. If the ObjectAttribute contains a String:
        1. Let name be the string value of the String.
      2. Otherwise:
        1. Let name be the result of DetermineName(valueNode).
      3. If value is null:
        1. Remove the attribute name in result.
      4. Otherwise:
        1. Set the attribute name to value in result.
  3. Return result.
DetermineName(node)
  1. If node is an ThisAttribute:
    1. Return the string value of the Identifier of node.
  2. If node is a Projection, ElementAccess, Slice, or Filter:
    1. Let base be the first Expression of expr.
    2. Return the result of DetermineName(base).
  3. Otherwise: Return an error.

4.7Pair

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 serialized JSON, pairs are represented as arrays with two values.

EvaluatePair(scope)
  1. Let firstNode be the first Expression.
  2. Let secondNode be the second Expression.
  3. Let result be a new pair.
  4. Set the first value of result to the result of Evaluate(firstNode, scope).
  5. Set the second value of result to the result of Evaluate(secondNode, scope).
  6. Return result.

4.8Range

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.

EvaluateRange(scope)
  1. Let startNode be the first Expression.
  2. Let endNode be the second Expression.
  3. Let start be the result of Evaluate(startNode, scope).
  4. Let end be the result of Evalaute(endNode, scope).
  5. If the type of start is not the same as the type of end:
    1. Return null.
  6. Let result be a new range.
  7. Set the start value of result to start.
  8. Set the end value of result to end.
  9. Mark the range as inclusive or exclusive.
  10. Return result.

5Equality and comparison

GROQ provides trivial equality and comparison between numbers, strings and booleans. Other types are considered inequal or incomparable to each other. Incomparability between values are represented by operators returning null (e.g. 2 > "1" is null).

5.1Equality

Simple values such as numbers, strings, booleans and null are equal when they contain the same data. All other values are considered inequal to each other (e.g. [] != []).

Note In GROQ 1 == null returns false (which is different from e.g. SQL).
Equal(a, b)
  1. If both a and b is null:
    1. Return true.
  2. Let cmp be the result of PartialCompare(a, b).
  3. If cmp is Equal:
    1. Return true.
  4. Otherwise:
    1. Return false.

5.2Partial comparison

A partial comparison between two values return either Greater, Equal, Less or null. null represents that the values are incomparable to each other. This is used by the comparison operators (<, ≤, >, ≥).

PartialCompare(a, b)
  1. If the type of a is different from the type of b:
    1. Return null.
  2. If a is a number:
    1. If a < b:
      1. Return Less.
    2. If a > b:
      1. Return Greater.
    3. If a = b:
      1. Return Equal.
  3. If a is a string:
    1. For each Unicode code point (aCodePoint, bCodePoint) in a and b:
      1. If aCodePoint < bCodePoint:
        1. Return Less.
    2. If aCodePoint > bCodePoint:
      1. Return Greater.
    3. If a is shorter than b:
      1. Return Less.
    4. If a is longer than b:
      1. Return Greater.
    5. Return Equal.
  4. If a is a boolean:
    1. Return the comparison between a and b with false < true.
  5. Return null.

5.3Total comparison

A total comparison between two values return either Greater, Equal or Less. It provides a consistent ordering of values of different types (for string, numbers and boolean) and considers all other types to be equal to each other. This is used by the order() function.

TypeOrder(val)
  1. If val is number:
    1. Return 1.
  2. If val is a string:
    1. Return 2.
  3. If val is a boolean:
    1. Return 3.
  4. Return 4.
TotalCompare(a, b)
  1. Let aTypeOrder be the result of TypeOrder(a).
  2. Let bTypeOrder be the result of TypeOrder(b).
  3. If aTypeOrder != bTypeOrder:
    1. Return the result of PartialCompare(aTypeOrder, bTypeOrder).
  4. Let result be the result of PartialCompare(a, b).
  5. If result is null:
    1. Return Equal.
  6. Otherwise:
    1. Return result.

6Simple expressions

6.1This expression

A this expression returns the this value of the current scope.

*[_id == "doc"][0].numbers[@ >= 10]
                           ~
This
@
EvaluateThis(scope)
  1. Return the this value of scope.

6.2This attribute expression

A this attribute expression returns an attribute from the this value of the current scope.

*[_id == "document"][name == "Michael Bluth"]
  ~~~                ~~~~
EvaluateThisAttribute(scope)
  1. Let base be the this value of scope.
  2. Let name be the string value of the Identifier.
  3. If base is not an object, return null.
  4. If base does not contain an attribute name, return null.
  5. Return the value of the attribute name in base.

6.3Everything expression

An everything expression returns the full dataset.

*[_type == "person"]
~
EvaluateEverything(scope)
  1. Let context be the query context of scope.
  2. Return the dataset of context.

6.4Parent expression

A parent expression returns a this value for an upper scope.

// Find all people who have a cool friend
*[_type == "person" && *[_id == ^.friend._ref][0].isCool]
                                ~
EvaluateParent(scope)
  1. Let level be the number of ^ in the parent expression.
  2. Let currentScope be scope.
  3. While levelis greater than zero:
    1. Set currentScope to the parent of currentScope.
    2. If currentScope is now null, return null.
    3. Decrease level by one.
  4. Return the this value of currentScope.

6.5Function call expression

GROQ comes with a set of built‐in functions which provides additional features. See the “Functions” for available functions.

*{"score": round(score, 2)}
           ~~~~~~~~~~~~~~~
EvaluateFuncCall(scope)
  1. Let name be the string value of the Identifier.
  2. If there is no function named name:
    1. Return null.
  3. Let args be an empty array.
  4. For each Expression in FuncCallArgs:
    1. Let argumentNode be the Expression.
    2. Append argumentNode to args.
  5. Let func be the function defined under the name name.
  6. Return the result of func(args, scope).

7Compound expressions

7.1Parenthesis expression

A parenthesis expression allows you to add parenthesis around another expression to control precedence of operators.

(1 + 2) * 3
~~~~~~~
EvaluateParenthesis(scope)
  1. Let innerNode be the Expression.
  2. Let result be the result of Evaluate(innerNode, scope).
  3. Return result.

7.2Attribute access expression

An attribute access expression returns an attribute of an object.

person.name
      ~~~~~
      
person["Full Name"]
      ~~~~~~~~~~~~~
Note This expression is syntactially a subset of a filter expression.
EvaluateAttributeAccess(scope)
  1. Let baseNode be the Expression.
  2. Let base be the result of Evaluate(baseNode, scope).
  3. If base is not an object, return null.
  4. Let name be the string value of String or Identifier.
  5. If base does not contain an attribute name, return null.
  6. Return the value of the attribute name in base.

7.3Element access expression

An element access expression returns an element stored in an array. The array is 0‐indexed and a negative index accesses the array from the end (i.e. an index of -1 returns the last element; -2 refers to the second last element).

Note This expression is syntactially a subset of a filter expression.
EvaluateElementAccess(scope)
  1. Let baseNode be the Expression.
  2. Let base be the result of Evaluate(baseNode, scope).
  3. If base is not an array, return null.
  4. Let idx be the number value of Integer.
  5. If idx is negative, add the length of base to idx.
  6. If idx is still negative, return null.
  7. If idx is equal to or greater than the length of base, return null.
  8. Return the value stored at position idx in base.

7.4Slice expression

A slice expression returns a slice of an array.

people[0..10]
      ~~~~~~~
Note This expression is syntactially a subset of a filter expression.
EvaluateSlice(scope)
  1. Let baseNode be the Expression.
  2. Let base be the result of Evaluate(baseNode, scope).
  3. If base is not an array, return null.
  4. Process the left index:
    1. Let leftNode be the left value of the Range.
    2. Let left be the result of Evaluate(leftNode, scope).
    3. If left is not a number, return null.
    4. If leftis negative, add the length of base to left.
    5. Clamp left between 0 and (the length of base minus 1).
  5. Process the right index:
    1. Let rightNode be the right value of the Range.
    2. Let right be the result of Evaluate(rightNode, scope).
    3. If right is not a number, return null.
    4. If rightis negative, add the length of base to right.
    5. If the Range is exclusive, subtract one from right.
    6. Clamp right between 0 and (the length of base minus 1).
  6. Let result be an array containing the elements of base from position leftup to and including position right.
  7. Return result.

7.5Filter expression

A filter expression filters an array using another expression.

*[_type == "person"]
 ~~~~~~~~~~~~~~~~~~~
Note If the second Expression is a string/integer/range literal, this is parsed as an attribute access/element access/slice expression instead.
EvaluateFilter(scope)
  1. Let baseNode be the first Expression.
  2. Let base be the result of Evaluate(baseNode, scope).
  3. If base is not an array, return base.
  4. Let filterNode be the second Expression.
  5. Let result be a new empty array.
  6. For each element value in baseValue:
    1. Let elementScope be the result of NewNestedScope(value, scope).
    2. Let matched be the result of Evaluate(filterNode, elementScope).
    3. If matched is true, append value to result.
  7. Return result.

7.6Projection expression

A projection expression iterates over an array and creates new objects for each element.

*[_type == "person"]{name, "isLegal": age >= 18}
                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
EvaluateProjection(scope)
  1. Let baseNode be the Expression.
  2. Let base be the result of Evaluate(baseNode, scope).
  3. Let objNode be the Object.
  4. If base is not an array:
    1. Let elementScope be the result of NewNestedScope(base, scope).
    2. Let result be the result of Evaluate(objNode, elementScope).
  5. Otherwise:
    1. Let result be a new empty array.
    2. For each element value in base:
      1. Let elementScope be the result of NewNestedScope(value, scope).
    3. Let newValue be the result of Evaluate(objNode, elementScope).
      1. Append newValue to result.
  6. Return result.

7.7Pipe function call expression

GROQ comes with a set of built‐in pipe functions which provides additional features. Pipe functions always accepts an array on the left‐hand side and returns another array, and the syntax is optimized for being able to chain it together with other compund expressions. See the “Pipe functions” for available functions.

*[_type == "person"] | order(name) | {age}
                     ~~~~~~~~~~~~~
EvaluatePipeFuncCall(scope)
  1. Let baseNode be the first Expression.
  2. Let base be the result of Evaluate(baseNode, scope).
  3. If base is not an array:
    1. Return null.
  4. Let name be the string value of the Identifierof the FuncCall.
  5. If there is no pipe function named name:
    1. Return null.
  6. Let args be an empty array.
  7. For each Expression in the FuncCallArgs of the FuncCall.
    1. Let argumentNode be the Expression.
    2. Append argumentNode to args.
  8. Let func be the pipe function defined under the name name.
  9. Return the result of func(base, args, scope).

8Operators

8.1And operator

EvaluateAnd(scope)
  1. Let leftNode be the first Expression.
  2. Let left be the result of Evaluate(leftNode, scope).
  3. Let rightNode be the last Expression.
  4. Let right be the result of Evaluate(rightNode, scope).
  5. If left or right is false:
    1. Return false.
  6. If left or right is not a boolean:
    1. Return null.
  7. Return true.

8.2Or operator

EvaluateOr(scope)
  1. Let leftNode be the first Expression.
  2. Let left be the result of Evaluate(leftNode, scope).
  3. Let rightNode be the last Expression.
  4. Let right be the result of Evaluate(rightNode, scope).
  5. If left or right is true:
    1. Return true.
  6. If left or right is not a boolean:
    1. Return null.
  7. Return false.

8.3Not operator

EvaluateNot(scope)
  1. Let valueNode be the Expression.
  2. Let value be the result of Evaluate(valueNode, scope).
  3. If value is false:
    1. Return true.
  4. If value is true:
    1. Return false.
  5. Return null.

8.4Equality operators

EvaluateEquality(scope)
  1. Let leftNode be the first Expression.
  2. Let left be the result of Evaluate(leftNode, scope).
  3. Let rightNode be the last Expression.
  4. Let right be the result of Evaluate(rightNode, scope).
  5. Let result be the result of Equal(left, right).
  6. If the operator is !=:
    1. If result is true:
      1. Return false.
    2. If result is false:
      1. Return true.
  7. Return result.

8.5Comparison operators

ComparisonOperator
<,<=,>,>=
EvaluateComparison(scope)
  1. Let leftNode be the first Expression.
  2. Let left be the result of Evaluate(leftNode, scope).
  3. Let rightNode be the last Expression.
  4. Let right be the result of Evaluate(rightNode, scope).
  5. Let cmp be the result of PartialCompare(left, right).
  6. If cmp is null:
    1. Return null.
  7. If cmp is Less and the operator is < or <=:
    1. Return true.
  8. If cmp is Greater and the operator is > or >=:
    1. Return true.
  9. If cmp is Equal and the operator is <= or >=:
    1. Return true.
  10. Return false.

8.6In operator

EvaluateIn(scope)
  1. Let leftNode be the first Expression.
  2. Let left be the result of Evaluate(leftNode, scope).
  3. Let rightNode be the last Expression.
  4. Let right be the result of Evaluate(rightNode, scope).
  5. If right is an array:
    1. For each value in right:
      1. If Equal(left, value) is true:
        1. Return true.
    2. Return false.
  6. If right is a range:
    1. Let lower be the start value of right.
    2. Let upper be the end value of right.
    3. Let leftCmp be the result of PartialCompare(left, lower).
    4. Let rightCmp be the result of PartialCompare(left, upper).
    5. If leftCmp or rightCmp is null:
      1. Return null.
    6. If leftCmp is Less:
      1. Return false.
    7. If rightCmp is Greater:
      1. Return false.
    8. If the right range is exclusive and rightCmp is Equal:
      1. Return false.
    9. Return true.
  7. Return null.

8.7Match operator

8.8Asc operator

The asc operator is used by the order() function to signal that you want ascending sorting. Evaluating it in any other context returns null.

EvaluateAsc(scope)
  1. Return null.

8.9Desc operator

The desc operator is used by the order() function to signal that you want descending sorting. Evaluating it in any other context returns null.

EvaluateDesc(scope)
  1. Return null.

8.10Unary plus operator

EvaluateUnaryPlus(scope)
  1. Let valueNode be the Expression.
  2. Let value be the result of Evaluate(valueNode, scope).
  3. If value is a number:
    1. Return value.
  4. Return null.

8.11Unary minus operator

EvaluateUnaryMinus(scope)
  1. Let valueNode be the Expression.
  2. Let value be the result of Evaluate(valueNode, scope).
  3. If value is a number:
    1. Return value with opposite sign.
  4. Return null.

8.12Binary plus operator

EvaluatePlus(scope)
  1. Let leftNode be the first Expression.
  2. Let left be the result of Evaluate(leftNode, scope).
  3. Let rightNode be the last Expression.
  4. Let right be the result of Evaluate(rightNode, scope).
  5. If both left and right are strings:
    1. Return the string concatenation of left and right.
  6. If both left and right are numbers:
    1. Return the addition of left and right.
  7. Return null.

8.13Binary minus operator

EvaluateMinus(scope)
  1. Let leftNode be the first Expression.
  2. Let left be the result of Evaluate(leftNode, scope).
  3. Let rightNode be the last Expression.
  4. Let right be the result of Evaluate(rightNode, scope).
  5. If both left and right are numbers:
    1. Return the subtraction of left from right.
  6. Return null.

8.14Binary star operator

EvaluateStar(scope)
  1. Let leftNode be the first Expression.
  2. Let left be the result of Evaluate(leftNode, scope).
  3. Let rightNode be the last Expression.
  4. Let right be the result of Evaluate(rightNode, scope).
  5. If both left and right are numbers:
    1. Return the multiplication of left and right.
  6. Return null.

8.15Binary slash operator

EvaluateSlash(scope)
  1. Let leftNode be the first Expression.
  2. Let left be the result of Evaluate(leftNode, scope).
  3. Let rightNode be the last Expression.
  4. Let right be the result of Evaluate(rightNode, scope).
  5. If both left and right are numbers:
    1. Return the division of left by right.
  6. Return null.

8.16Binary percent operator

EvaluatePercent(scope)
  1. Let leftNode be the first Expression.
  2. Let left be the result of Evaluate(leftNode, scope).
  3. Let rightNode be the last Expression.
  4. Let right be the result of Evaluate(rightNode, scope).
  5. If both left and right are numbers:
    1. Return the remainder of left after division by right.
  6. Return null.

8.17Binary double star operator

EvaluateStarStar(scope)
  1. Let leftNode be the first Expression.
  2. Let left be the result of Evaluate(leftNode, scope).
  3. Let rightNode be the last Expression.
  4. Let right be the result of Evaluate(rightNode, scope).
  5. If both left and right are numbers:
    1. Return the exponentiation of left to the power of right.
  6. Return null.

8.18Dereference operator

EvaluateDereference(scope)
  1. Let valueNode be the Expression.
  2. Let value be the result of Evaluate(valueNode, scope).
  3. If value is not an object:
    1. Return null.
  4. If value does not have an attribute “_ref”:
    1. Return null.
  5. Let ref be the value of the attribute “_ref” in value.
  6. If ref is not a string:
    1. Return null.
  7. Let dataset be the dataset of the query context of scope.
  8. If dataset is not an array:
    1. Return null.
  9. Let result be null.
  10. For each document in dataset:
    1. If document is an object and has an attribute “_id”:
      1. Let id be the value of the attribute “_id” in document.
    2. If Equal(ref, id) is true:
      1. Set result to document.
    3. Stop the loop.
  11. If the dereference expression contains a String:
    1. Let name be the string value of the String.
    2. If result is an object and contains an attribute name:
      1. Return the value of the attribute name in result.
    3. Otherwise:
      1. Return null.
  12. Return result.

9Precedence and associativity

In this specification the various expressions and operators are defined in ambiguously in terms on precedence and associativity. The table below describes the precedence levels used to determine the correct unambiguous interpretation.

From highest to lowest:

10Functions

Functions provide additional functionalty to GROQ queries. They are invoked through a Function call expression. Note that function arguments are not evaluated eagerly, and it’s up to the function to decide which scope the arguments are evaluated it. As such, all functions below take an array of nodes.

An implementation may provide additional functions, but should be aware that this can cause problems when interopting with future versions of GROQ.

10.1coalesce

The coalesce function returns the first value of the arguments which is not null.

coalesce(args, scope)
  1. For each arg in args:
    1. Let value be the result of Evaluate(arg, scope).
    2. If value is not null:
      1. Return value.
  2. Return null.

10.2count

The count function returns the length of an array.

count(args, scope)
  1. If the length of args is not 1:
    1. Return null.
  2. Let baseNode be the first element of args.
  3. Let base be the result of Evaluate(baseNode, scope).
  4. If base is an array:
    1. Return the length of base.
  5. Otherwise:
    1. Return null.

10.3defined

The defined function checks if the argument is not null.

defined(args, scope)
  1. If the length of args is not 1:
    1. Return null.
  2. Let baseNode be the first element of args.
  3. Let base be the result of Evaluate(baseNode, scope).
  4. If base is null:
    1. Return false.
  5. Otherwise:
    1. Return true.

10.4length

The length function returns the length of a string or an array.

length(args, scope)
  1. If the length of args is not 1:
    1. Return null.
  2. Let baseNode be the first element of args.
  3. Let base be the result of Evaluate(baseNode, scope).
  4. If base is a string:
    1. Return the length of base.
  5. If base is an array:
    1. Return the length of base.
  6. Return null.

10.5references

The references function implicitly takes this value of the current scope and recursively checks whether it contains any references to the given document ID.

references(args, scope)
  1. If the length of args is not 1:
    1. Return null.
  2. Let pathNode be the first element of args.
  3. Let path be the result of Evaluate(pathNode, scope).
  4. If path is not a string:
    1. Return null.
  5. Let base be the this value of scope.
  6. Return the result of HasReferenceTo(base, path).
HasReferenceTo(base, path)
  1. If base is an array:
    1. For each value in base:
      1. Let result be the result of HasReferenceTo(value, base).
    2. If result is true:
      1. Return true.
    3. Return false.
  2. If base is an object:
    1. If base has an attribute “_ref”:
      1. Let ref be the value of the attribute “_ref” in base.
    2. Return the result of Equal(ref, path).
    3. For each key and value in base:
      1. Let result be the result of HasReferenceTo(value, base).
    4. If result is true:
      1. Return true.
  3. Return false.

10.6round

The round function accepts a number and rounds it to a certain precision.

round(args, scope)
  1. If the length of args is less than 1 or greater than 2:
    1. Return null.
  2. Let numNode be the first element of args.
  3. Let num be the result of Evaluate(numNode, scope).
  4. If num is not a number:
    1. Return null.
  5. If the length of args is 2:
    1. Let precNode be the second element of args.
    2. Let prec be the result of Evaluate(precNode, scope).
    3. If prec is not a number:
      1. Return null.
  6. Otherwise:
    1. Let prec be 0.
  7. Return num rounded to prec number of digits after the decimal point.

10.7select

The select function chooses 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.

select(args, scope)
  1. For each arg in args:
    1. If arg is a Pair:
      1. Let condNode be the first Expression of the Pair.
    2. Let resultNode be the second Expression of the Pair.
      1. Let cond be the result of Evaluate(condNode, scope).
      2. If cond is true:
        1. Return the result of Evaluate(resultNode, scope).
    3. Otherwise:
      1. Return the result of Evaluate(arg, scope).

11Pipe functions

Pipe functions provide additional functionalty to GROQ queries. They are invoked through a Pipe function call expression. They differ from regular functions in that they always accept an array as input and returns another array (or null). As such, the syntax is optimized for chaining (the array it works on comes on the left‐hand side instead of being argument):

*[_type == "person"] | order(name) | {age}
Note that function arguments are not evaluated eagerly, and it’s up to the function to decide which scope the arguments are evaluated in. All definitions below take an array of nodes.

An implementation may provide additional pipe functions, but should be aware that this can cause problems when interopting with future versions of GROQ.

11.1order

The order function sorts an array based on arbitrary expressions.

order(base, args, scope)
  1. If the length of args is 0:
    1. Return null.
  2. Let cmpbe a function which takes two arguments and returns either Less, Equal or Greater.
  3. Define cmp(left, right) as follows:
    1. Let leftScope be the result of NewNestedScope(left, scope).
    2. Let rightScope be the result of NewNestedScope(right, scope).
    3. For each argNode of args:
      1. Let direction be Normal.
    4. Let valueNode be argNode.
      1. If valueNode is an Ascoperator:
        1. Set valueNode to be the Expression of the Asc operator.
      2. Else if valueNode is a Desc operator:
        1. Set direction to Reverse.
      3. Set valueNode to be the Expression of the Desc operator.
      4. Let leftValue be the result of Evaluate(valueNode, leftScope).
      5. Let rightValue be the result of Evaluate(valueNode, rightScope).
      6. Let order be the result of TotalCompare(leftValue, rightValue).
      7. If directionis Reverse and order is Less:
        1. Set order to Greater.
      8. Else if direction is Reverse and order is Greater:
        1. Set order to Less.
      9. If order is not Equal:
        1. Return order.
    5. Return Equal.
  4. Return a sorted array using cmp as the comparator function.

§Index

  1. And
  2. Array
  3. ArrayElement
  4. ArrayElements
  5. Asc
  6. AttributeAccess
  7. Boolean
  8. Comment
  9. CommentChar
  10. Comparison
  11. ComparisonOperator
  12. CompoundExpression
  13. Decimal
  14. Dereference
  15. Desc
  16. DetermineName
  17. Digit
  18. DoubleStringCharacter
  19. ElementAccess
  20. Equal
  21. Equality
  22. EqualityOperator
  23. EscapeSequence
  24. Evaluate
  25. EvaluateAnd
  26. EvaluateArray
  27. EvaluateAsc
  28. EvaluateAttributeAccess
  29. EvaluateComparison
  30. EvaluateDereference
  31. EvaluateDesc
  32. EvaluateElementAccess
  33. EvaluateEquality
  34. EvaluateEverything
  35. EvaluateFilter
  36. EvaluateFuncCall
  37. EvaluateIn
  38. EvaluateMinus
  39. EvaluateNot
  40. EvaluateObject
  41. EvaluateOr
  42. EvaluatePair
  43. EvaluateParent
  44. EvaluateParenthesis
  45. EvaluatePercent
  46. EvaluatePipeFuncCall
  47. EvaluatePlus
  48. EvaluateProjection
  49. EvaluateRange
  50. EvaluateSlash
  51. EvaluateSlice
  52. EvaluateStar
  53. EvaluateStarStar
  54. EvaluateThis
  55. EvaluateThisAttribute
  56. EvaluateUnaryMinus
  57. EvaluateUnaryPlus
  58. Everything
  59. ExclusiveRange
  60. ExecuteQuery
  61. ExponentMarker
  62. Expression
  63. Filter
  64. Fractional
  65. FuncCall
  66. FuncCallArgs
  67. HasReferenceTo
  68. HexDigit
  69. HexLetter
  70. Identifier
  71. In
  72. InclusiveRange
  73. Integer
  74. Literal
  75. Match
  76. Minus
  77. NewNestedScope
  78. NewRootScope
  79. Not
  80. Null
  81. Number
  82. Object
  83. ObjectAttribute
  84. ObjectAttributes
  85. OperatorCall
  86. Or
  87. Pair
  88. Parent
  89. Parenthesis
  90. PartialCompare
  91. Percent
  92. PipeFuncCall
  93. Plus
  94. Projection
  95. Range
  96. ScientificNotation
  97. Sign
  98. SimpleExpression
  99. SingleEscapeSequence
  100. SingleStringCharacter
  101. Slash
  102. Slice
  103. SourceCharacter
  104. Star
  105. StarStar
  106. String
  107. This
  108. ThisAttribute
  109. TotalCompare
  110. TypeOrder
  111. UnaryMinus
  112. UnaryPlus
  113. UnicodeEscapeSequence
  114. WhiteSpace
  115. coalesce
  116. count
  117. defined
  118. length
  119. order
  120. references
  121. round
  122. select
  1. 1Overview
  2. 2Syntax
    1. 2.1JSON Superset
    2. 2.2White Space
    3. 2.3Comments
    4. 2.4Identifier
    5. 2.5Digits
    6. 2.6Expression
  3. 3Execution
    1. 3.1Overview
    2. 3.2Query context
    3. 3.3Scope
    4. 3.4Expression evaluation
    5. 3.5Query execution
  4. 4Data types
    1. 4.1Null
    2. 4.2Boolean
    3. 4.3Number
    4. 4.4String
    5. 4.5Array
    6. 4.6Object
    7. 4.7Pair
    8. 4.8Range
  5. 5Equality and comparison
    1. 5.1Equality
    2. 5.2Partial comparison
    3. 5.3Total comparison
  6. 6Simple expressions
    1. 6.1This expression
    2. 6.2This attribute expression
    3. 6.3Everything expression
    4. 6.4Parent expression
    5. 6.5Function call expression
  7. 7Compound expressions
    1. 7.1Parenthesis expression
    2. 7.2Attribute access expression
    3. 7.3Element access expression
    4. 7.4Slice expression
    5. 7.5Filter expression
    6. 7.6Projection expression
    7. 7.7Pipe function call expression
  8. 8Operators
    1. 8.1And operator
    2. 8.2Or operator
    3. 8.3Not operator
    4. 8.4Equality operators
    5. 8.5Comparison operators
    6. 8.6In operator
    7. 8.7Match operator
    8. 8.8Asc operator
    9. 8.9Desc operator
    10. 8.10Unary plus operator
    11. 8.11Unary minus operator
    12. 8.12Binary plus operator
    13. 8.13Binary minus operator
    14. 8.14Binary star operator
    15. 8.15Binary slash operator
    16. 8.16Binary percent operator
    17. 8.17Binary double star operator
    18. 8.18Dereference operator
  9. 9Precedence and associativity
  10. 10Functions
    1. 10.1coalesce
    2. 10.2count
    3. 10.3defined
    4. 10.4length
    5. 10.5references
    6. 10.6round
    7. 10.7select
  11. 11Pipe functions
    1. 11.1order
  12. §Index