Neo4j Optional Relationship Match - neo4j

I want to write a query that returns a node (a), the nodes that are directly adjacent to it (b), and then all nodes that connect to (b) but not those nodes that have already been identified as (b).
So... If my graph was:
d
/
a<--b
\
c
I want to return { a, [b], [c, d] }.
So far, I have the following query (the 'prop' attribute distinguishes each node from each other):
MATCH (a)<-[:something]-(b)<-[:something*0..]<-(c)
WHERE NOT (c.prop IN b.prop)
RETURN a.prop, collect(b.prop), collect (c.prop)
If my graph looks like:
a<--b
I expect the result to be { a, [b], [] } but instead I get nothing back, most likely due to c.prop being in b.prop. I tried using the OPTIONAL MATCH but that did not work either:
MATCH (a)<-[:something]-(b)
OPTIONAL MATCH (a)<-[:something]<-(b)<-[:something*0..]<-(c)
WHERE NOT (c.prop IN b.prop)
RETURN a.prop, collect(b.prop), collect (c.prop)
Any way to get the intended results?

When I run the following query:
MATCH (n:Crew)-[r:LOVES*]->m
OPTIONAL MATCH (m:Crew)-[r2:KNOWS*]->o
WHERE n.name='Neo' AND NOT (o.name IN m.name)
RETURN n,m,o
in http://console.neo4j.org/, on the sample graph, I get Neo and Trinity, even though Trinity knows nobody (o is empty). I think the OPTIONAL MATCH only needs to contain the actual optional part of your traversal, whereas in your code you have everything. The (a)<-[:something]<-(b) should not appear there, only (b)<-[:something*0..]<-(c)

Related

Neo4j query for getting multiple connected nodes

In my graph, I want to get the first-degree, second-degree, and third-degree neighbors of a certain node. If my graph is A -> B -> C -> D -> E, then
first-degree neighbor of C is B
second-degree neighbor of C is A
third-degree neighbor of C is none
When checking neighbors, I go in the reverse direction of the edge. To get these nodes, I wrote the following query.
MATCH (changedNode: Function) WHERE changedNode.signature IN [...]
MATCH (neig1: Function)-[:CALLS]->(changedNode)
MATCH (neig2: Function)-[:CALLS]->(neig1)
MATCH (neig3: Function)-[:CALLS]->(neig2)
RETURN DISTINCT neig1.functionName, neig2.functionName, neig3.functionName
I realized that this code does not return B as the first-degree neighbor of C since A does not have any neighbors(neig3 is empty). In other words, this query requires a node to have a third-degree neighbor. I understood this but could not update my code. How should I revise my query?
You can use OPTIONAL MATCH since A may not have a neighbor. Then the query will return a null value for neigh3.
MATCH (changedNode: Function) WHERE changedNode.signature IN [...]
MATCH (neig1: Function)-[:CALLS]->(changedNode)
OPTIONAL MATCH (neig2: Function)-[:CALLS]->(neig1)
OPTIONAL MATCH (neig3: Function)-[:CALLS]->(neig2)
RETURN DISTINCT neig1.functionName, neig2.functionName, neig3.functionName
Use of OPTIONAL MATCH matches patterns against your graph database, just like a MATCH does. The difference is that if no matches are found, OPTIONAL MATCH will use a null for missing parts of the pattern. OPTIONAL MATCH could be considered the Cypher equivalent of the outer join in SQL.
MATCH (changedNode: Function) WHERE changedNode.signature IN [...]
MATCH (neig1: Function)-[:CALLS]->(changedNode)
OPTIONAL MATCH (neig2: Function)-[:CALLS]->(neig1)
OPTIONAL MATCH (neig3: Function)-[:CALLS]->(neig2)
RETURN DISTINCT neig1.functionName, neig2.functionName, neig3.functionName
For more explanation, visit: https://neo4j.com/developer/kb/a-note-on-optional-matches/

Neo4j if a node has no outgoing edges delete that node otherwise return its next nodes

I'm trying to use Neo4j Cypher to implement the following function: given a node, check if it has any outgoing edges with a specific relationship type. If so, return the nodes it can reach out by those edges, otherwise delete this node. And my code is like this
MATCH (m:Node{Properties})
WITH (size((m)-[:type]->(:Node))) AS c,m
WHERE c=0
DETACH DELETE m
However I don't know how to apply the if/else condition here, and this code only implements part of what I need. I'd really appreciate your help and suggestions!
For example the database is like this:
A-[type]->B
A-[type]->C
If the original node is A and it has two edges with that type to B and C, then I want the query to return B and C as result.
If the original node is B, it should be deleted because there's no such outgoing edge from B.
[UPDATED]
The following query uses a FOREACH hack to conditionally delete m, and returns either the found n nodes, or NULL if there were none.
OPTIONAL MATCH (m:Node {...Properties...})-[:type]->(n:Node)
FOREACH(x IN CASE WHEN n IS NULL THEN [1] END | DETACH DELETE m)
RETURN n
You could also use the APOC procedure apoc.do.when instead of the FOREACH hack:
OPTIONAL MATCH (m:Node {...Properties...})-[:type]->(n:Node)
CALL apoc.do.when(n IS NULL, 'DETACH DELETE m', '', {m: m}) YIELD value
RETURN n

match a branching path of variable length

I have a graph which looks like this:
Here is the link to the graph in the neo4j console:
http://console.neo4j.org/?id=av3001
Basically, you have two branching paths, of variable length. I want to match the two paths between orange node and yellow nodes. I want to return one row of data for each path, including all traversed nodes. I also want to be able to include different WHERE clauses on different intermediate nodes.
At the end, i need to have a table of data, like this:
a - b - c - d
neo - morpheus - null - leo
neo - morpheus - trinity - cypher
How could i do that?
I have tried using OPTIONAL MATCH, but i can't get the two rows separately.
I have tried using variable length path, which returns the two paths but doesn't allow me to access and filter intermediate nodes. Plus it returns a list, and not a table of data.
I've seen this question:
Cypher - matching two different possible paths and return both
It's on the same subject but the example is very complex, a more generic solution to this simpler problem is what i'm looking for.
You can define what your end node by using WHERE statement. So in your case end node has no outgoing relationship. Not sure why you expect a null on return as you said neo - morpheus - null - leo
MATCH p=(n:Person{name:"Neo"})-[*]->(end) where not (end)-->()
RETURN extract(x IN nodes(p) | x.name)
Edit:
may not the the best option as I am not sure how to do this programmatically. If I use UNWIND I get back only one row. So this is a dummy solution
MATCH p=(n{name:"Neo"})-[*]->(end) where not (end)-->()
with nodes(p) as list
return list[0].name,list[1].name,list[2].name,list[3].name
You can use Cypher to match a path like this MATCH p=(:a)-[*]->(:d) RETURN p, and p will be a list of nodes/relationships in the path in the order it was traversed. You can apply WHERE to filter the path just like with node matching, and apply any list functions you need to it.
I will add these examples too
// Where on path
MATCH p=(:a)-[*]-(:d) WHERE NONE(n in NODES(p) WHERE n.name="Trinity") WITH NODES(p) as p RETURN p[0], p[1], p[2], p[3]
// Spit path into columns
MATCH p=(:a)-[*]-(:d) WITH NODES(p) as p RETURN p[0], p[1], p[2], p[3]
// Match path, filter on label
MATCH p=(:a)-[*]-(:d) WITH NODES(p) as p RETURN FILTER(n in p WHERE "a" in LABELS(n)) as a, FILTER(n in p WHERE "b" in LABELS(n)) as b, FILTER(n in p WHERE "c" in LABELS(n)) as c, FILTER(n in p WHERE "d" in LABELS(n)) as d
Unfortunately, you HAVE to explicitly set some logic for each column. You can't make dynamic columns (that I know of). In your table example, what is the rule for which column gets 'null'? In the last example, I set each column to be the set of nodes of a label.
I.m.o. you're asking for extensive post-processing of the results of a simply query (give me all the paths starting from Neo). I say this because :
You state you need to be able to specify specific WHERE clauses for each path (but you don't specify which clauses for which path ... indicating this might be a dynamic thing ?)
You don't know the size of the longest path beforehand ... but you still want the result to be a same-size-for-all-results table. And would any null columns then always be just before the end node ? Why (for that makes no real sense other then convenience) ?
...
Therefore (and again i.m.o.) you need to process the results in a (Java or whatever you prefer) program. There you'll have full control over the resultset and be able to slice and dice as you wish. Cypher (exactly like SQL in fact) can only do so much and it seems that you're going beyond that.
Hope this helps,
Regards,
Tom
P.S. This may seem like an easy opt-out, but look at how simple your query is as compared to the constructs that have to be wrought trying to answer your logic. So ... separate the concerns.

Cypher Query where 2 different labels do not contain a relationship to a 3rd label/node

I have 3 labels, A, B, and Z. A & B both have a relationship to Z. I want to find all the A nodes that do not have share any of nodes Z in common with B
Currently, doing a normal query where the relationship DOES exist, works.
MATCH (a:A)-[:rel1]->(z:Z)<-[:rel2]-(b:B { uuid: {<SOME ID>} })
RETURN DISTINCT a
But when I do
MATCH (a:A)
WHERE NOT (a)-[:rel1]->(z:Z)<-[:rel2]-(b:B { uuid: {<SOME ID>} }))
RETURN DISTINCT a
It throws an error
Neo4j::Server::CypherResponse::ResponseError: z not defined
Not sure if the syntax for this is incorrect, I tried WHERE NOT EXIST() but no luck.
The query is part of a larger one called through a rails app using neo4jrb / (Neo4j::Session.query)
This is a problem to do with the scope of your query. When you describe a node in a MATCH clause like the below
MATCH (n:SomeLabel)
You're telling cypher to look for a node with the label SomeLabel, and assign it to the variable n in the rest of the query, and at the end of the query, you can return the values stored in this node using RETURN n (unless you drop n by not including it in a WITH clause).
Later on in you query, if you want to MATCH another node, you can do it in reference to n, so for example:
MATCH (m:SomeOtherLabel)-[:SOME_RELATIONSHIP]-(n)
Will match a variable connected (in any direction) to the node n, with a label SomeOtherLabel, and assign it to the variable m for the rest of the query.
You can only assign nodes to variables like this in MATCH, OPTIONAL MATCH, MERGE, CREATE and (sort of) in WITH and UNWIND clauses (someone correct me here if I've missed one, I suppose you also do this in list comprehensions and FOREACH clauses).
In your second query, you are trying to find a node with the label A, which is not connected to a node with the label Z. However, the way you have written the query means that you are actually saying find a node with label A which is not connected via a rel1 relationship to the node stored as z. This will fail (and as shown, neo complains that z is not defined), because you can't create a new variable like this in the WHERE clause.
To correct your error, you need to remove the reference to the variable z, and ensure you have also defined the variable b containing your node before the WHERE clause. Now, you keep the label in the query, like the below.
MATCH (a:A)
MATCH (b:B { uuid: {<SOME ID>} })
WHERE NOT (a)-[:rel1]->(:Z)<-[:rel2]-(b) // changed this line
RETURN DISTINCT a
And with a bit of luck, this will now work.
You get the error because z is the identifier of a node that you are using in a where clause that you have not yet identified.
Since you know b already I would match it first and then use it in your where clause. You don't need to assign :Z an identifier, simply using the node label will suffice.
MATCH (b:B { uuid: {<SOME ID>} })
WITH b
MATCH (a:A)
WHERE NOT (a)-[:rel1]->(:Z)<-[:rel2]-(b)
RETURN DISTINCT a

How can I make the following Cypher query have the results that setting auto-complete control does?

I am doing the following Cypher query:
MATCH (a) WHERE a.itemId IN ['Q2', 'Q24', 'Q30', 'Q23', 'Q16', 'Q20'] RETURN a
I'd like it to return the same results as when I set the Auto-Complete switch. This question was asked and answered 1 but I don't understand the answer. Can this be performed in one query, and if so, what would the modified query be?
Yeah, you should be able to do it all in one query. To get the nodes in question along with their relationships you can do:
MATCH (a)
WHERE a.itemId IN ['Q2', 'Q24', 'Q30', 'Q23', 'Q16', 'Q20']
OPTIONAL MATCH (a)-[rel]-()
RETURN a, collect(rel)
That will return you, for each node, an array of the relationships.
Depending on your library that you're using for Neo4j, that may or may not return you something useful to give you the startnode/endnode. You could also do:
MATCH (a)
WHERE a.itemId IN ['Q2', 'Q24', 'Q30', 'Q23', 'Q16', 'Q20']
OPTIONAL MATCH (a)-[rel]-()
RETURN a, collect({rel: rel, start_node: startNode(rel), end_node: endNode(rel)})
That would give you an array for each node containing objects/maps of the data.
Got some separate clarification on the question about how to only return the relationships between the nodes that are matched. This should be possible like so:
MATCH (a)
WHERE a.itemId IN {itemIds}
OPTIONAL MATCH (a)-[rel]-(b)
WHERE b.itemId IN {itemIds}
RETURN a, collect(rel)
Again, you also might want to return the startNode/endNode of the relationships

Resources