Union nested arrays with key

Prev Next

For each parent item under dataPath, the union_nested_arrays_with_key adapter looks up the object at containerPath, treats each of its child keys as a category, and emits one row per entry found inside each category's array. Each emitted row records its source category name under keyName, and the parent item's other (non-container) fields are preserved on every row. Use mode + keys to whitelist or denylist which categories to include.

Use this when an API returns per-category arrays nested under a single object (for example, one array per channel under a messages object) and you want a single flat report tagged with the category name. Stacking flatten_values once per category instead produces a cartesian product across categories.

Field Type Default Required Description
dataPath string $.* Yes JSONPath to the iterable of parent items. The transformed list is written back under this path.
containerPath string – Yes JSONPath, relative to each parent item, pointing at the container whose children will be unioned.
keyName string – Yes Property name under which each emitted row records its source category key.
mode string include No One of include or exclude. With include, keys is a whitelist; with exclude, keys is a denylist.
keys array [] No Category keys to include or exclude depending on mode. May be empty for include (unions every category); must be non-empty for exclude.

The combinations of mode and keys resolve as follows:

mode keys Behavior
include [] Default — union every category in the container.
include non-empty Whitelist — union only the listed categories.
exclude non-empty Denylist — union every category except the listed ones.
exclude [] Invalid — rejected by the configuration validator.

Note: Non-list siblings of the container (for example, a scalar or object alongside the per-category arrays) are silently skipped — they do not produce rows and do not raise errors.

Below you can find a before/after example. The original response groups variations per channel:

{
  "data": [
    {
      "time": "2024-01-01T00:00:00Z",
      "messages": {
        "email": [
          {
            "variation_name": "v1",
            "sent": 100
          },
          {
            "variation_name": "v2",
            "sent": 80
          }
        ],
        "push": [
          {
            "variation_name": "v1",
            "sent": 50
          }
        ]
      }
    }
  ]
}

After the adapter runs, each variation becomes its own row with the channel tagged on it:

{
  "data": [
    {
      "time": "2024-01-01T00:00:00Z",
      "channel_type": "email",
      "variation_name": "v1",
      "sent": 100
    },
    {
      "time": "2024-01-01T00:00:00Z",
      "channel_type": "email",
      "variation_name": "v2",
      "sent": 80
    },
    {
      "time": "2024-01-01T00:00:00Z",
      "channel_type": "push",
      "variation_name": "v1",
      "sent": 50
    }
  ]
}

Configuration:

"response": {
  "dataRows": {
    "source": "jsonPath",
    "value": "$.data.*"
  },
  "dataAdapters": {
    "items": [
      {
        "type": "union_nested_arrays_with_key",
        "config": {
          "dataPath": "$.data.*",
          "containerPath": "$.messages",
          "keyName": "channel_type"
        }
      }
    ]
  }
}

To include only specific categories, set mode: "include" and list them in keys:

{
  "type": "union_nested_arrays_with_key",
  "config": {
    "dataPath": "$.data.*",
    "containerPath": "$.messages",
    "mode": "include",
    "keys": [
      "email",
      "push"
    ],
    "keyName": "channel_type"
  }
}

To exclude specific categories instead, set mode: "exclude" and list the keys to skip:

{
  "type": "union_nested_arrays_with_key",
  "config": {
    "dataPath": "$.data.*",
    "containerPath": "$.messages",
    "mode": "exclude",
    "keys": [
      "summary",
      "metadata"
    ],
    "keyName": "channel_type"
  }
}

Below you can find example field definitions that read from the transformed structure:

[
  {
    "id": "time",
    "label": "Time",
    "dataType": "string.time.date_time",
    "value": {
      "source": "jsonPath",
      "value": "$.time"
    }
  },
  {
    "id": "channel_type",
    "label": "Channel type",
    "dataType": "string.text.value",
    "value": {
      "source": "jsonPath",
      "value": "$.channel_type"
    }
  },
  {
    "id": "variation_name",
    "label": "Variation name",
    "dataType": "string.text.value",
    "value": {
      "source": "jsonPath",
      "value": "$.variation_name"
    }
  },
  {
    "id": "sent",
    "label": "Sent",
    "dataType": "int.number.value",
    "value": {
      "source": "jsonPath",
      "value": "$.sent"
    }
  }
]