Skip to content

Multi-sized results

Some rules can produce more than one output value per input record. This fans the execution out: every downstream rule in the chain runs once per result, and the source row produces multiple CDM rows.

The [] and [N] suffixes

Suffix Meaning
[] Unbounded — produce as many output rows as the rule returns matches.
[N] Exactly N results required. If fewer are available, the row is skipped and an error is logged.
UsagiMap("file.csv")[]    # variable number of results
UsagiMap("file.csv")[3]   # exactly 3 results required

LoopOver controls the cartesian product

When several fields produce multi-sized results, the engine needs to know how to combine them. The LoopOver table rule declares this at the CDM-table level:

  • Fields in the same array are zipped — the i-th result of one is paired with the i-th result of the others.
  • Fields in different arrays form a cartesian product.
LoopOver([:field_one])
LoopOver([:field_one, :field_two])
LoopOver([:field_one, :field_two], [:cartesian_field_three])

Any mapping with a multi-sized result must declare LoopOver, even if only one field is multi-sized.

Forking and converging execution paths

A multi-sized rule forks the execution path. Successive multi-sized rules multiply forks, so the engine may end up tracking many parallel (var1, var2, …) tuples per source row. Subsequent aggregations such as Unique can be used as a convergence point: every fork's value is collected, deduplicated, and the chain refurnishes one fork per unique value.

Don't put Unique inside a conditional branch

If some forks reach Unique and others don't, the post-Unique state is inconsistent — some forks see the converged value, others the original variables. This is rarely what you want.