GROQ Specification

repository·main·Indexed 19 days ago

https://github.com/sanity-io/groq

The official specification for GROQ (Graph-Relational Object Queries), an open standard query language for filtering, joining, and projecting JSON documents. This documentation covers the language syntax, execution model, traversal logic, and versioning scheme (GROQ-X.revisionY).

Tokens
13K
Snippets
72
Records
92
Agent score
64%

What's inside GROQ

  1. What is GROQ?

    main

    GROQ (Graph-Relational Object Queries) is a declarative query language for collections of schema-less JSON documents. It is designed to achieve three primary goals:

    1. Expressive filtering: Selecting specific documents based on complex criteria.
    2. Joining: Combining information from multiple documents into a single response.
    3. Shaping: Structuring the output response to match the exact requirements of a client application, ensuring only necessary fields are returned.
  2. Use the Portable Text Extension

    main

    The Portable Text extension provides tools to work with objects following the portable text spec. Functions are grouped under the pt namespace, except for the constructor which is global.

    • pt type: Represents a Portable Text object or an array of Portable Text objects.
    • global::pt(): A constructor that validates if an object or an array of objects is a valid Portable Text Block. Returns the value if valid, otherwise null.
    • pt::text(): Converts a Portable Text value into a string. If the value contains multiple blocks, they are joined with double newlines (\n\n).
    // Example usage (conceptual)
    // pt::text(pt_value)
    // global::pt(object_or_array)
  3. Call built-in and custom functions

    main

    GROQ supports function calls using the syntax FuncNamespace::FuncIdentifier(args).

    • Built-in functions: Accessed via the global namespace or specific namespaces (e.g., global::lower()).
    • Custom functions: Can be defined to extend or override built-in behavior. When a custom function is called, it executes within a new scope where the provided arguments are mapped to the function's parameters.
    *{"score": round(score, 2)}
               ~~~~~~~~~~~~~~~
    
    *{"description": global::lower(description)}
                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~
  4. Understand the structure of a GROQ query

    main

    A standard GROQ query follows a specific pattern to select, filter, and shape data:

    1. Selection (*): Queries typically start with an asterisk, representing all documents in the dataset.
    2. Filtering ({filter}): Brackets following the asterisk contain filters. These filters use terms, operators, and functions to narrow down the document set.
    3. Projection ({projection}): Curly braces at the end of the query define the shape of the returned data, specifying exactly which fields should be included in the output.
    *[id > 2]{name}
  5. The Match operator

    main

    The match operator is used for pattern-based text searching. It returns true when any of the provided patterns match all of the tokens extracted from the target expression.

    • The left-hand side can be a string or an array of strings.
    • The right-hand side can be a string or an array of strings (patterns).
    • If the right-hand side is an array containing non-string values, the operator returns false.
    • If the patterns list is empty, it returns false.
    // Pattern matching
    *[_title match 'hello*']
    *[_tags match ['tech', 'web']]
  6. Use Array types in GROQ

    main

    An array is an ordered collection of values (e.g., [1, 2, 3]) that can contain mixed types.

    Spread Operator: You can use the ... prefix on an element within an array literal to flatten it into the parent array.

    [1, 2, ...[3, 4], 5] // results in [1, 2, 3, 4, 5]
  7. How custom functions work in GROQ

    main
    Custom functions are a mechanism for modularity in GROQ. Instead of repeating complex query logic, you define a named, namespaced function at the top of your query string. This function is then available to be called later in the same query. This pattern promotes DRY (Don't Repeat Yourself) principles within complex GROQ expressions.
  8. How different traversal combinations work

    main

    GROQ combines multiple traversals using four distinct logic patterns. Understanding these helps predict how your query results are shaped:

    1. Joined (EvaluateTraversalJoin): Executes the first traversal, then applies the second traversal to the result.
      • Example: .user.name (Get user, then get name from that user).
    2. Mapped (EvaluateTraversalMap): Executes the first traversal (which must return an array), then applies the second traversal to each element of that array.
      • Example: [_type == "user"].id (Filter users, then get the id of every user found).
    3. Flat-mapped (EvaluateTraversalFlatMap): Executes the first traversal, applies the second to each element, and then flattens the resulting arrays into a single array.
      • Example: [_type == "user"].names[] (Filter users, get their names arrays, and flatten them into one list of names).
    4. Inner mapped (EvaluateTraversalInnerMap): Applies the first traversal to each element of an array, then applies the second traversal to the entire resulting array.
      • Example: {name, type}[type == "admin"] (Project name/type for all elements, then filter that projected list for admins).
  9. Understand the GROQ specification versioning

    main

    The GROQ specification follows a specific versioning scheme: GROQ-X.revisionY, where X is the major version and Y is the revision number.

    • Major versions (X): Used to introduce breaking changes.
    • Revisions (Y): Include minor clarifications or new functionality. Revisions are always backwards compatible within the same major version.

    Example: The first version is GROQ-1.revision0.

  10. Understand GROQ traversal execution

    main

    GROQ uses a terse syntax for traversing deeply nested JSON objects and arrays, similar to JavaScript's map, filter, and flatMap.

    Key principles of GROQ traversals:

    • Static Semantics: The interpretation of a traversal is determined statically; the runtime value of an expression does not change how the traversal is interpreted.
    • Traversal Types: Traversals are categorized based on whether they work on arrays and what they return (Plain, Array, Array Source, or Array Target).
    • Automatic Mapping: If you place a plain traversal (like .name) immediately after an array traversal (like [_type == 'user']), GROQ automatically executes that plain traversal for each element in the array.

    Common traversal operators include:

    • AttributeAccess (e.g., user.name)
    • Dereference (e.g., image->)
    • ElementAccess (e.g., users[0])
    • Slice (e.g., users[0...5])
    • Filter (e.g., users[type == "admin"])
    • ArrayPostfix (e.g., users[])
    • Projection (e.g., user{name})
    // The following GROQ:
    *[_type == "user"]._id
    
    // Is equivalent to this JavaScript:
    // data.filter(u => u._type == "user").map(u => u._id)
  11. Use the 'parent' expression (^) to traverse up scopes

    main

    The ^ operator allows you to access the value of an upper scope. The number of carets determines how many levels you move up. ^. is used to access a specific attribute on a parent scope.

    // Find all people who have a cool friend
    *[_type == "person" && *[_id == ^.friend._ref][0].isCool]
                                    ~
  12. Use JSON values as GROQ expressions

    main

    GROQ is a superset of JSON. This means any valid JSON value is a valid GROQ expression that simply returns that value. You can use strings, arrays, objects, numbers, booleans, and nulls directly in your queries.

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