MapNode
A MapNode runs the same subflow once for each item in an input collection and combines selected outputs from those executions into declared MapNode outputs. For each item in the collection, the runtime:
1. Extracts the item from the collection supplied to the MapNode.
2. Supplies that item to the corresponding input of the subflow.
3. Executes the subflow from its StartNode until it reaches an EndNode.
4. Reads each subflow output named in reducers.
After all iterations complete, each reducer combines the corresponding subflow outputs. The resulting values become the outputs of the MapNode. For example, a MapNode can receive an array of order IDs, execute an order-lookup subflow once for each ID, and append the returned order statuses into a single array.
The collection item type must be compatible with the corresponding subflow input. If the MapNode receives an array of strings, each item is a string and the subflow input must accept a string.
MapNode input and output names describe how the collection boundary corresponds to the subflow contract. An input named iterated_order_id supplies each collection item to the subflow input named order_id, while an output named collected_order_status contains the reduced values from the subflow output named order_status. The suffixes are case-sensitive and must match the corresponding subflow names. Reducer entries use the unprefixed subflow output name, such as order_status.
MapNode Fields
| Field | Requirement | Purpose |
|---|---|---|
component_type | Required | Identifies the component as a MapNode. |
id | Required | Uniquely identifies the MapNode within its component scope. |
name | Required | Provides a human-readable name for the node. |
subflow | Required | Defines or references the Flow executed once for each collection item. |
inputs | Optional | Declares the collections processed by the node. Each collection's item specification must be compatible with the corresponding subflow input. |
outputs | Optional | Declares the values produced after the configured reducers combine the subflow outputs. |
branches | Optional | Declares the control branches that can continue after the MapNode completes. |
reducers | Optional | Maps subflow output names to the reduction operations that produce the MapNode outputs. A subflow output without an explicit reducer uses append. |
Reducer Behavior
| Reducer | Result |
|---|---|
append | Collect each order status into an array. |
sum | Add compatible numeric child values. |
average | Compute the mean of compatible numeric values. |
min | Keep the smallest compatible value. |
max | Keep the largest compatible value. |
A MapNode can apply different reducers to multiple subflow outputs. For example, it can apply append to an order_status output and sum to a numeric order_amount output. Each reducer independently combines the values produced for its named subflow output. A reducer must name an output the child flow actually publishes, and its result must satisfy the MapNode output contract. For strings or structured results that must be preserved individually, append is usually the natural choice.
MapNode Example
An order-support Flow receives an array of order IDs and returns the status of each order. An existing order-lookup-flow accepts one order_id and returns one order_status:
| Direction | Name | Type |
|---|---|---|
| Input | order_id | string |
| Output | order_status | string |
The MapNode executes this subflow once for each value in the order_ids array. The enclosing Flow interacts with the repeated work through the MapNode collection contracts.

Description of the illustration MapNode Repeated Subflow Execution and Reduction
Control Flow
The enclosing Flow treats the MapNode as one graph component. Each collection item causes a separate execution of the subflow's internal control graph.
Data Flow
The fields describe the collection boundary:
inputsdeclaresiterated_order_id, the array supplied by the enclosing Flow. Itsitemsdeclaration matches theorder_idinput of the subflow.outputsdeclarescollected_order_status, the array produced after the subflow executions complete. Itsitemsdeclaration matches theorder_statusoutput of the subflow.branchesdeclares thenextbranch that can continue after theMapNodecompletes.subflowidentifies the Flow executed for each item.reducersappliesappendto theorder_statusvalue returned by each subflow execution.
Referenced Subflow
The subflow reference resolves to the Flow whose id is order-lookup-flow. The subflow processes one order at a time:
{
"component_type": "Flow",
"id": "order-lookup-flow",
"name": "order_lookup_flow",
"inputs": [
{
"title": "order_id",
"type": "string"
}
],
"outputs": [
{
"title": "order_status",
"type": "string"
}
],
"start_node": {
"$component_ref": "order-lookup-start"
},
"nodes": [
{"$component_ref": "order-lookup-start"},
{"$component_ref": "lookup-order"},
{"$component_ref": "order-lookup-end"}
],
"control_flow_connections": [
{
"component_type": "ControlFlowEdge",
"id": "order-lookup-start-to-tool",
"name": "order_lookup_start_to_tool",
"from_node": {
"$component_ref": "order-lookup-start"
},
"from_branch": null,
"to_node": {
"$component_ref": "lookup-order"
}
},
{
"component_type": "ControlFlowEdge",
"id": "order-lookup-tool-to-end",
"name": "order_lookup_tool_to_end",
"from_node": {
"$component_ref": "lookup-order"
},
"from_branch": null,
"to_node": {
"$component_ref": "order-lookup-end"
}
}
],
"data_flow_connections": [
{
"component_type": "DataFlowEdge",
"id": "order-id-to-lookup-tool",
"name": "order_id_to_lookup_tool",
"source_node": {
"$component_ref": "order-lookup-start"
},
"source_output": "order_id",
"destination_node": {
"$component_ref": "lookup-order"
},
"destination_input": "order_id"
},
{
"component_type": "DataFlowEdge",
"id": "order-status-to-lookup-end",
"name": "order_status_to_lookup_end",
"source_node": {
"$component_ref": "lookup-order"
},
"source_output": "order_status",
"destination_node": {
"$component_ref": "order-lookup-end"
},
"destination_input": "order_status"
}
],
"$referenced_components": {
"order-lookup-start": {
"component_type": "StartNode",
"id": "order-lookup-start",
"name": "order_lookup_start",
"inputs": [
{
"title": "order_id",
"type": "string"
}
],
"outputs": [
{
"title": "order_id",
"type": "string"
}
],
"branches": ["next"]
},
"lookup-order": {
"component_type": "ToolNode",
"id": "lookup-order",
"name": "lookup_order",
"inputs": [
{
"title": "order_id",
"type": "string"
}
],
"outputs": [
{
"title": "order_status",
"type": "string"
}
],
"branches": ["next"],
"tool": {
"$component_ref": "lookup-orders-tool"
}
},
"order-lookup-end": {
"component_type": "EndNode",
"id": "order-lookup-end",
"name": "order_lookup_end",
"inputs": [
{
"title": "order_status",
"type": "string"
}
],
"outputs": [
{
"title": "order_status",
"type": "string"
}
],
"branches": [],
"branch_name": "next"
},
"lookup-orders-tool": {
"component_type": "BuiltinTool",
"id": "lookup-orders-tool",
"name": "lookup_orders",
"executor_name": "Oracle In-DB Agent Executor",
"tool_type": "plsql_exec",
"configuration": {
"schema_name": "APP_SCHEMA",
"package_name": "ORDER_TOOLS",
"procedure_name": "LOOKUP_ORDER"
}
}
}
}
The subflow contract contains scalar values because each execution handles one order. The MapNode contract contains arrays because it receives all order IDs and returns the combined statuses.
During execution:
- A data-flow edge transfers
StartNode.order_idstoMapNode.iterated_order_id. - The runtime takes one item from
iterated_order_idand supplies it asorder-lookup-flow.order_id. - The subflow begins at
order-lookup-start, invokes the order-lookup tool, and reachesorder-lookup-endwith oneorder_status. - The runtime repeats the subflow execution for the remaining order IDs.
- The
appendreducer collects theorder_statusvalue from each execution intoMapNode.collected_order_status. - A data-flow edge transfers the collected array to
EndNode.order_statuses, and thenextbranch identifies the possible control-flow continuation after theMapNodecompletes.
The subflow does not receive the complete collection and does not combine the results. It defines only the operation for one order. The MapNode owns the collection iteration and reduction behavior.
Complete Runnable Agent Spec
The following example accepts an array of three order IDs. The MapNode executes order-lookup-flow once for each ID and uses the append reducer to return an array of order statuses. This example does not require an LLM configuration, credential, or network ACL. The application schema must be able to create the example package and execute DBMS_AI_AGENT.
Create the Example Tool
Connect as the application schema and create the following package:
CREATE OR REPLACE PACKAGE ORDER_TOOLS AS
PROCEDURE LOOKUP_ORDER (
ORDER_ID IN VARCHAR2,
ORDER_STATUS OUT VARCHAR2
);
END ORDER_TOOLS;
/
CREATE OR REPLACE PACKAGE BODY ORDER_TOOLS AS
PROCEDURE LOOKUP_ORDER (
ORDER_ID IN VARCHAR2,
ORDER_STATUS OUT VARCHAR2
) IS
BEGIN
CASE ORDER_ID
WHEN '7812' THEN
ORDER_STATUS := 'SHIPPED';
WHEN '7930' THEN
ORDER_STATUS := 'PROCESSING';
WHEN '8041' THEN
ORDER_STATUS := 'DELIVERED';
ELSE
ORDER_STATUS := 'UNKNOWN';
END CASE;
END LOOKUP_ORDER;
END ORDER_TOOLS;
/
Execute the Complete Agent Spec
SET SERVEROUTPUT ON SIZE UNLIMITED
SET DEFINE OFF
DECLARE
L_AGENT_SPEC CLOB := q'~
{
"component_type": "Flow",
"agentspec_version": "25.4.1",
"id": "map-node-example-flow",
"name": "map_node_example_flow",
"inputs": [
{
"title": "order_ids",
"type": "array",
"items": {
"title": "order_id",
"type": "string"
}
}
],
"outputs": [
{
"title": "order_statuses",
"type": "array",
"items": {
"title": "order_status",
"type": "string"
}
}
],
"start_node": {
"$component_ref": "start"
},
"nodes": [
{
"$component_ref": "start"
},
{
"$component_ref": "lookup-orders"
},
{
"$component_ref": "end"
}
],
"control_flow_connections": [
{
"component_type": "ControlFlowEdge",
"id": "start-to-map",
"name": "start_to_map",
"from_node": {
"$component_ref": "start"
},
"to_node": {
"$component_ref": "lookup-orders"
}
},
{
"component_type": "ControlFlowEdge",
"id": "map-to-end",
"name": "map_to_end",
"from_node": {
"$component_ref": "lookup-orders"
},
"from_branch": "next",
"to_node": {
"$component_ref": "end"
}
}
],
"data_flow_connections": [
{
"component_type": "DataFlowEdge",
"id": "order-ids-to-map",
"name": "order_ids_to_map",
"source_node": {
"$component_ref": "start"
},
"source_output": "order_ids",
"destination_node": {
"$component_ref": "lookup-orders"
},
"destination_input": "iterated_order_id"
},
{
"component_type": "DataFlowEdge",
"id": "collected-status-to-end",
"name": "collected_status_to_end",
"source_node": {
"$component_ref": "lookup-orders"
},
"source_output": "collected_order_status",
"destination_node": {
"$component_ref": "end"
},
"destination_input": "order_statuses"
}
],
"$referenced_components": {
"start": {
"component_type": "StartNode",
"id": "start",
"name": "start",
"inputs": [
{
"title": "order_ids",
"type": "array",
"items": {
"title": "order_id",
"type": "string"
}
}
],
"outputs": [
{
"title": "order_ids",
"type": "array",
"items": {
"title": "order_id",
"type": "string"
}
}
],
"branches": [
"next"
]
},
"lookup-orders": {
"component_type": "MapNode",
"id": "lookup-orders",
"name": "lookup_orders",
"inputs": [
{
"title": "iterated_order_id",
"type": "array",
"items": {
"title": "order_id",
"type": "string"
}
}
],
"outputs": [
{
"title": "collected_order_status",
"type": "array",
"items": {
"title": "order_status",
"type": "string"
}
}
],
"branches": [
"next"
],
"subflow": {
"$component_ref": "order-lookup-flow"
},
"reducers": {
"order_status": "append"
}
},
"end": {
"component_type": "EndNode",
"id": "end",
"name": "end",
"inputs": [
{
"title": "order_statuses",
"type": "array",
"items": {
"title": "order_status",
"type": "string"
}
}
],
"outputs": [
{
"title": "order_statuses",
"type": "array",
"items": {
"title": "order_status",
"type": "string"
}
}
],
"branches": [],
"branch_name": "next"
},
"order-lookup-flow": {
"component_type": "Flow",
"id": "order-lookup-flow",
"name": "order_lookup_flow",
"inputs": [
{
"title": "order_id",
"type": "string"
}
],
"outputs": [
{
"title": "order_status",
"type": "string"
}
],
"start_node": {
"$component_ref": "order-lookup-start"
},
"nodes": [
{
"$component_ref": "order-lookup-start"
},
{
"$component_ref": "lookup-order"
},
{
"$component_ref": "order-lookup-end"
}
],
"control_flow_connections": [
{
"component_type": "ControlFlowEdge",
"id": "order-lookup-start-to-tool",
"name": "order_lookup_start_to_tool",
"from_node": {
"$component_ref": "order-lookup-start"
},
"to_node": {
"$component_ref": "lookup-order"
}
},
{
"component_type": "ControlFlowEdge",
"id": "order-lookup-tool-to-end",
"name": "order_lookup_tool_to_end",
"from_node": {
"$component_ref": "lookup-order"
},
"to_node": {
"$component_ref": "order-lookup-end"
}
}
],
"data_flow_connections": [
{
"component_type": "DataFlowEdge",
"id": "order-id-to-lookup-tool",
"name": "order_id_to_lookup_tool",
"source_node": {
"$component_ref": "order-lookup-start"
},
"source_output": "order_id",
"destination_node": {
"$component_ref": "lookup-order"
},
"destination_input": "order_id"
},
{
"component_type": "DataFlowEdge",
"id": "order-status-to-lookup-end",
"name": "order_status_to_lookup_end",
"source_node": {
"$component_ref": "lookup-order"
},
"source_output": "order_status",
"destination_node": {
"$component_ref": "order-lookup-end"
},
"destination_input": "order_status"
}
],
"$referenced_components": {
"order-lookup-start": {
"component_type": "StartNode",
"id": "order-lookup-start",
"name": "order_lookup_start",
"inputs": [
{
"title": "order_id",
"type": "string"
}
],
"outputs": [
{
"title": "order_id",
"type": "string"
}
],
"branches": [
"next"
]
},
"lookup-order": {
"component_type": "ToolNode",
"id": "lookup-order",
"name": "lookup_order",
"inputs": [
{
"title": "order_id",
"type": "string"
}
],
"outputs": [
{
"title": "order_status",
"type": "string"
}
],
"branches": [
"next"
],
"tool": {
"$component_ref": "lookup-orders-tool"
}
},
"order-lookup-end": {
"component_type": "EndNode",
"id": "order-lookup-end",
"name": "order_lookup_end",
"inputs": [
{
"title": "order_status",
"type": "string"
}
],
"outputs": [
{
"title": "order_status",
"type": "string"
}
],
"branches": [],
"branch_name": "next"
},
"lookup-orders-tool": {
"component_type": "BuiltinTool",
"id": "lookup-orders-tool",
"name": "lookup_orders",
"description": "Return the current status of an order.",
"inputs": [
{
"title": "order_id",
"type": "string"
}
],
"outputs": [
{
"title": "order_status",
"type": "string"
}
],
"requires_confirmation": false,
"executor_name": "Oracle In-DB Agent Executor",
"tool_version": "1.0",
"tool_type": "plsql_exec",
"configuration": {
"package_name": "ORDER_TOOLS",
"procedure_name": "LOOKUP_ORDER"
}
}
}
}
}
}
~';
L_INPUTS CLOB := TO_CLOB (
'[{"name":"order_ids","value":["7812","7930","8041"]}]'
);
L_OUTPUTS CLOB;
BEGIN
L_OUTPUTS := DBMS_AI_AGENT.EXECUTE_IMMEDIATE (
AGENT_SPEC => L_AGENT_SPEC,
INPUTS => L_INPUTS
);
DBMS_OUTPUT.PUT_LINE (
DBMS_LOB.SUBSTR (L_OUTPUTS, 32767, 1)
);
END;
/
The returned JSON has the following form:
{
"order_statuses": [
"SHIPPED",
"PROCESSING",
"DELIVERED"
]
}
In this execution:
- The enclosing
StartNodepublishes theorder_idsarray. - The outer data-flow edge supplies the array to
MapNode.iterated_order_id. - The MapNode extracts one ID and maps it to the
order_idinput of
order-lookup-flow. - The subflow invokes
ORDER_TOOLS.LOOKUP_ORDERand returns one
order_status. - The MapNode repeats the subflow for the remaining IDs.
- The
appendreducer combines the per-itemorder_statusvalues into
MapNode.collected_order_status. - The enclosing Flow maps the collected array to
EndNode.order_statuses
and returns it through the Flow output contract.