Skip to main content
Unwind stage showing array decomposition into separate documents
The Unwind stage decomposes array fields into separate documents, producing one output document per array element. This is the retriever pipeline equivalent of MongoDB’s $unwind, Snowflake’s LATERAL FLATTEN, and Spark’s explode().
Stage Category: APPLY (Expands documents)Transformation: N documents → M documents (where M ≥ N, one per array element)

When to Use

Use CaseDescription
Tag expansionDecompose multi-tag documents for per-tag analysis
Segment decompositionFlatten video/audio segments into individual results
Author attributionExpand author lists for per-author scoring
Chunk flatteningConvert grouped chunks back into individual documents
Category expansionExpand multi-category items for faceted search

When NOT to Use

ScenarioRecommended Alternative
Filtering documentsattribute_filter or llm_filter
Restructuring without expansionjson_transform
Sorting documentssort_attribute or sort_relevance
Grouping documentsgroup_by (inverse operation)

Parameters

ParameterTypeDefaultDescription
fieldstringrequiredDot-notation path to the array field to unwind
preserve_null_and_emptybooleanfalseKeep documents where array is null/missing/empty
include_array_indexstringnullField name to store the element’s array index
output_fieldstringnullPlace unwound element in this field instead of replacing

Configuration Examples

{
  "stage_type": "apply",
  "stage_id": "unwind",
  "parameters": {
    "field": "metadata.tags"
  }
}

How It Works

  1. For each input document, extracts the array value at the specified field path
  2. If the value is an array with K elements, produces K output documents
  3. Each output document preserves all original fields, with the array field replaced by a single element
  4. Documents with null/empty arrays are either dropped or preserved based on preserve_null_and_empty
  5. Non-array values are passed through unchanged
Use include_array_index when you need to reconstruct the original order later, such as when reassembling video segments after per-segment scoring.

Performance

MetricValue
Latency< 5ms
MemoryProportional to output count
CostFree
ComplexityO(total array elements)

Common Pipeline Patterns

Per-Tag Scoring

[
  {
    "stage_type": "filter",
    "stage_id": "feature_search",
    "parameters": {
      "feature_uris": [{"input": {"text": "{{INPUT.query}}"}, "uri": "mixpeek://text_extractor@v1/embedding"}],
      "limit": 50
    }
  },
  {
    "stage_type": "apply",
    "stage_id": "unwind",
    "parameters": {
      "field": "metadata.tags",
      "include_array_index": "tag_index"
    }
  },
  {
    "stage_type": "reduce",
    "stage_id": "group_by",
    "parameters": {
      "field": "metadata.tags"
    }
  }
]

Segment-Level Retrieval

[
  {
    "stage_type": "filter",
    "stage_id": "feature_search",
    "parameters": {
      "feature_uris": [{"input": {"text": "{{INPUT.query}}"}, "uri": "mixpeek://text_extractor@v1/embedding"}],
      "limit": 20
    }
  },
  {
    "stage_type": "apply",
    "stage_id": "unwind",
    "parameters": {
      "field": "content.segments",
      "output_field": "current_segment"
    }
  },
  {
    "stage_type": "sort",
    "stage_id": "rerank",
    "parameters": {
      "inference_name": "baai_bge_reranker_v2_m3",
      "query": "{{INPUT.query}}",
      "document_field": "current_segment"
    }
  }
]

Error Handling

ErrorBehavior
Field path doesn’t existDocument dropped (or preserved if preserve_null_and_empty=true)
Field is not an arrayDocument passed through unchanged
Empty arrayDocument dropped (or preserved with null if preserve_null_and_empty=true)
Null field valueSame as empty array behavior
  • JSON Transform - Restructure document fields without expansion
  • Group By - Inverse operation: group documents by field value
  • Deduplicate - Remove duplicates after expansion