Applies the same node to each element of a runtime-determined list.
MapNode implements the traverse composition constructor — it takes a
collection from context (resolved via the over path) and runs a single
node against every element. Results are collected as an ordered list
under %{results: [...]}.
Unlike FanOutNode (which runs N different branches known at definition
time), MapNode runs one node N times over a collection discovered at
runtime.
Options
:name— state name (required):over— context key or path to the list (atom()or[atom()]):node— any Node struct, or a bareJido.Actionmodule (auto-wrapped):max_concurrency— limit parallel tasks (default: list length):timeout— per-element timeout in ms (default: 30_000):on_error—:fail_fast(default) or:collect_partial
Example
{:ok, map_node} = MapNode.new(
name: :process,
over: [:extract, :items],
node: DoubleValueAction
)
# Use in a workflow:
nodes: %{
extract: ExtractAction,
process: map_node,
aggregate: AggregateAction
}
# After execution:
# ctx[:process][:results] => [%{value: 2}, %{value: 4}, %{value: 6}]Input Preparation
Each element from the collection is prepared as action params:
- Map elements are merged directly into the flattened context
(
Map.merge(flat_context, element)), so the action receives both the element's keys and the upstream context. - Non-map elements (integers, strings, etc.) are wrapped as
%{item: element}and merged into the context.
Edge Cases
- Empty list: produces
%{results: []}and completes with:ok. - Missing key: if the
overpath doesn't resolve to a list (key is missing, value isnil, or value is not a list), it's treated as an empty list. - Nested path:
over: [:step_a, :sub_key, :items]usesget_in/2to traverse arbitrarily deep context structures.
Summary
Types
@type t() :: %Jido.Composer.Node.MapNode{ max_concurrency: pos_integer() | nil, merge: :ordered_list, name: atom(), node: struct(), on_error: :fail_fast | :collect_partial, over: atom() | [atom()], timeout: pos_integer() }