query-language.md

April 22, 2026 ยท View on GitHub

{% raw %}

Overview

The templjs query engine resolves values from structured input data using:

  • dot notation for nested properties
  • bracket access for array indexes and quoted keys
  • built-in filter functions for transformation
  • strict/default execution options for missing-path handling

Query Resolution Flow

flowchart LR
  A[Template Expression] --> B{Path Type?}
  B -->|dot notation| C[Property Lookup]
  B -->|bracket index| D[Array / Key Access]
  B -->|variable index| E[Resolve Index Var First]
  C --> F[Resolved Value]
  D --> F
  E --> D
  F --> G{Filters?}
  G -->|yes| H[Apply Filter Chain]
  G -->|no| I[Output]
  H --> H2{More Filters?}
  H2 -->|yes| H
  H2 -->|no| I

Filter Pipeline

flowchart LR
  V[Value] --> F1["filter1()"]
  F1 --> F2["filter2(arg)"]
  F2 --> F3["filter3(a, b)"]
  F3 --> Out[Final Output]

  style V fill:#ddf,stroke:#99b
  style Out fill:#dfd,stroke:#9b9

Syntax in templates:

{{ value | upper | truncate(50) | escape }}

Source implementation: Source implementation: src/packages/core/src/query-engine/query-engine.ts

Primary behavior tests: Primary behavior tests: src/packages/core/test/query-engine/query-engine.test.ts

Dot Notation

Use . to walk nested object properties.

{{ user.profile.name }}

With input:

{
  "user": {
    "profile": {
      "name": "Alice"
    }
  }
}

Result:

Alice

Array Access

Use bracket notation for numeric indexes.

{{ items[1] }}
{
  "items": ["zero", "one", "two"]
}

Result:

one

Variable Index Access

Bracket indexes can reference another root variable.

{{ items[idx] }}
{
  "items": ["zero", "one", "two"],
  "idx": 2
}

Result:

two

Chained bracket access also works:

{{ matrix[row][col] }}

Quoted Keys

Use quoted brackets for object keys that are not valid identifiers.

{{ user["display-name"] }}

Missing Paths and Defaults

By default, unresolved paths return undefined.

Programmatic callers can provide defaultValue or enable strict mode through the query engine API.

Implementation reference: Implementation reference: src/packages/core/src/query-engine/types.ts

Filters

Apply filters with pipe syntax.

{{ user.name | upper }}
{{ total | round(2) }}
{{ tags | join(", ") }}

Built-in function catalogs:

Whitespace Control

templjs supports delimiter trim markers to remove formatting-only whitespace around expressions and statements.

Syntax contract:

  • {{- expr }}: trim whitespace immediately to the left of the expression
  • {{ expr -}}: trim whitespace immediately to the right of the expression
  • {{- expr -}}: trim both sides
  • {%- statement %} / {% statement -%} / {%- statement -%}: same trim behavior for statements

Trim markers are optional. Templates without trim markers keep existing whitespace behavior.

Examples by Output Format

JSON:

{
  "items": [
    {%- for item in items -%}
    "{{- item -}}"{%- if !loop.last -%},{%- endif -%}
    {%- endfor -%}
  ]
}

Markdown:

# Release Notes
{%- for entry in entries -%}
- {{- entry.title -}}
{%- endfor -%}

HTML:

<ul>
{%- for item in items -%}
  <li>{{- item.name -}}</li>
{%- endfor -%}
</ul>

Migration Guidance

  • Start by adding trim markers only where output diffs are noisy (blank lines, indentation gaps, trailing separators).
  • Keep control structures readable first; apply {%- ... -%} to the outer loop/condition boundaries before trimming every nested expression.
  • Prefer {{- value -}} when values are punctuation-sensitive (CSV/JSON lists) to avoid accidental spaces around separators.
  • Existing templates remain backward-compatible unless trim markers are added explicitly.

Overloads and Runtime Dispatch

Some filter names are overloaded by runtime category. For example, reverse supports both strings and arrays. The query engine selects the appropriate overload by inspecting the input value and argument shape.

Metadata and overload tests:

Expression-Based Array Filters

Array helpers such as filter and find support simple expression strings.

Examples:

{{ scores | filter("> 90") }}
{{ users | find("active == true") }}
{{ items | where("published") }}

Supported comparison forms include:

  • scalar comparisons such as > 1, <= 10, == "ok"
  • field comparisons such as age >= 18, status == "active"
  • truthy property lookup such as published

Limits and Current Behavior

  • Maximum nesting depth defaults to 100 in query execution options.
  • Malformed bracket syntax may fall back to property lookup behavior rather than throwing.
  • reduce is registered but currently returns the input array unchanged.
  • Datetime formatting/parsing examples can vary by runtime timezone.

{% endraw %}