Shortest paths excluding one node - neo4j

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

Related

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

Neo4j: Find nodes with property name that contains string (in the property name, not the property value)

Is there a way to find all nodes with properties that have a certain string?
Eg here with "ID":
match (n) where exists( n[".*"+"ID"]) return n
(this does not work).
Thanks!
This will give you just the keys.
MATCH (n) WHERE ANY(x IN KEYS(n) WHERE x =~".*ID") RETURN n, KEYS(n) AS myKeys
This will give you just the values.
MATCH (n) WHERE ANY(x IN KEYS(n) WHERE x =~".*ID")
RETURN n, [x IN KEYS(n) WHERE x =~".*ID" | n[x]] AS myValues
If you have apoc, this will give you the keys and the values.
MATCH (n) WHERE ANY(x IN KEYS(n) WHERE x =~".*ID")
WITH n, [x IN KEYS(n) WHERE x =~".*ID" | x] AS myKeys
RETURN id(n) AS nodeId, apoc.map.submap(n, myKeys) as submap

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

neo4j cypher, return fields instead of nodes when collecting

Given the following query
match (a)-->(b)-->(c)
where id(a) = 0
return b.name, collect(c) as cs
How it's possible to return a collection composed of only a couple of fields, instead of the whole nodes?
A single field is easy:
match (a)-->(b)-->(c)
where id(a) = 0
return b.name, collect(c.fieldName) as cs
For multiple field names, maybe concatenate them?
match (a)-->(b)-->(c)
where id(a) = 0
return b.name, collect(c.fieldName1 + delimiter + c.fieldName2) as cs
match (a)-->(b)-->(c)
where id(a) = 0
return b.name, collect( { field1: c.field1, field2: c.field2 } ) as cs

Resources