Skip to main content
Version: Canary 🚧

Query API

Traversal and Aggregation

The query module provides a functional API to filter, search, and aggregate data from child components. It is primarily used within the measure phase of a component to extract information from the children array.

Search & Traversal​

These functions help you navigate the component tree and locate specific elements.

For most traversal functions, there are two variants:

  1. Standard (e.g., select): Returns frames (the component envelopes containing metadata like path and kind).
  2. Signals (e.g., select-signals): Returns the user data directly (the signal payload), automatically unwrapping the frame and filtering out empty signals.

select / select-signals​

Filters the list of immediate children, returning only those matching a specific component kind.

query.select(children, kind)
query.select-signals(children, kind)
ParameterTypeDefaultDescription
childrenarray<frame>RequiredThe list of frames to search.
kindstrRequiredThe component kind to match (e.g., "task").

Returns:

  • select: array<frame>
  • select-signals: array<any>

Example:

// Get frames (if you need path/key)
let task-frames = query.select(children, "task")

// Get data directly (if you just need values)
let task-data = query.select-signals(children, "task")

find / find-signal​

Finds the first immediate child of a specific kind. Returns none (or the default) if not found.

query.find(children, kind, default: none)
query.find-signal(children, kind, default: none)
ParameterTypeDefaultDescription
childrenarray<frame>RequiredThe list of frames to search.
kindstrRequiredThe component kind to find.
defaultanynoneThe value to return if no matching child is found.

Returns:

  • find: frame | none
  • find-signal: any | none

Example:

// Get the configuration object directly
let config = query.find-signal(children, "config", default: (:))

collect / collect-signals​

Recursively traverses the tree to find all descendants of a specific kind (or all nodes if kind is none).

Traversal Logic: Since child components are passed via the signal, these functions inspect the signal of each node to find the next generation of children. They support three patterns:

  1. Wrapper: The signal is a frame (single child).
  2. List: The signal is an array (list of children).
  3. Container: The signal is a dictionary with a children key.
query.collect(children, kind: none, depth: 10)
query.collect-signals(children, kind: none, depth: 10)
ParameterTypeDefaultDescription
childrenarray<frame>RequiredThe root list of frames to start searching from.
kindstrnoneThe component kind to filter by. If none, collects all descendants.
depthint10The maximum recursion depth to traverse.

Returns:

  • collect: array<frame>
  • collect-signals: array<any>
"Ghost" Nodes

These functions automatically filter out non-frame data found in signals (e.g., strings or config objects) during traversal to ensure they only recurse into valid component frames.

Example:

// Flatten the tree and get all ingredient data
let all-ingredients = query.collect-signals(children, "ingredient")

where / where-signals​

Filters children using a custom predicate function.

query.where(children, predicate)
query.where-signals(children, predicate)
ParameterTypeDefaultDescription
childrenarray<frame>RequiredThe list of frames to filter.
predicatefunctionRequiredA function frame => bool that returns true for items to keep.

Returns:

  • where: array<frame>
  • where-signals: array<any>

Example:

// Find all components with a cost signal greater than 100
let expensive = query.where-signals(children, c => c.signal.cost > 100)

Data Extraction & Aggregation​

These functions operate on the signal data carried by the components, helping you calculate totals or extract lists of values.

pluck​

Extracts a specific field from the signal of each child, returning an array of values.

query.pluck(children, key, default: none)
ParameterTypeDefaultDescription
childrenarray<frame>RequiredThe list of frames to process.
keystrRequiredThe key to look up in each child's signal.
defaultanynoneValue to use if the key is missing in a signal.

sum-signals​

Sums up a specific numeric field from the signals of all children. This is a shorthand for pluck(..).sum().

query.sum-signals(children, key, default: 0)
ParameterTypeDefaultDescription
childrenarray<frame>RequiredThe list of frames to process.
keystrRequiredThe key containing the number in child.signal.
defaultint or float0Value to use if the key is missing or invalid.

group-by / group-signals​

Groups children into a dictionary based on a value in their signal.

query.group-by(children, key)
query.group-signals(children, key)
ParameterTypeDefaultDescription
childrenarray<frame>RequiredThe list of frames to group.
keystrRequiredThe signal key to use as the grouping category.

Returns:

  • group-by: dictionary<str, array<frame>>
  • group-signals: dictionary<str, array<any>>

Example:

// Group ingredient data by category
let groups = query.group-signals(ingredients, "category")
// -> (fruit: ((name: "Apple"), ...), veg: (...))

fold​

Aggregates data from a list of children using a custom reducer function. This operates directly on the signal of the children.

query.fold(children, fn, base)
ParameterTypeDefaultDescription
childrenarray<frame>RequiredThe list of frames to process.
fnfunctionRequiredReducer (acc, signal) => new_acc.
baseanyRequiredThe initial value for the accumulator.

Example:

let total-value = query.fold(children, (acc, sig) => acc + sig.value, 0)