Share a query across templates
A template's xpath-functions reference loads a separate
named function library.
CEM-QL passes declared scalars or explicitly selected XML nodes to XPath;
ordinary slice bindings and CEMT rules keep the page reactive. Each XPath case
has an adjacent CEM-QL pair with the same input and result. Edit either
independently to compare them.
The templates share the compiled library. Its URL resolves from the declaration document, and each input keeps its own state and focus. Libraries are limited to 32 KiB and 64 functions; library imports are not supported yet. Each call also bounds text to 1 MiB per intermediate or final value and shares a finite work budget across its nested expressions.
For XML, cem-data @projection=xpath exposes the native document
to the library. The reader reuses unchanged XML across renders, retains up to
16 documents per compiled template, and reports invalid input without a partial result.
Each XML source is limited to 32 KiB, 64 levels and 4096 events.
Read the CEM-QL function use cases for string composition, predicates, native node queries, and string import and source metadata.
See also
XPath XML tables and trees · DOM merge and live slices · CEM-QL string functions
CEM-QL function use cases
Compose a label from scalar values
str:concat((text, " 🍒"), "") joins a sequence with an empty
separator. It matches the library's $text || ' 🍒' for
these string inputs, including an empty string and Unicode emoji.
Use a nonempty separator when joining labels or tokens. This native
CEM-QL call needs no XPath library dependency.
Extract a URL segment with a string chain
url.split("/").nth(6) selects the Pokémon ID from
https://pokeapi.co/api/v2/pokemon/1/. Both splitting and
zero-based selection follow Rust conventions. Out-of-range selection
is empty; use ?? "No ID" for a fallback. Try the
interactive string-function samples
or the JSON cell override.
Choose presentation with a predicate
node == "cherry" matches the library's string equality
test. A CEMT rule consumes either boolean result; the lower-priority
fallback handles other text. Use seq:any(items, fn(item) =>
condition) for an existential test over a sequence.
Select native nodes and inspect attributes
Explore the DOM chain samples for fluent navigation, predicates, extraction and immutable sorting.
seq:where(nodes, fn(n) => n.kind == "element" &&
n.name == "item" && n.namespace == "") selects
unqualified item elements. Apply it at each level to match
/r/item. Filtering by kind and namespace avoids treating
comments or namespaced elements as matching data.
The quantity examples expect numeric qty values; a missing
quantity selects low stock. dom:attribute(node, "qty")
selects only the unqualified attribute, like XPath @qty.
Use dom:attribute(node, {namespace: "urn:catalog", name: "qty"})
for a qualified attribute; prefixes and wildcards are not selectors.
Both forms retain the native attribute and its typed values.
num:double converts both the quantity and threshold to the
same numeric type for CEM-QL comparison. dom:text(item)
extracts descendant text, CDATA and
whitespace nodes, excluding comments and processing instructions.
All selected nodes retain their CEM owner and source maps. XML,
JSON, YAML and CSV document syntax is decoded only by CEM-ML import.
For a search at any depth, dom:descendants(node) is the
CEM-QL pair of XPath descendant::node(). It excludes the
starting node and returns children in depth-first source order.
Filter by kind, namespace and name for descendant::item.
The pipeline nodes.dom:descendants() preserves input
order and duplicates. Attributes and reference targets are separate
from children; use dom:attribute and .targets
explicitly. Unsupported node views and denied access raise errors.
Parse a string and keep source metadata
data:parse(source, "xml") returns a native CEM document
directly. The format may also be json, yaml
or csv. JSON uses the same native XML-shaped projection
as XPath json-to-xml, without constructing or reparsing
XML text. An optional CEM-QL options record supports JSON
{duplicates: "use-first", escape: true}, CSV
{header: "present"}, and an explicit
{"base-uri": "https://example.test/source"}.
data:node_key(node) and data:line_number(node)
expose selection provenance. data:base_uri(node) and
data:document_uri(node) read retained metadata;
parsed strings have no document URI. Import is limited to 32 KiB,
64 levels and 4096 values/events. Each parse produces a new owner;
stable source keys, rather than owner identity, track selection.
Use try { data:parse(source, "xml") } catch (code, message) {
message } to recover from malformed input. Query diagnostics
preserve the import error category and parser locations. Import
limits, cancellation and unsupported capabilities remain fatal.
data:read and cem-data, used by the paired
XML examples, keep their cached report contract with
error and root; their strict input profile
is unchanged.
Group, sort and normalize viewer values
seq:group_by(rows, fn(row) => row.name) returns groups
with key and native items. Pass those values
explicitly to templates in place of XPath's ambient group context.
CEM grouping accepts one atomic key and uses CEM identity equality;
it does not implement every XSLT grouping mode.
seq:sorted(rows, fn(row) => row.attributes.value,
"ascending", "number") performs a stable numeric sort with
missing/invalid keys last. Supply a selector for the particular
attribute when a row has several. XPath sorting uses its own typed
comparison rules; the viewer authors the same missing-last policy.
str:trim(text, "xml") and
str:normalize_space(text, "xml") recognize only space,
tab, CR and LF, matching the viewer's XPath whitespace expressions.
Nonbreaking spaces remain text. Omit the profile to retain the
original CEM-QL whitespace behavior. str:replace remains
literal replacement; it is not XPath's regular-expression function.