Sequential queries (NEXT)

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

NEXT has the following benefits:

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

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 a query and which are not explicitly returned 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.

Interactions with CALL subqueries

NEXT can serve as a more readable alternative to CALL subqueries.

MATCH (p:Product)
CALL (p) {
    MATCH (c:Customer)-[:BUYS]->(p)
    RETURN collect(c.firstName) AS customers
}
RETURN p.name as product, customers
MATCH (p:Product)
RETURN p

NEXT

MATCH (c:Customer)-[:BUYS]->(p)
RETURN collect(c.firstName) AS customers, p

NEXT

RETURN p.name as product, customers
Result
product customers

"Laptop"

["Amir", "Mateo", "Leila", "Yusuf"]

"Phone"

["Niko"]

"Headphones"

["Keisha", "Hannah", "Niko"]

"Chocolate"

["Amir", "Mateo", "Yusuf"]

"Coffee"

["Mateo", "Hannah", "Niko"]

Rows: 5

Even though the query which uses NEXT has more lines, it is divided into three segments which are easy to read. It also avoids the parentheses and indentation of the CALL subquery.

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

Interactions with conditional queries

Conditional queries in 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 a 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.

Interactions with UNION queries

NEXT in a query using UNION
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