Skip to content

Commit 0c48f1d

Browse files
image and more example
1 parent 5444a97 commit 0c48f1d

File tree

2 files changed

+59
-55
lines changed

2 files changed

+59
-55
lines changed
Lines changed: 1 addition & 1 deletion
Loading

modules/ROOT/pages/clauses/order-by.adoc

Lines changed: 58 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -6,31 +6,30 @@
66
`ORDER BY` is a subclause which specifies the order in which the output of a xref:clauses/return.adoc[`RETURN`] or xref:clauses/with.adoc[`WITH`] clause.
77
As of Neo4j 5.24, it can also be used as a standalone clause, either on its own or in combination with `SKIP`/`OFFSET` or `LIMIT`.
88

9-
`ORDER BY` by default sorts results in an ascending order, though it can be modified to return results in a xref:clauses/order-by.adoc#ascending-descending-order[descending order].
9+
`ORDER BY` defaults to sorting results in an ascending order, though it can be modified to sort results in a xref:clauses/order-by.adoc#ascending-descending-order[descending order].
1010

1111
`ORDER BY` relies on comparisons to sort the output (see xref:values-and-types/ordering-equality-comparison.adoc[] for more details).
1212
You can sort on different values, such as node or relationship properties, IDs, or the result of expressions.
1313

1414
[IMPORTANT]
15-
====
1615
Unless `ORDER BY` is used, Neo4j does not guarantee the row order of a query result.
17-
====
16+
1817

1918
[[example-graph]]
2019
== Example graph
2120

22-
The following graph is used for the examples below:
21+
A graph with the following schema is used for the examples below:
2322

24-
image::list_expressions_graph.svg[width="600", role="middle"]
23+
image::graph_order_by_clause.svg[width="400", role="middle"]
2524

2625
To recreate it, run the following query against an empty Neo4j database:
2726

2827
[source, cypher, role=test-setup]
2928
----
30-
CREATE (o1:Order {id: 'ORD-001', orderDate: datetime('2024-05-01T10:00:00'), total: 750, status: 'shipped'}),
29+
CREATE (o1:Order {id: 'ORD-001', orderDate: datetime('2024-05-01T10:00:00'), total: 550, status: 'shipped'}),
3130
(o2:Order {id: 'ORD-002', orderDate: datetime('2024-05-02T14:30:00'), total: 1000, status: 'pending'}),
32-
(o3:Order {id: 'ORD-003', orderDate: datetime('2024-05-03T09:15:00'), total: 750, status: 'pending'}),
33-
(o4:Order {id: 'ORD-004', orderDate: datetime('2024-05-04T12:45:00'), total: 500}),
31+
(o3:Order {id: 'ORD-003', orderDate: datetime('2024-05-03T09:15:00'), total: 550, status: 'pending'}),
32+
(o4:Order {id: 'ORD-004', orderDate: datetime('2024-05-04T12:45:00'), total: 200}),
3433
(o5:Order {id: 'ORD-005', orderDate: datetime('2024-05-05T15:00:00'), total: 800, status: 'shipped'}),
3534
3635
(i1:Item {name: 'Phone', price: 500}),
@@ -74,11 +73,11 @@ The nodes are returned, sorted by the value of the `total` properties in an asce
7473
|===
7574
| order | total
7675

77-
| "ORD-004" | 500
78-
| "ORD-001" | 750
79-
| "ORD-003" | 750
76+
| "ORD-004" | 200
77+
| "ORD-001" | 550
78+
| "ORD-003" | 550
8079
| "ORD-005" | 800
81-
| "ORD-002" | 1000
80+
| "ORD-002" | 1000
8281

8382
2+d|Rows: 5
8483
|===
@@ -108,9 +107,9 @@ This returns the nodes, sorted first by their `total` property, and then, for eq
108107
|===
109108
| order | total | orderDate
110109

111-
| "ORD-004" | 500 | 2024-05-04T12:45Z
112-
| "ORD-001" | 750 | 2024-05-01T10:00Z
113-
| "ORD-003" | 750 | 2024-05-03T09:15Z
110+
| "ORD-004" | 200 | 2024-05-04T12:45Z
111+
| "ORD-001" | 550 | 2024-05-01T10:00Z
112+
| "ORD-003" | 550 | 2024-05-03T09:15Z
114113
| "ORD-005" | 800 | 2024-05-05T15:00Z
115114
| "ORD-002" | 1000 | 2024-05-02T14:30Z
116115

@@ -132,8 +131,6 @@ RETURN o.id AS order,
132131
ORDER BY elementId
133132
----
134133

135-
The nodes are returned, sorted by their internal IDs.
136-
137134
.Result
138135
[role="queryresult",options="header,footer",cols="2*<m"]
139136
|===
@@ -154,7 +151,7 @@ Applications relying on internal Neo4j IDs are, as a result, brittle and can be
154151
It is recommended to use application-generated IDs instead.
155152

156153
[[order-by-expression]]
157-
== Order by expressions results
154+
== Order by expressions
158155

159156
`ORDER BY` can be used to sort according to the results of an xref:expressions/index.adoc[expression].
160157
The below query calculates a 10% discount on each order's `total` property value, and then orders the results by the discounted total.
@@ -173,9 +170,9 @@ RETURN o.id AS order,
173170
|===
174171
| order | discountedTotal
175172

176-
| "ORD-004" | 450.0
177-
| "ORD-001" | 675.0
178-
| "ORD-003" | 675.0
173+
| "ORD-004" | 180.0
174+
| "ORD-001" | 495.0
175+
| "ORD-003" | 495.0
179176
| "ORD-005" | 720.0
180177
| "ORD-002" | 900.0
181178

@@ -228,9 +225,9 @@ RETURN o.id AS order,
228225
|===
229226
| order | total
230227

231-
| "ORD-004" | 500
232-
| "ORD-001" | 750
233-
| "ORD-003" | 750
228+
| "ORD-004" | 200
229+
| "ORD-001" | 550
230+
| "ORD-003" | 550
234231
| "ORD-005" | 800
235232
| "ORD-002" | 1000
236233

@@ -255,9 +252,9 @@ RETURN o.id AS order,
255252

256253
| "ORD-002" | 1000
257254
| "ORD-005" | 800
258-
| "ORD-001" | 750
259-
| "ORD-003" | 750
260-
| "ORD-004" | 500
255+
| "ORD-001" | 550
256+
| "ORD-003" | 550
257+
| "ORD-004" | 200
261258

262259
2+d|Rows: 5
263260
|===
@@ -292,7 +289,7 @@ RETURN o.id AS order,
292289
|===
293290

294291
[[null]]
295-
== Ordering `null`
292+
== Null values
296293

297294
When sorting, `null` values appear last in ascending order and first in descending order.
298295

@@ -321,13 +318,12 @@ RETURN o.id AS order,
321318

322319

323320
[[order-with]]
324-
== Ordering in a `WITH` clause
321+
== ORDER BY and the WITH clause
325322

326-
When `ORDER BY` is present on a `WITH` clause , the immediately following clause will receive records in the specified order.
323+
When `ORDER BY` is present on a `WITH` clause, the immediately following clause will receive records in the specified order.
327324
The ordering guarantee can be useful to exploit by operations which depend on the order in which they consume values.
328-
For example, this can be used to control the order of items in the list produced by the xref:functions/aggregating.adoc#functions-collect[`collect()`] aggregating function.
325+
For example, appending `ORDER BY` to a `WITH` clause can be used to control the order of items in the list produced by the xref:functions/aggregating.adoc#functions-collect[`collect()`] aggregating function.
329326
The xref:clauses/merge.adoc[`MERGE`] and xref:clauses/set.adoc[`SET`] clauses also have ordering dependencies which can be controlled this way.
330-
The order is not guaranteed to be retained after the following clause, unless that also has an `ORDER BY` subclause.
331327

332328
The below example uses `WITH` and `ORDER BY` to sort `Item` nodes by their `price` property, then the `collect()` in the subsequent `RETURN` clause builds an ordered list per order based on that sort.
333329

@@ -351,40 +347,48 @@ RETURN o.id AS orderId,
351347
| "ORD-001" | ["Phone($500)", "Charger($50)"]
352348
| "ORD-003" | ["Phone($500)", "Charger($50)"]
353349
| "ORD-005" | ["Phone($500)", "Headphones($250)", "Charger($50)"]
354-
| "ORD-004" | ["Keyboard($200)"]
350+
| "ORD-004" | ["Keyboard($200)"]
355351

356352
2+d|Rows: 5
357353
|===
358354

359-
The next query uses `ORDER BY` to sort `Item` nodes by order count, then creates a `discount` property with `SET` based on the order count:
355+
[[aggregation-distinct]]
356+
== Ordering aggregated or DISTINCT results
360357

361-
.`WITH`, `ORDER BY`, and `SET`
358+
The variables available to `ORDER BY` depends on whether or not the preceding `RETURN` or `WITH` clause performs an aggregation to combine results or uses `DISTINCT` to remove duplicates.
359+
360+
* If the `RETURN` or `WITH` is not aggregating values or using `DISTINCT`, then `ORDER BY` can reference any variables referenced in the preceding `RETURN` or `WITH` clause.
361+
362+
.`ORDER BY` following a `WITH` clause excluding aggregation or `DISTINCT`
362363
[source, cypher]
363364
----
364-
MATCH (i:Item)<-[:CONTAINS]-(o:Order)
365-
WITH i,
366-
count(o) AS orderCount
367-
ORDER BY orderCount DESC
368-
SET i.discount = CASE
369-
WHEN orderCount < 2 THEN 0.10
370-
ELSE 0.05
371-
END
372-
RETURN i.name AS item,
373-
orderCount,
374-
i.price AS originalPrice,
375-
(i.price * (1 - i.discount)) AS discountedPrice
365+
MATCH (o:Order)-[:CONTAINS]->(i:Item)
366+
WITH o.id AS order,
367+
i.name AS item
368+
ORDER BY o.orderDate
369+
RETURN order, item
376370
----
377371

378-
== Ordering aggregated or DISTINCT results
372+
* If the `RETURN` or `WITH` performs an aggregation or uses `DISTINCT` only the projected variables from either operation are available to `ORDER BY`.
373+
This is because these operations alter the number of rows produced by the clause and any variables not explicitly projected are discarded.
379374

380-
In terms of scope of variables, `ORDER BY` follows special rules, depending on if the projecting `RETURN` or `WITH` clause is either aggregating or `DISTINCT`.
381-
If it is an aggregating or `DISTINCT` projection, only the variables available in the projection are available.
382-
If the projection does not alter the output cardinality (which aggregation and `DISTINCT` do), variables available from before the projecting clause are also available.
383-
When the projection clause shadows already existing variables, only the new variables are available.
375+
.`ORDER BY` following a `WITH` clause projecting an aggregated value
376+
[source, cypher, role=test-fail]
377+
----
378+
MATCH (o:Order)-[:CONTAINS]->(i:Item)
379+
WITH collect(o.id) AS orders,
380+
i.name AS items
381+
ORDER BY o.orderDate
382+
RETURN orders, items
383+
----
384384

385-
It is also not allowed to use aggregating expressions in the `ORDER BY` subclause if they are not also listed in the projecting clause.
386-
This rule is to make sure that `ORDER BY` does not change the results, only the order of them.
385+
.Error message
386+
[source, error]
387+
----
388+
In a WITH/RETURN with DISTINCT or an aggregation, it is not possible to access variables declared before the WITH/RETURN: o
389+
----
387390

391+
[[indexes]]
388392
== ORDER BY and indexes
389393

390394
The performance of Cypher queries using `ORDER BY` on node properties can be influenced by the existence and use of an index for finding the nodes.

0 commit comments

Comments
 (0)