Confused about using ORDER BY correctly - neo4j

I am experimenting with dates in neo4j. Now I'd like to sort my results by the ISODateString. I created a cypher query like this:
MATCH(e:Expedition {id : "BJGYmzwZb"})-[pje]-(u:User)
WHERE (e)-[:POSSIBLY_JOINS_EXPEDITION]-(u) OR (e)-[:JOINS_EXPEDITION]-(u)
WITH e, u, apoc.date.parse(pje.createdAt, 's',"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'") as date
ORDER by date
OPTIONAL MATCH(u)<-[invitee:POSSIBLY_JOINS_EXPEDITION]-(e)
OPTIONAL MATCH(u)-[attendee:JOINS_EXPEDITION]->(e)
OPTIONAL MATCH(u)-[applicant:POSSIBLY_JOINS_EXPEDITION]->(e)
RETURN date, {user: properties(u), isInvitee: COUNT(invitee) > 0, isApplicant: COUNT(applicant) > 0, isAttendee: COUNT(attendee) > 0} as u
The returned results are not sorted properly. Whereas the following query does return the results in the right order. I just removed the parts with OPTIONAL MATCH.
MATCH(e:Expedition {id : "BJGYmzwZb"})-[pje]-(u:User)
WHERE (e)-[:POSSIBLY_JOINS_EXPEDITION]-(u) OR (e)-[:JOINS_EXPEDITION]-(u)
WITH e, u, apoc.date.parse(pje.createdAt, 's',"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'") as date
ORDER by date
RETURN date, {user: properties(u)} as u
Any suggestions what I am doing wrong? Do I need to deal differently with the OPTIONAL MATCH-additions?

Put ORDER by date after the RETURN statement, like this:
MATCH(e:Expedition {id : "BJGYmzwZb"})-[pje]-(u:User)
WHERE (e)-[:POSSIBLY_JOINS_EXPEDITION]-(u) OR (e)-[:JOINS_EXPEDITION]-(u)
WITH e, u, apoc.date.parse(pje.createdAt, 's',"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'") as date
OPTIONAL MATCH(u)<-[invitee:POSSIBLY_JOINS_EXPEDITION]-(e)
OPTIONAL MATCH(u)-[attendee:JOINS_EXPEDITION]->(e)
OPTIONAL MATCH(u)-[applicant:POSSIBLY_JOINS_EXPEDITION]->(e)
RETURN date, {user: properties(u), isInvitee: COUNT(invitee) > 0, isApplicant: COUNT(applicant) > 0, isAttendee: COUNT(attendee) > 0} as u
ORDER by date

Related

How to continue executing a Neo4J Cypher request after MATCH is not matched?

I have the following params in my Neo4J:
{
"lists": [
{
"from": "someone",
"to": "somebody"
}
]
}
And the following query:
MATCH (c:Concept{name:'infranodus'}) WITH c, $lists AS list
UNWIND CASE WHEN list = [{}] THEN [null] ELSE list END AS l
WITH l
MATCH (cp1:Concept{name:l.from})
WITH cp1
MATCH (cp2:Concept{name:'somebody'})
RETURN cp1,cp2;
The query above will work.
However, if I replace l.from with a non-existent parameter, e.g. l.about, then — as the match doesn't happen — the second cp2 match doesn't fire.
How can I change this behavior and continue executing this query even if cp1 is not found? Maybe there's a way to pass on a dummy variable as a result?
MATCH (c:Concept{name:'infranodus'}) WITH c, $lists AS list
UNWIND CASE WHEN list = [{}] THEN [null] ELSE list END AS l
WITH l
MATCH (cp1:Concept{name:l.about})
WITH cp1
MATCH (cp2:Concept{name:'somebody'})
RETURN cp1,cp2;
Use OPTIONAL MATCH. If there is no match is found, then it will use NULL for the missing part of the pattern. It is similar to outer join in SQL.
NEW:
OPTIONAL MATCH (cp1:Concept{name:l.about})
OLD:
MATCH (cp1:Concept{name:l.about})
You can maybe replace it with an IN predicate ?
For eg :
WITH {from: 'Matt Olg', about: 'Matthew Olg'}
AS l
MATCH (n:Person)
WHERE n.name IN [l.from, l.to]
RETURN n.name
╒══════════╕
│"n.name" │
╞══════════╡
│"Matt Olg"│
└──────────┘

Neo4j Match with properties on a variable length path

I checked on the developer manual that you can filter with property on a variable length with relationship like below cypher.
MATCH p =(charlie:Person)-[* { blocked:false }]-(martin:Person)
WHERE charlie.name = 'Charlie Sheen' AND martin.name = 'Martin Sheen'
RETURN p
What I am looking here is can I filter by some logic on numeric properties on the relationship with a variable length.
For example, change the {blocked:false} in the query to a numeric properties like {amount_paid} and filter by amount_paid > 20.
So I can hop on Person by the relationship with amount_paid > 20.
You can use the predicate ALL:
MATCH p=(charlie:Person {name: 'Charlie Sheen')-[*]-(martin:Person {name: 'Martin Sheen')
WHERE ALL(rel in relationships(p) WHERE rel.amount_paind > 20)
RETURN p

Select 'x' returns "x"()

I want to return 3 columns, A(description), 'D'(hard coded value of D), Q(date)
=query('Detailed Plan'!$A$2:$Q, "select A,'D',Q where D = date)
It returns the following results. Rows 2 and greater are exactly what I want and would be perfect if I didn't get the first row. How do I get a hard coded value into a column without "D"() showing up in the first row?
blank, "D"(), blank
Description, D, date
Description, D, date
Description, D, date
Thanks so much for any help that is provided.
You can use 'label' in the query string.
=query('Detailed Plan'!$A$2:$Q, "select A, D, Q where D = date label A 'Description', D 'Some value', Q 'Date' ", 0)
EDIT: if you don't need headers at all, try
=query('Detailed Plan'!$A$2:$Q, "select A, D, Q where D = date ", 0)

Neo4j Aggregate Multiple Lines into a Map

I have the following Cypher script:
MATCH (sy:SchoolYear)<-[:TERM_OF*]-()<-[:DAY_OF]-(d:Day)
WHERE sy.year = 2015
OPTIONAL MATCH (d)<-[:START]-(e:Enrollment)-[:AT]->(s:School)
RETURN d.date, s.abbreviation, count(e)
ORDER BY d.date
This gives me all of the dates in the range that I want and returns number of students that have enrolled for each school for that date, or null. The only issue I have is that different schools are on different lines, causing a single date to have multiple lines. I would like to aggregate those into a single line per date.
I'm given:
1/1/2000, School 1, 5
1/1/2000, School 2, 10
1/2/2000, null, null
1/3/2000, School 1, 6
What I would like:
1/1/2000, {School 1 : 5, School 2: 10}
1/2/2000, null
1/3/2000, {School 1: 6}
I've tried:
MATCH (sy:SchoolYear)<-[TERM_OF*]-()<-[:DAY_OF]-(d:Day)
WHERE sy.year = 2015
OPTIONAL MATCH (d)<-[:START]-(e:Enrollment)-[:AT]->(s:School)
WITH d, s.abbreviation as abb, count(e) as enr
RETURN d.date, {abb:enr}
ORDER BY d.date
How should I go about this?
Here is how I would go with this aggregate each school into a map and the maps into a collection
MATCH (sy:SchoolYear)<-[TERM_OF*]-()<-[:DAY_OF]-(d:Day)
WHERE sy.year = 2015
OPTIONAL MATCH (d)<-[:START]-(e:Enrollment)-[:AT]->(s:School)
WITH d, s, count(e) as students
RETURN d.date, collect({name:s.abbreviation, students:students})
ORDER BY d.date
This is a bit ugly, but I think it returns what you are after. I tried using the school name as a key just like you did in your example and I could not get that to work either. In the end I resorted to this.
MATCH (sy:SchoolYear)<-[TERM_OF*]-()<-[:DAY_OF]-(d:Day)
WHERE sy.year = 2015
OPTIONAL MATCH (d)<-[:START]-(e:Enrolment)-[:AT]->(s:School)
// collect the schools and their counts together
with d, [s.abbreviation, count(e)] as school_count
// collect all of the school counts together by date
with d.date as date, collect(school_count) as school_counts
// format the school counts as a string with the schools
// as keys and the counts as values
with date, reduce( out = "", s in school_counts | out + s[0] + " : " + s[1] + ", " ) as school_count_str
return date, '{ ' + left(school_count_str, length(school_count_str)-2) + ' }' as school_counts
order by date

Neo4j - minimum value from array properties

How do I get with cypher the minimum value of array with properties?
MATCH (n)-[r]->(m) RETURN n,m,min(r.timestamps)
Above query does not work.
r has an array with timestamps r.timestamps
How to get the lowest value of timestamps?
You can use unwind:
MATCH (n)-[r]->(m)
UNWIND r.timestamps as timestampts
RETURN n, m, min(timestampts)
I found an answer like this, but it looks ugly
MATCH
(h1)-[r]-(h2)
RETURN h1, h2,
reduce(minTimestamp = 999999999999999999, t IN r.timestamps | CASE WHEN minTimestamp < t THEN minTimestamp ELSE t END)

Resources