Skip to main content

Join ExecModule

Overview

JoinModule version 2.0.0 performs deterministic inner, left, right, and full joins over two arrays of workflow objects. It is a local, pure data transformation: it needs no network access or IntegrationAccount, and it creates no external side effects.

Version 2 replaces catalog metadata with zero inputs and malformed output names, and hardens execution around explicit join and collision modes, nested key paths, fail-closed record validation, SQL-like null-key behavior, deterministic ordering, input limits, and a caller-bounded result limit.

Usage

  1. Put both arrays in workflow state.
  2. Configure left_source and right_source as nested paths to those arrays.
  3. Select the key path inside each record and the intended join type.
  4. Add prefixes when both sides can contain the same field names, or choose an explicit collision strategy.
  5. Set max_output_records below the 100,000 hard cap when downstream work needs a tighter bound.
  6. Read canonical rows from items. The configured output_field is also populated as a compatibility alias.

Inputs

NameTypeRequiredLimitDescription
leftarray of objectsYes10,000 recordsArray resolved from left_source.
rightarray of objectsYes10,000 recordsArray resolved from right_source.

The input names describe the logical sides of the join. Their physical locations are selected by the source-path configuration. Every array element must be an object; the module rejects the full execution rather than silently discarding invalid elements.

Outputs

NameTypeConditionDescription
statusstringAlwayssuccess or error.
itemsarraySuccessCanonical joined rows.
joined_dataarraySuccess with default aliasCompatibility alias selected by default output_field.
countintegerSuccessNumber of output rows.
leftCountintegerSuccessValidated left input count.
rightCountintegerSuccessValidated right input count.
matchedPairsintegerSuccessNumber of actual left/right record pairs.
joinTypestringSuccessNormalized join mode.
errorCodestringErrorStable failure code.
errorstringErrorSafe, actionable failure message.

When output_field is changed, the same result list is exposed under both items and that configured alias. Reserved status and diagnostic names cannot be used as aliases.

IntegrationAccount Requirements

None. JoinModule is an in-memory transformation and must not receive credentials. Joined data can be confidential, so downstream modules should preserve its classification and apply normal workflow ACL rules.

Configuration

FieldTypeRequiredDefaultDescription
left_sourcetextYesNested path to the left array, such as payload.users.
right_sourcetextYesNested path to the right array, such as payload.orders.
left_keytextYesidNested key path inside left records.
right_keytextYesidNested key path inside right records.
join_typeselectYesinnerinner, left, right, or full.
collision_strategyselectYesright_winsright_wins, left_wins, or error.
prefix_lefttextNoemptyPrefix applied to every left output field.
prefix_righttextNoemptyPrefix applied to every right output field.
output_fieldtextNojoined_dataCompatibility alias for canonical items.
max_output_recordsintegerNo10000Execution-specific output cap, from 1 through 100,000.

Paths support object segments and bounded zero-based array indexes, for example payload.groups[0].users or identity.id. Prefixes and aliases accept only bounded alphanumeric, underscore, dash, and dot characters.

Operations

JoinModule exposes one transformation with four modes:

ModeMatched rowsUnmatched left rowsUnmatched right rows
innerIncludedExcludedExcluded
leftIncludedIncludedExcluded
rightIncludedExcludedIncluded
fullIncludedIncludedIncluded

Duplicate keys produce one row for every matching pair. Null or missing keys never match; an outer join retains those records on their original side. Left input order is preserved, right-side matches preserve right input order, and unmatched right rows are appended in right input order.

Errors and Failure Modes

Error codeCauseRecovery
VALIDATION_ERRORMissing/unsafe path, unsupported join or collision mode, reserved alias, non-object record, or invalid limit.Correct the named configuration or input.
INPUT_LIMIT_EXCEEDEDEither source contains more than 10,000 records.Partition the arrays or aggregate before joining.
RESULT_LIMIT_EXCEEDEDThe join would exceed max_output_records.Tighten upstream filtering, use more selective keys, or raise the bound deliberately.
FIELD_COLLISIONcollision_strategy: error encountered the same output field on both sides.Add distinct prefixes or choose an explicit winning side.
EXECUTION_ERRORAn unexpected local runtime failure occurred.Preserve the inputs, inspect sanitized runtime diagnostics, and retry only after fixing the defect.

Failures clear partial rows and return only the safe error contract. Workflow data is not copied into logs or failure messages.

Example

Configuration:

{
"left_source": "payload.users",
"right_source": "payload.orders",
"left_key": "id",
"right_key": "user_id",
"join_type": "inner",
"prefix_left": "user_",
"prefix_right": "order_",
"collision_strategy": "error",
"max_output_records": 1000
}

Input:

{
"payload": {
"users": [
{"id": "u-1", "name": "Ada"},
{"id": "u-2", "name": "Lin"}
],
"orders": [
{"user_id": "u-1", "order_id": "o-7", "total": 42.5},
{"user_id": "u-1", "order_id": "o-8", "total": 19.0}
]
}
}

Expected result:

{
"status": "success",
"items": [
{
"user_id": "u-1",
"user_name": "Ada",
"order_user_id": "u-1",
"order_order_id": "o-7",
"order_total": 42.5
},
{
"user_id": "u-1",
"user_name": "Ada",
"order_user_id": "u-1",
"order_order_id": "o-8",
"order_total": 19.0
}
],
"count": 2,
"leftCount": 2,
"rightCount": 2,
"matchedPairs": 2,
"joinType": "inner"
}

The default joined_data alias contains the same rows as items.

Notes

  • Pagination: none. Inputs are already-materialized workflow arrays; partition large datasets before this module.
  • Limits: at most 10,000 records per side, nested paths up to 256 characters, prefixes up to 64 characters, aliases up to 128 characters, and at most 100,000 output rows.
  • Idempotency: natural and deterministic for the same ordered inputs and configuration.
  • Rate limits: none; the module does not call a provider.
  • API constraints: key equality uses Java object equality after workflow deserialization. A numeric 7 and string "7" are different keys.
  • Collision behavior: right_wins preserves legacy overwrite behavior; left_wins retains the left field; error fails the complete execution. Prefixes are the clearest contract.
  • Destructive behavior: none. No records are mutated, written externally, or deleted.
  • Observability: events report only status, safe error code, and result count, never joined payloads.
  • Testing boundary: focused tests cover join modes, one-to-many order, nested paths, null keys, collision policies, malformed records, result bounds, output compatibility, and metadata serialization. No external service is involved.
  • Runtime boundary: merged source, production documentation, public launch content, and deployed /v1/modules/metadata are separate states.

See the ExecModule catalog for neighboring Map, Merge, Filter, Sort, and Deduplicate transformations.