Reputation: 22243
I have a neo4j database which has nearly 500k CK_ITEM
nodes defined as follows:
CK_ITEM: {
id (String),
name (String),
description (String)
}
Suppose we have this sample data:
+--------+----+-----------------+
| name | id | description |
+--------+----+-----------------+
| Mark | 1 | A lot of things |
| Gerald | 9 | Coff2e |
| Carl | 2 | 1 mango |
| James | 3 | 5 lemons |
| Edward | 4 | Coffee |
+--------+----+-----------------+
I need to order the data by description ASC
. This is my query:
MATCH (n:CK_ITEM)
ORDER BY
n.description ASC
This results in:
+--------+----+-----------------+
| name | id | description |
+--------+----+-----------------+
| Carl | 2 | 1 mango | <-- '1' < '5'
| James | 3 | 5 lemons | <-- '5' < 'A'
| Mark | 1 | A lot of things | <-- 'A' < 'C'
| Gerald | 9 | Coff2e | <-- '2' < 'e'
| Edward | 4 | Coffee |
+--------+----+-----------------+
Now, the customer asked me to order the results so that they are still in ascending order, but numbers are left last.
Basically he wants the results to be:
+--------+----+-----------------+
| name | id | description |
+--------+----+-----------------+
| Mark | 1 | A lot of things |
| Edward | 4 | Coffee |
| Gerald | 9 | Coff2e | <-- Coff2e after Coffee
| Carl | 2 | 1 mango | <-- 1 and 5 ASC after everything
| James | 3 | 5 lemons |
+--------+----+-----------------+
Translated to a pseudo-query, it would be something like this:
MATCH CK_ITEM ORDER BY letters(description) ASC numbers(description) ASC
Is it possible to have this kind of sorting (letters first ascending, numbers last ascending) in a single query? How?
Upvotes: 2
Views: 260
Reputation: 67044
The following is a Cypher query that will perform a sort where digits come last (at every character position).
NOTE: THIS APPROACH IS NOT EFFICIENT, but is presented as an example of how to do this in Cypher if you absolutely needed to.
The query splits every description
value into single-character strings, tests each character to see if it is a digit, constructs a new string (character by character) -- replacing every digit with a corresponding UTF-16 character in the hex range FFF6
to FFFF
(these are the highest possible UTF-16 character encodings, and your raw data is unlikely to already be using them), and uses that new string for sorting purposes.
WITH {`0`:'\uFFF6',`1`:'\uFFF7',`2`:'\uFFF8',`3`:'\uFFF9',`4`:'\uFFFA',`5`:'\uFFFB',`6`:'\uFFFC',`7`:'\uFFFD',`8`:'\uFFFE',`9`:'\uFFFF'} AS big
MATCH (n:CK_ITEM)
WITH n, SPLIT(n.description, '') AS chars, big
RETURN n
ORDER BY
REDUCE(s='', i IN RANGE(0, LENGTH(chars)-1) |
CASE WHEN '9' >= chars[i] >= '0'
THEN s + big[chars[i]]
ELSE s + chars[i]
END)
Upvotes: 1
Reputation: 29167
You can use reqular expression and UNION:
MATCH (n:CK_ITEM) WHERE NOT n.description =~ '[0-9].*'
RETURN n
ORDER BY
n.description ASC
UNION
MATCH (n:CK_ITEM) WHERE n.description =~ '[0-9].*'
RETURN n
ORDER BY
n.description ASC
Upvotes: 0
Reputation: 16373
You can order this way:
MATCH (n:CK_ITEM)
RETURN n
ORDER BY
substring(n.description,0,1) in ['0','1','2','3','4','5','6','7','8','9'], n.name
Upvotes: 0