# UNWIND Turns a list into rows: one working row per element. ## Synopsis
UNWIND list_expression AS variable

bold keywords · italic placeholders you supply · [ ] optional · ... repeatable. See Syntax conventions.

## Description `UNWIND` expands a list into the pipeline. For each element it emits one row, binding *variable* to that element; everything else already in scope is carried along unchanged. It is the inverse of `collect()`, and the way to get from one row holding a list to many rows holding its parts. - `AS variable` is **required**. There is no unnamed form. - The list may be a **literal**, a **list-valued property**, a variable a `collect()` produced, or any expression that yields a jsonb array or a PostgreSQL array. A scalar is refused. - An **empty list** produces no rows, so the whole input row disappears. - **Nulls inside** the list are elements like any other and produce rows. - `UNWIND` runs once per incoming row, and the rows it produces multiply out: three input rows over a two-element list give six. - `UNWIND` may lead a query or follow another clause. It cannot end one. - Nest two `UNWIND` clauses to flatten a list of lists. [`FOR`](for.md) is the GQL counterpart and does the same job, with an optional ordinal. ## Parameters list_expression : An expression yielding a jsonb array or a PostgreSQL array. variable : Name bound to each element. Visible to every clause after this one. ## Examples Every example runs against [the example dataset](../../get_started/example-dataset.md). ### Expand a list literal ```cypher UNWIND [1, 91, 203, 1, 7, 91] AS n RETURN n; ``` ``` n ----- 1 91 203 1 7 91 (6 rows) ``` Six rows, in list order, duplicates included. ### Deduplicate and sort a list There is no way to apply `DISTINCT` to a list in place. Unwind it, deduplicate the rows, and collect them back: ```cypher UNWIND [1, 91, 203, 1, 7, 91] AS n WITH DISTINCT n ORDER BY n RETURN collect(n) AS uniq; ``` ``` uniq ----------------- [1, 7, 91, 203] (1 row) ``` ### Drive a MATCH from a list One row per element, each matched in turn: ```cypher UNWIND ['The Matrix', 'Inception'] AS t MATCH (m:movie {title: t}) RETURN m.title AS film, m.year AS year; ``` ``` film | year --------------+------ "The Matrix" | 1999 "Inception" | 2010 (2 rows) ``` This is the usual way to run a query over a client-supplied list of keys, with the list passed as one parameter. ### Expand a list-valued property A property may hold a list, and `UNWIND` opens it: ```cypher MATCH (m:movie {title: 'The Matrix'}) UNWIND m.embedding AS component RETURN component; ``` ``` component ------------- -0.07594558 0.04081754 0.29592122 -0.11921061 (4 rows) ``` ### Undo a collect `collect()` folds rows into a list; `UNWIND` unfolds it again. The pair is how you aggregate, then go back to rows: ```cypher MATCH (p:person)-[:acted_in]->(m:movie) WITH collect(DISTINCT m.genre) AS genres UNWIND genres AS genre RETURN genre ORDER BY genre; ``` ``` genre ------------- "Action" "Adventure" "Sci-Fi" (3 rows) ``` ### Flatten a list of lists Two `UNWIND` clauses, the second over the variable the first bound: ```cypher UNWIND [[1, 2], [3]] AS pair UNWIND pair AS x RETURN pair, x; ``` ``` pair | x --------+--- [1, 2] | 1 [1, 2] | 2 [3] | 3 (3 rows) ``` ### Expand an empty list No rows come out, and the input row goes with them: ```cypher UNWIND [] AS x RETURN x; ``` ``` x --- (0 rows) ``` Use [`OPTIONAL MATCH`](optional-match.md) or a `CASE` guard when a row must survive an empty list. ### Keep the nulls A null element is still an element: ```cypher UNWIND [1, NULL, 3] AS x RETURN x; ``` ``` x ------ 1 null 3 (3 rows) ``` The value prints as `null` because the output column is jsonb, and a jsonb null is a value rather than a missing one. ## Notes `UNWIND` requires a list. Neither a scalar nor a bare `NULL` is treated as a one-element or zero-element list: ```cypher UNWIND 3 AS x RETURN x; ``` ``` ERROR: expression must be jsonb or array, but integer LINE 1: UNWIND 3 AS x RETURN x; ^ ``` ```cypher UNWIND NULL AS x RETURN x; ``` ``` ERROR: expression must be jsonb or array, but unknown LINE 1: UNWIND NULL AS x RETURN x; ^ ``` Wrap a value that might not be a list, as in `UNWIND coalesce(m.tags, []) AS tag`, when the property is not guaranteed to be there. ## Compatibility ### UNWIND is not a GQL clause ISO/IEC 39075 has no `UNWIND`. Its list-expanding statement is `` (§14.8), which AgensGraph also implements as [`FOR`](for.md). `FOR` adds a `WITH OFFSET` clause that gives each element its 0-based position, something `UNWIND` cannot do. ## See also - [`FOR`](for.md) {bdg-primary-line}`GQL` — the standard clause, with an ordinal - [`WITH`](with.md) — where `collect()` goes, and how to deduplicate the rows - [`MATCH`](match.md) — driving a match from the expanded rows - [Functions](../functions/index) — `collect()` and the other list functions