Neo4j Cypher query and index of element in the collection - neo4j

I'm trying to find index number of Decision by {decisionGroupId}, {decisionId} and {criteriaIds}
This is my current Cypher query:
MATCH (dg:DecisionGroup)-[:CONTAINS]->(childD:Decision)
WHERE dg.id = {decisionGroupId}
OPTIONAL MATCH (childD)-[vg:HAS_VOTE_ON]->(c:Criterion)
WHERE c.id IN {criteriaIds}
WITH childD, vg.avgVotesWeight as weight, vg.totalVotes as totalVotes
ORDER BY weight DESC, totalVotes DESC
WITH COLLECT(childD) AS ps
RETURN REDUCE(ix = -1, i IN RANGE(0, SIZE(ps)-1)
| CASE ps[i].id WHEN {decisionId} THEN i ELSE ix END) AS ix
I have only 3 Decision in the database but this query returns the following indices:
2
3
4
while I expecting something like(starting from 0 and -1 if not found)
0
1
2
What is wrong with my query and how to fix it?
UPDATED
This query is working fine with COLLECT(DISTINCT childD) AS ps:
MATCH (dg:DecisionGroup)-[:CONTAINS]->(childD:Decision)
WHERE dg.id = {decisionGroupId}
OPTIONAL MATCH (childD)-[vg:HAS_VOTE_ON]->(c:Criterion)
WHERE c.id IN {criteriaIds}
WITH childD, vg.avgVotesWeight as weight, vg.totalVotes as totalVotes
ORDER BY weight DESC, totalVotes DESC
WITH COLLECT(DISTINCT childD) AS ps
RETURN REDUCE(ix = -1, i IN RANGE(0, SIZE(ps)-1)
| CASE ps[i].id WHEN {decisionId} THEN i ELSE ix END) AS ix
Please help me to refactor this query and get rid of heavy REDUCE.

Let's try to get the reduce part right with a simpler query:
WITH ['a', 'b', 'c'] AS ps
RETURN
reduce(ix = -1, i IN RANGE(0, SIZE(ps)-1) |
CASE ps[i] WHEN 'b' THEN i ELSE ix END) AS ix
)
As I stated in the comments, it is usually better to avoid reduce if possible. So, to express the same using a list comprehension, use WHERE for filtering.
WITH ['a', 'b', 'c'] AS ps
RETURN [i IN RANGE(0, SIZE(ps)-1) WHERE ps[i] = 'b'][0]
The list comprehension results in a list with a single element, and we will use the [0] indexer to select that element.
After adapting this to your query, we'll get something like this:
MATCH (dg:DecisionGroup)-[:CONTAINS]->(childD:Decision)
WHERE dg.id = {decisionGroupId}
OPTIONAL MATCH (childD)-[vg:HAS_VOTE_ON]->(c:Criterion)
WHERE c.id IN {criteriaIds}
WITH childD, vg.avgVotesWeight as weight, vg.totalVotes as totalVotes
ORDER BY weight DESC, totalVotes DESC
WITH COLLECT(DISTINCT childD) AS ps
RETURN [i IN RANGE(0, SIZE(ps)-1) WHERE ps[i].id = {decisionId}][0]

If you have APOC installed, you can also use the function:
return apoc.coll.indexOf([1,2,3],2)

Related

Combine two cypher queries

Currently this is the data stored in the database
Org Name Org ID
A 1
B 2
C 5
D 9
I'm trying to combine these 2 queries:
MATCH (n:Org)
WHERE n.id in [1,2]
RETURN n.name as group1_name, n.id as group1_id
MATCH (n:Org)
WHERE n.id in [5,9]
RETURN n.name as group2_name, n.id as group2_id
I need the result to be shown like this:
group1_id group1_name group2_id group1_name
1 A 5 C
2 B 9 D
Assuming the two id lists are always the same size (in your example, 2), here is one approach (assuming you also want the id values sorted in ascending order):
MATCH (n:Org)
WHERE n.id in [1, 2]
WITH n ORDER BY n.id
WITH COLLECT(n) AS ns
MATCH (m:Org)
WHERE m.id in [5, 9]
WITH ns, m ORDER BY m.id
WITH ns, COLLECT(m) AS ms
UNWIND [i IN RANGE(0, SIZE(ns)-1) | {a: ns[i], b: ms[i]}] AS row
RETURN
row.a.id as group1_id, row.a.name as group1_name,
row.b.id as group2_id, row.b.name as group2_name
And here is a simpler approach:
WITH [1, 2] AS xs, [5, 9] AS ys
UNWIND RANGE(0, SIZE(xs)-1) AS i
MATCH (n:Org), (m:Org)
WHERE n.id = xs[i] AND m.id = ys[i]
RETURN n.id as group1_id, n.name as group1_name, m.id as group2_id, m.name as group2_name
And finally, if the xs and ys lists are passed to the query as parameters:
UNWIND RANGE(0, SIZE($xs)-1) AS i
MATCH (n:Org), (m:Org)
WHERE n.id = $xs[i].id AND m.id = $ys[i].y
RETURN n.id as group1_id, n.name as group1_name, m.id as group2_id, m.name as group2_name

Is there a way i can return all the nodes their relationship and it's properties for the following query

I want to get all the list of distinct nodes and relationship that I am getting through this query.
MATCH (a:Protein{name:'9606.ENSP00000005995'})-[r:ON_INTERACTION_WITH]-(b:Protein)-[d:ON_INTERACTION_WITH]-(c:Protein)
Return a,b,c,d,r
limit 10
This should work:
MATCH (a:Protein{name:'9606.ENSP00000005995'})-[r:ON_INTERACTION_WITH]-(b:Protein)-[d:ON_INTERACTION_WITH]-(c:Protein)
WITH * LIMIT 10
RETURN
COLLECT(DISTINCT a) AS aList,
COLLECT(DISTINCT b) AS bList,
COLLECT(DISTINCT c) AS cList,
COLLECT(DISTINCT r) AS rList,
COLLECT(DISTINCT d) AS dList

Shortest paths excluding one node

I am working on a query on the movie database to check the shortest paths between n nodes. In this simplified example, we want all the shortest paths between 2 movies:
match p=allShortestPaths((n)-[*]-(m)) where id(n) = 87 and id(m) = 121
return p;
Now I want to have all the shortest paths that don't include Keanu Reeves in it.
I tried this:
match p=allShortestPaths((n)-[*]-(m)) where id(n) = 87 and id(m) = 121 and NONE(n in nodes(p) where n.name = "Keanu Reeves")
return p;
This however takes an eternity to load, even after I have indexed the name field of Person...
Then In tried the following:
match p=allShortestPaths((n)-[*]-(m)) where id(n) = 87 and id(m) = 121
with p WHERE NONE(n in nodes(p) where n.name = "Keanu Reeves")
return p;
This however gives me no results. I misinterpreted this by thinking it would just return those paths which don't have Keanu Reeves in between:
(no changes, no records)
I tested if I could get only those with Keanu Reeves in between with the any() function. This works perfectly:
match p=allShortestPaths((n)-[*]-(m)) where id(n) = 87 and id(m) = 121
with p WHERE ANY(n in nodes(p) where n.name = "Keanu Reeves")
return p;
What is the best approach to tackle this? I must say that my production query is way more complex than this, but it all boils down to this problem. It has to be a performant solution.
The problem is that if one of the nodes from the path does not have the property name, then the entire check will not be passed.
So or do we check for the existence of a property:
match p=allShortestPaths((n)-[*]-(m))
where
n.title = 'The Replacements' and
m.title = 'Speed Racer' and
NONE(n in nodes(p) where EXISTS(n.name) and n.name = "Keanu Reeves")
return p
Or use the COALESCE function:
match p=allShortestPaths((n)-[*]-(m))
where
n.title = 'The Replacements' and
m.title = 'Speed Racer' and
NONE(n in nodes(p) where COALESCE(n.name, '') = "Keanu Reeves")
return p

Cypher Query fail while defining the relationship count

Neo4j Version: 3.0.4
Objective of the below query is to eliminate duplicate bus service and bustop in a path, it work fine if i didn't provide the relationship count -[r:CONNECTSWITH]-> but if the relationship count defined -[r:CONNECTSWITH*..3]-> ,then its throwing
Key not found: r
Working:
OPTIONAL MATCH p=(o:PORT{name:"busstop1"})-[r:CONNECTSWITH]->(d:PORT{name:"busstop2"})
WHERE ALL(r1 IN rels(p)
WHERE 1 = size(filter(r2 IN rels(p) WHERE (r1.service = r2.service))))
AND ALL(n IN nodes(p) WHERE 1 = size(filter(m IN nodes(p) WHERE id(m) = id(n))))
RETURN p
LIMIT 10
Not Working:
OPTIONAL MATCH p=(o:PORT{name:"busstop1"})-[r:CONNECTSWITH*..3]->(d:PORT{name:"busstop2"})
WHERE ALL(r1 IN rels(p)
WHERE 1 = size(filter(r2 IN rels(p) WHERE (r1.service = r2.service))))
AND ALL(n IN nodes(p) WHERE 1 = size(filter(m IN nodes(p) WHERE id(m) = id(n))))
RETURN p
LIMIT 10
Work around Solution:
OPTIONAL MATCH p=(o:PORT{name:"busstop1"})-[r:CONNECTSWITH*..3]->(d:PORT{name:"busstop2"})
WHERE ALL(r1 in rels(p)
WHERE 1 = size(filter(r2 IN rels(p) WHERE (r1.service = r2.service)))) =
ALL(n IN nodes(p) WHERE 1 = size(filter(m IN nodes(p) WHERE id(m) = id(n))))
AND ALL(r1 in rels(p)
WHERE 1 = size(filter(r2 IN rels(p) WHERE (r1.service = r2.service))))
RETURN p
LIMIT 10
Aside: This feels like a neo4j bug. If you are encountering this with the latest neo4j version, you may want to submit a neo4j issue.
As a possible workaround, since the query does not actually use the r identifier, try removing it from the query.

Neo4j collection function error?

I am running the following query that is meant to compare two collections nodes set1 and set2. All nodes in set2 are in set1, and I would like to identify all the nodes in set1 that are NOT in set2. However, the query returns a set of nodes that includes some of the nodes in set1. I am running this query on v2.1.7. Suggestions?
Query:
MATCH p=(a:ObjectConcept{sctid:233604007})<-[:ISA*]-(b:ObjectConcept)
with nodes(p) as set1, p
MATCH q=(a:ObjectConcept{sctid:34020007})<-[:ISA*]-(b:ObjectConcept)
with nodes(q) as set2,set1, p
WHERE ALL(x in set2 WHERE NOT x in set1)
with nodes(p) as pneumo
UNWIND pneumo AS pneumolist
RETURN distinct pneumolist.FSN,pneumolist.sctid
Alternative query, same result:
Query:
MATCH p=(a:ObjectConcept{sctid:233604007})<-[:ISA*]-(b:ObjectConcept)
with nodes(p) as set1, p
MATCH q=(a:ObjectConcept{sctid:34020007})<-[:ISA*]-(b:ObjectConcept)
with nodes(q) as set2,set1, p
WHERE NONE(x in set2 WHERE x in set1)
with nodes(p) as pneumo
UNWIND pneumo AS pneumolist
RETURN distinct pneumolist.FSN,pneumolist.sctid
Your matches don't return just one row as you might expect but many rows,
and your comparison is done between the cross product of those many row combinations. You probably want to create a set for each of your two subtrees first with a combination of unwind + collect(distinct)
The code below will not be as fast, as cypher internally doesn't have a Set concept yet.
try this
MATCH p=(a:ObjectConcept{sctid:233604007})<-[:ISA*]-(b:ObjectConcept)
unwind nodes(p) as n
with collect(distinct n) as set1
MATCH q=(a:ObjectConcept{sctid:34020007})<-[:ISA*]-(b:ObjectConcept)
unwind nodes(q) as m
with collect(distinct m) as set2
WHERE NONE(x in set2 WHERE x in set1)
UNWIND set1 AS pneumolist
RETURN distinct pneumolist.FSN,pneumolist.sctid
The following query was successful, and addresses Michael's discussion regarding cross products (above).
MATCH p=(a:ObjectConcept{sctid:233604007})<-[:ISA*]-(b:ObjectConcept)
with distinct nodes(p) as set1
UNWIND set1 as x1
with collect(DISTINCT x1) as set11
MATCH q=(a:ObjectConcept{sctid:34020007})<-[:ISA*]-(b:ObjectConcept)
with distinct nodes(q) as set2,set11
UNWIND set2 as x2
with collect(distinct x2) as set22,set11
with REDUCE(pneumo=[],x in set11|case when x in set22 then pneumo else pneumo
+ [x] END) AS pneumo
return pneumo

Resources