Invalid use of aggregating function max - neo4j

match(n)-[r:LIKES]->(m) with count(n) as cnt, m where cnt = max(cnt) return m
Above query results in following error:
Invalid use of aggregating function max(...) in this context (line 1,
column 61 (offset: 60))

This query should return a collection of the m nodes that have the maximum count. It only needs to perform a single MATCH operation, and should be relatively performant.
MATCH (n)-[:LIKES]->(m)
WITH m, COUNT(n) AS cnt
WITH COLLECT({m: m, cnt: cnt}) AS data, MAX(cnt) AS maxcnt
RETURN REDUCE(ms = [], d IN data | CASE WHEN d.cnt = maxcnt THEN ms + d.m ELSE ms END) AS ms;

If you're just trying to find a single node that has the most LIKES relationships leading to it, then you can add an ORDER BY and LIMIT:
MATCH (n)-[:LIKES]->(m)
WITH m, count(n) AS cnt
ORDER BY cnt DESC
LIMIT 1
RETURN m
However, that query has the limitation in that if more than one node has the maximum number of inbound relationships, then those tied nodes won't be returned. To achieve that result, you might try something like this:
MATCH (n)-[:LIKES]->(m)
WITH m, count(n) AS cnt
WITH MAX(cnt) AS maxcnt
MATCH (o)
WHERE size((o)<-[:LIKES]-()) = maxcnt
RETURN o

Related

Cypher variable lenght pattern aggregated property filtering

I'm working on neo4j and I'm trying to put a condition on a series of relationships, where I need to sum a property of said relationship.
When I do the basic textbook part of filtering for the relationship without the sum, it all works.
MATCH c= (a)-[:VENDE*2..3]->(b)
WHERE ALL (r IN relationships(c)
WHERE r.ammontare = 25000)
RETURN c
When I try to put the condition on the sum of said property, I can't find a way.
I tried REDUCE but it's stuck because the this error I didn't manage to work around: "Type mismatch: accumulator is Integer but expression has type Boolean"
MATCH c= (a)-[:VENDE*2..3]->(b)
WHERE ALL (r IN relationships(c)
WHERE REDUCE( t = 0, n in relationships(c)|t= t+ n.ammontare) = 25000)
RETURN c
I tried with apoc but this doesn't work either. What am I getting wrong?
MATCH c= (a)-[:VENDE*2..3]->(b)
WHERE all(r in relationships(r)
WHERE APOC.COLL.SUM( [r IN relationships(c)| r.ammontare]) = 25000)
return c
The last method runs but it's too heavy and goes into timeout.
MATCH c= (a)-[:VENDE*2]->(b)
WITH c, relationships(c) as rels
UNWIND (rels) as rel
With c, sum(rel.ammontare) as re
where re > 25000
return c
I'm not sure but this will work.
MATCH c= (a)-[:VENDE*2..3]->(b)
WHERE ALL (r IN relationships(c)
WHERE REDUCE( t = 0, n in relationships(c)| t+ n.ammontare) = 25000)
RETURN c

Cannot aggregate total with a CALL with UNION

I'm trying to get the total rows of my union but it only shows a total of 1 for each (rather than 2)
CALL {
OPTIONAL MATCH
(a:Account {
id : $account_id
})
-[:MEMBER]->
(n:Workspace)
RETURN n
UNION
OPTIONAL MATCH
(n:Workspace {
account_id: $account_id
})
RETURN n
}
WITH n, COUNT(n) AS total
RETURN n, total
Is it possible to aggregate the total?
Current non-UNION query (works)
OPTIONAL MATCH
(a:Account {
id : $account_id
})
-[:MEMBER]->
(n:Workspace)
WITH
COLLECT(n) AS nodes,
COUNT(n) AS total
OPTIONAL MATCH
(n:Workspace {
account_id: $account_id
})
WITH
nodes + COLLECT(n) AS n,
total + COUNT(n) AS total
UNWIND n AS node
WITH
node,
total
SKIP $skip
LIMIT $limit
WITH
COLLECT(node) AS results,
total
RETURN results, total
If I understand you correctly, you get two rows with a result of 1 instead of 1 row with the result of 2. When aggregating in cypher it does implied groupby, meaning that all the columns in the WITH or RETURN statement will be used in the groupby statement by default. So if you want to return only a total count you can use:
CALL {
OPTIONAL MATCH
(a:Account {
id : $account_id
})
-[:MEMBER]->
(n:Workspace)
RETURN n
UNION
OPTIONAL MATCH
(n:Workspace {
account_id: $account_id
})
RETURN n
}
WITH n
RETURN count(*) as total

two match and with queries return no results

With my following query, I expected a r to be an array and t to be a number. But when I run the query, it just shows (no changes, no records).
When run individually, their output is as expected. So what is wrong with my query?
MATCH
(a:Account {
id : $account_id
})
-[:MEMBER]->
(n:Workspace)
WITH
COLLECT(n) AS nodes,
COUNT(n) AS total
MATCH
(n:Workspace {
account_id: $account_id
})
WITH
nodes + COLLECT(n) AS r,
total + COUNT(n) AS t
RETURN r, t

Possible to combine these two queries

I have two cypher queries and would like to know if there is a possibility to get the Points and HeadHunter information within one single statement.
MATCH (s:SEASON)-[*]->(e:EVENT)<-[f:FINISHED]-(p:PLAYER)
WHERE s.id = 8 AND e.id <= 1197
RETURN p, sum(f.points) AS Points
ORDER BY Points DESC
MATCH (s:SEASON)-[*]->(e:EVENT)<-[el:ELIMINATED]-(p:PLAYER)
WHERE s.id = 8 AND e.id <= 1197
RETURN p, sum(el.points) AS HeadHunter
ORDER BY HeadHunter DESC
I played around with different approaches but none of them worked. I would like to do something like this:
MATCH (s:SEASON)-[*]->(e:EVENT)<-[el:ELIMINATED]-(p:PLAYER),(e)<-[f:FINISHED]-(p)
WHERE s.id = 8 AND e.id <= 1197
RETURN p, sum(el.points) AS HeadHunter, sum(f.points) AS Points
Does this work for you?
MATCH (s:SEASON)-[*]->(e:EVENT)<-[rel:FINISHED|ELIMINATED]-(p:PLAYER)
WHERE s.id = 8 AND e.id <= 1197
WITH p, COLLECT(rel) AS rels
RETURN p,
REDUCE(s = 0, x IN rels | CASE WHEN TYPE(x) = 'FINISHED' THEN s + x.points ELSE s END) AS Points,
REDUCE(s = 0, x IN rels | CASE WHEN TYPE(x) = 'ELIMINATED' THEN s + x.points ELSE s END) AS HeadHunter
This should return the relevant sums for each PLAYER that either finished and/or got eliminated.
In principle I think your query can work, but you may have made a mistake with your p variable. In this query:
MATCH (s:SEASON)-[*]->(e:EVENT)<-[el:ELIMINATED]-(p:PLAYER),(e)<-[f:FINISHED]-(p)
WHERE s.id = 8 AND e.id <= 1197
RETURN p, sum(el.points) AS HeadHunter, sum(f.points) AS Points
Note that p has to be a player with both the ELIMINATED relationship and the FINISHED relationship. I'm guessing based on the semantics of your domain, that doesn't make sense; you can't be eliminated and have finished, right?
So you might want to try some variant of this:
MATCH (s:SEASON)-[*]->(e:EVENT)<-[el:ELIMINATED]-(pElim:PLAYER),(e)<-[f:FINISHED]-(pFinish)
WHERE s.id = 8 AND e.id <= 1197
RETURN pElim, sum(el.points) AS HeadHunter, pFinish, sum(f.points) AS Points
Note with this you'll have a lot of extra rows of output for various combinations of pElim and pFinish but the respective values should be correct.

cypher path with condition on relationship direction

I want to find all paths given a start node
MATCH path=(n)-[rels*1..10]-(m)
with the following 2 conditions on path inlcusion:
true if relationship between subsequent nodes in path has property PROP='true'
if type(relationship)=SENDS then true if direction of the relationship is outgoing (from one path node to the next node in the path)
Another way of phrasing this is that direction doesn't matter unless the relationship name is SENDS
I can do condition 1 with WHERE ALL (r IN rels WHERE r.PROP='true') however ive no idea how to do condition 2.
The only way I can think of to filter on relationship direction without declaring direction in the match pattern is by comparing the start node of each relationship in the path with the node at the corresponding index of the nodes() collection from the path. For this you need the relationship and node collections from the path, an index counter and some boolean evaluation equivalent to ALL(). One way to do it is to use REDUCE with a collection for the accumulator, so you can accumulate index and maintain a true/false value for the path at the same time. Here's an example, the accumulator starts at [0,1] where the 0 is the index for testing that startNode(r) equals the node at the corresponding index in the node collection (i.e. it's an outgoing relationship) and the 1 represents true, which signifies that the path has not yet failed your conditions. For each relationship the index value is incremented and the CASE/WHEN clause multiplies the 'boolean' with 1 if your conditions are satisfied, with 0 if not. The evaluation of the path is then the evaluation of the second value in the collection returned by REDUCE -- if 1 then yay, if 0 then boo.
MATCH path = (n)-[*1..10]-(m)
WITH path, nodes(path) as ns, relationships(path) as rs
WHERE REDUCE(acc = [0,1], r IN rs |
[acc[0]+1, CASE WHEN
r.PROP='true' AND
(type(r) <> "SENDS" OR startNode(r) = ns[acc[0]]) THEN acc[1]*1 ELSE acc[1]*0 END]
)[1] = 1
RETURN path
or maybe this is more readable
WHERE REDUCE(acc = [0,1], r IN rs |
CASE WHEN
r.PROP=true AND
(type(r) <> "SENDS" OR startNode(r) = ns[acc[0]])
THEN [acc[0]+1, acc[1]*1]
ELSE [acc[0]+1, acc[1]*0]
END
)[1] = 1
Here's a console: http://console.neo4j.org/?id=v3kgz9
For completeness I've answered the question using jjaderberg correct solution plus a condition to fix the start node and ensure that no zero length paths are included
MATCH p = (n)-[*1..10]-(m)
WHERE ALL(n in nodes(p) WHERE 1=length(filter(m in nodes(p) WHERE m=n)))
AND (id(n)=1)
WITH p, nodes(p) as ns, relationships(p) as rs
WHERE REDUCE(acc = [0,1], r IN rs | [acc[0]+1,
CASE WHEN r.PROP='true' AND (type(r) <> "SEND" OR startNode(r) = ns[acc[0]])
THEN acc[1]*1
ELSE acc[1]*0
END])[1] = 1
RETURN nodes(p);
Or my alternative answer based on jjaderbags answer but does not use accumulator but which is slightly slower
MATCH p=(n)-[rels*1..10]-(m)
WHERE ALL(n in nodes(p) WHERE 1=length(filter(m in nodes(p) WHERE m=n)))
AND( ALL (r IN rels WHERE r.PROP='true')
AND id(n)=1)
WITH p, range(0,length(p)-1) AS idx, nodes(p) as ns, relationships(p) as rs
WHERE ALL (i in idx WHERE
CASE type(rs[i])='SEND'
WHEN TRUE THEN startnode(rs[i])=ns[i]
ELSE TRUE
END)
RETURN nodes(p);

Resources