Sequential queries (NEXT)

NEXT allows for linear composition of queries into a sequence of smaller, self-contained segments, passing the whole table of intermediate results from one segment to the next.

NEXT has the following benefits:

  • NEXT can improve the modularity and readability of complex queries.

  • NEXT can be used instead of WITH clause to construct complex queries.

  • NEXT can improve the usability of Conditional queries (WHEN).

  • NEXT allows for passing the full table of intermediate results into the branches of a UNION.

Example graph

The following graph is used for the examples on this page:

To recreate the graph, run the following query against an empty Neo4j database.

CREATE (techCorp:Supplier {name: 'TechCorp', email: 'contact@techcorp.com'}),
       (foodies:Supplier {name: 'Foodies Inc.', email: 'info@foodies.com'}),

       (laptop:Product {name: 'Laptop', price: 1000}),
       (phone:Product {name: 'Phone', price: 500}),
       (headphones:Product {name: 'Headphones', price: 250}),
       (chocolate:Product {name: 'Chocolate', price: 5}),
       (coffee:Product {name: 'Coffee', price: 10}),

       (amir:Customer {firstName: 'Amir', lastName: 'Rahman', email: 'amir.rahman@example.com', discount: 0.1}),
       (keisha:Customer {firstName: 'Keisha', lastName: 'Nguyen', email: 'keisha.nguyen@example.com', discount: 0.2}),
       (mateo:Customer {firstName: 'Mateo', lastName: 'Ortega', email: 'mateo.ortega@example.com', discount: 0.05}),
       (hannah:Customer {firstName: 'Hannah', lastName: 'Connor', email: 'hannah.connor@example.com', discount: 0.15}),
       (leila:Customer {firstName: 'Leila', lastName: 'Haddad', email: 'leila.haddad@example.com', discount: 0.1}),
       (niko:Customer {firstName: 'Niko', lastName: 'Petrov', email: 'niko.petrov@example.com', discount: 0.25}),
       (yusuf:Customer {firstName: 'Yusuf', lastName: 'Abdi', email: 'yusuf.abdi@example.com', discount: 0.1}),

       (amir)-[:BUYS {date: date('2024-10-09')}]->(laptop),
       (amir)-[:BUYS {date: date('2025-01-10')}]->(chocolate),
       (keisha)-[:BUYS {date: date('2023-07-09')}]->(headphones),
       (mateo)-[:BUYS {date: date('2025-03-05')}]->(chocolate),
       (mateo)-[:BUYS {date: date('2025-03-05')}]->(coffee),
       (mateo)-[:BUYS {date: date('2024-04-11')}]->(laptop),
       (hannah)-[:BUYS {date: date('2023-12-11')}]->(coffee),
       (hannah)-[:BUYS {date: date('2024-06-02')}]->(headphones),
       (leila)-[:BUYS {date: date('2023-05-17')}]->(laptop),
       (niko)-[:BUYS {date: date('2025-02-27')}]->(phone),
       (niko)-[:BUYS {date: date('2024-08-23')}]->(headphones),
       (niko)-[:BUYS {date: date('2024-12-24')}]->(coffee),
       (yusuf)-[:BUYS {date: date('2024-12-24')}]->(chocolate),
       (yusuf)-[:BUYS {date: date('2025-01-02')}]->(laptop),

       (techCorp)-[:SUPPLIES]->(laptop),
       (techCorp)-[:SUPPLIES]->(phone),
       (techCorp)-[:SUPPLIES]->(headphones),
       (foodies)-[:SUPPLIES]->(chocolate),
       (foodies)-[:SUPPLIES]->(coffee)

Syntax

NEXT syntax
<Query1>

NEXT

<Query2>

NEXT

<Query3>

Passing values to subsequent queries

NEXT passes the result table of a query to the subsequent query. In the following example, NEXT passes the variable customer to the second query:

Passing a variable to another query via NEXT
MATCH (c:Customer)
RETURN c AS customer

NEXT

MATCH (customer)-[:BUYS]->(:Product {name: 'Chocolate'})
RETURN customer.firstName AS chocolateCustomer
Result
chocolateCustomer

"Amir"

"Mateo"

"Yusuf"

Rows: 3

Passing multiple variables to another query via NEXT
MATCH (c:Customer)-[:BUYS]->(p:Product {name: 'Chocolate'})
RETURN c AS customer, p AS product

NEXT

RETURN customer.firstName AS chocolateCustomer,
       product.price * (1 - customer.discount) AS chocolatePrice
Result
chocolateCustomer chocolatePrice

"Amir"

4.5

"Mateo"

4.75

"Yusuf"

4.5

Rows: 3

When followed by NEXT, a RETURN clause may only contain variables or aliased expressions. Literals or unaliased expressions are not allowed. For example, RETURN 1 and RETURN 1 + 1 cannot precede NEXT, but RETURN 1 AS one and RETURN 1 + 1 AS two can.

Variables which are local to the query preceding NEXT and which are not explicitly returned as part of the result of that query are not accessible by subsequent queries in the context of NEXT. This allows you to control variable scope similarly to what you can do with WITH, see Control variables in scope.

Aggregation after NEXT

NEXT passes the result table as a whole to the subsequent query. This is particularly useful when aggregating values.

In the following example, NEXT passes the variable customer to the second query:

Aggregation after NEXT
MATCH (c:Customer)-[:BUYS]->(p:Product)
RETURN c AS customer, p AS product

NEXT

RETURN product.name AS product,
       COUNT(customer) AS numberOfCustomers
Result
product numberOfCustomers

"Laptop"

4

"Chocolate"

3

"Headphones"

3

"Coffee"

3

"Phone"

1

Rows: 5

Interactions with UNION queries

Using UNION after NEXT

If a UNION query follows a NEXT the full table of intermediate results is passed into all branches of the UNION query.

UNION after NEXT
MATCH (c:Customer)-[:BUYS]->(p:Product)
RETURN c, p

NEXT

RETURN c.firstName AS name, COLLECT(p.price * (1 - c.discount)) AS purchases, "discounted price" AS type
UNION
RETURN c.firstName AS name, COLLECT(p.price) AS purchases, "real price" AS type

NEXT

RETURN * ORDER BY name, type
Result
name purchases type

"Amir"

[900.0, 4.5]

"discounted price"

"Amir"

[1000, 5]

"real price"

"Hannah"

[212.5, 8.5]

"discounted price"

"Hannah"

[250, 10]

"real price"

"Keisha"

[200.0]

"discounted price"

"Keisha"

[250]

"real price"

"Leila"

[900.0]

"discounted price"

"Leila"

[1000]

"real price"

"Mateo"

[950.0, 4.75, 9.5]

"discounted price"

"Mateo"

[1000, 5, 10]

"real price"

"Niko"

[375.0, 187.5, 7.5]

"discounted price"

"Niko"

[500, 250, 10]

"real price"

"Yusuf"

[900.0, 4.5]

"discounted price"

"Yusuf"

[1000, 5]

"real price"

Rows: 14

Using UNION before NEXT

If a UNION query precedes a NEXT the full result of the UNION is passed to the subsequent query.

UNION before NEXT
MATCH (c:Customer)-[:BUYS]->(:Product{name: "Laptop"})
RETURN c.firstName AS customer
UNION ALL
MATCH (c:Customer)-[:BUYS]-> (:Product{name: "Coffee"})
RETURN c.firstName AS customer

NEXT

RETURN customer AS customer, count(customer) as numberOfProducts
Result
customer numberOfProducts

"Amir"

1

"Mateo"

2

"Leila"

1

"Yusuf"

1

"Hannah"

1

"Niko"

1

Rows: 6

In this example, the list of customer names from the first segment has a duplicate entry for "Mateo" who bought both a laptop and coffee. The use of UNION ALL added him to the list twice. The second segment can access the list, because both parts of the UNION return a part of the list, aliased as customer. By using count(), the list aggregates the duplicate in the RETURN part of the query.

NEXT inside a UNION using {}

If a UNION query has a NEXT in any of its blocks, it is necessary to wrap that block with {}.

NEXT inside UNION
{
MATCH (c:Customer)-[:BUYS]->(:Product {name: 'Chocolate'})
RETURN c AS customer

NEXT

RETURN customer.firstName AS plantCustomer
}

UNION ALL

{
MATCH (c:Customer)-[:BUYS]->(:Product {name: 'Coffee'})
RETURN c AS customer

NEXT

RETURN customer.firstName AS plantCustomer
}
Result
plantCustomer

"Amir"

"Mateo"

"Yusuf"

"Mateo"

"Hannah"

"Niko"

Rows: 6

Interactions with CALL subqueries

CALL subqueries pass the table of intermediate results to the subquery row by row, while NEXT passes the table as a whole. If NEXT is wrapped in a CALL subquery, the result of the preceding query is passed to NEXT a single row at a time. This can be used to compute more complex aggregates in groups.

NEXT inside CALL
MATCH (p:Product) WHERE p.name <> "Coffee"
CALL (p) {
    MATCH (p)<-[:BUYS]-(c:Customer)-[:BUYS]->(otherProduct)
    RETURN c, otherProduct

    NEXT

    RETURN count(DISTINCT c) AS customers, 0 AS customersAlsoBuyingCoffee
    UNION
    FILTER otherProduct.name = "Coffee"
    RETURN 0 as customers, count(DISTINCT c) AS customersAlsoBuyingCoffee

    NEXT

    RETURN max(customers) AS customers, max(customersAlsoBuyingCoffee) AS customersAlsoBuyingCoffee
}
RETURN p.name AS product,
       round(toFloat(customersAlsoBuyingCoffee) * 100 / customers, 1) AS percentageOfCustomersAlsoBuyingCoffee
  ORDER BY product
Result
product percentageOfCustomersAlsoBuyingCoffee

"Chocolate"

33.3

"Headphones"

100.0

"Laptop"

33.3

"Phone"

100.0

Rows: 5

This example computes the percentage of customers that also bought coffee for each non-coffee product. For each product p the subquery finds all pairs of a customer c of product p and another product otherProduct that the customer has also bought. The first NEXT passes these pairs as a whole into a UNION, so that the query can:

  1. count all customers in the first branch of the union.

  2. count the customers who also bought coffee in the second branch of the union.

The UNION produces two rows — one from each branch. The second NEXT passes these two rows as a whole to a query that aggregates them into a single row, which is the result of the CALL subquery.

NEXT cannot be used inside a CALL subquery that uses the (deprecated) importing WITH syntax.

Interactions with conditional queries

Using conditional queries before or after NEXT

Conditional queries act similar to CALL by processing the incoming table of intermediate result row by row. A conditional query following a NEXT acts equivalent to a conditional query wrapped in a CALL subquery.

Conditional query inside NEXT
MATCH (c:Customer)-[:BUYS]->(:Product)<-[:SUPPLIES]-(s:Supplier)
RETURN c.firstName AS customer, s.name AS supplier

NEXT

WHEN supplier = "TechCorp" THEN
  RETURN customer, "Tech enjoyer" AS personality
WHEN supplier = "Foodies Inc." THEN
  RETURN customer, "Tropical plant enjoyer" AS personality

NEXT

RETURN customer, collect(DISTINCT personality) AS personalities

NEXT

WHEN size(personalities) > 1 THEN
  RETURN customer, "Enjoyer of tech and plants" AS personality
ELSE
  RETURN customer, personalities[0] AS personality
Result
customer personality

"Amir"

"Enjoyer of tech and plants"

"Mateo"

"Enjoyer of tech and plants"

"Yusuf"

"Enjoyer of tech and plants"

"Niko"

"Enjoyer of tech and plants"

"Hannah"

"Enjoyer of tech and plants"

"Leila"

"Tech enjoyer"

"Keisha"

"Tech enjoyer"

Rows: 7

In the query above, customers are assigned personality types based on the products they purchased. The second segment is a conditional query that returns different base personality types for different suppliers the customers purchased from. The third segment aggregates the personality types. Finally, the fourth segment is another conditional query which subsumes multiple base personality types, if present, to a new personality.

NEXT inside a conditional query using {}

If a conditional query has a NEXT in any of its THEN or ELSE blocks, it is necessary to wrap the part after THEN or ELSE with {}.

NEXT inside conditional query
MATCH (c:Customer)-[:BUYS]->(p:Product)
RETURN c AS customer, sum(p.price) AS sum

NEXT

WHEN sum >= 1000 THEN {
  RETURN customer.firstName AS customer, "club 1000 plus" AS customerType, sum AS sum
}
ELSE {
  RETURN customer AS customer, sum * (1 - customer.discount) AS finalSum

  NEXT

  RETURN customer.firstName AS customer, "club below 1000" AS customerType, finalSum AS sum
}
Result
customer customerType sum

"Amir"

"club 1000 plus"

1005

"Mateo"

"club 1000 plus"

1015

"Leila"

"club 1000 plus"

1000

"Yusuf"

"club 1000 plus"

1005

"Keisha"

"club below 1000"

200.0

"Hannah"

"club below 1000"

221.0

"Niko"

"club below 1000"

570.0

Rows: 3

The query above calculates the total price of products purchased per customer and then only applies the customer discount to sums below 1000.

Known issues and fixes

NEXT initially did not correctly handle aggregations in the context of UNION branches and CALL subqueries. As of Neo4j 2025.08, this issue has been fixed. The table below summarizes the behavior changes across versions.

Neo4j version Behavior

2025.06

NEXT yields incorrect results when using aggregations in the context of UNION branches and CALL subqueries.

2025.07

Using aggregations with NEXT in the context of UNION branches and CALL subqueries throws an error.

2025.08+

NEXT correctly supports aggregations in the context of UNION branches and CALL subqueries. See above for more details.