NEO4J: find a node according to multiple relationships - neo4j

I hope to find a node according to multiple relationship to other nodes.
For example, find a movie acted by actor A, directed by B and filmed by C.
Can anyone tell me how to do that?
Perhaps START would do that but since it needs legacy index, I prefer match.

You should be able to string multiple matches together, such as:
MATCH (m:Movie)<-[:ACTED_IN]-(a:Actor),
(m:Movie)<-[:DIRECTED]-(d:Director),
(m:Movie)<-[:FILMED_BY]-(f:Filmer)
or:
MATCH (m:Movie)<-[:ACTED_IN]-(a:Actor)
MATCH (m:Movie)<-[:DIRECTED]-(d:Director)
MATCH (m:Movie)<-[:FILMED_BY]-(f:Filmer)
Note: I haven't tested this, but I believe both styles should work. And... for brevity, I left out details such as specifying actor/director/filmer name, and the RETURN portion. (and I made an assumption you were using labels; again, just an example on how to accomplish this).

Related

Cypher return multiple hops through pattern of relationships and nodes

I'm making a proof of concept access control system with neo4j at work, and I need some help with Cypher.
The data model is as follows:
(:User|Business)-[:can]->(:Permission)<-[:allows]-(:Business)
Now I want to get a path from a User or a Business to all the Business-nodes that you can reach trough the
-[:can]->(:Permission)<-[:allows]-
pattern. I have managed to write a MATCH that gets me halfway there:
MATCH
path =
(:User {userId: 'e96cca53-475c-4534-9fe1-06671909fa93'})-[:can|allows*]-(b:Business)
but this doesn't have any directions, and I can't figure out how to include the directions without reducing the returned matches to only the direct matches (i.e it doesn't continue after the first hit on a :Business node)
So what I'm wondering is:
Is there a way to match multiple of these hops in one query?
Should I model this entirely different?
Am I on the wrong path completely and the query should be completely
rewritten
Currently the syntax of variable-length expansions doesn't allow fine control for separate directions for certain types. There are improvements in the pipeline around this, but for the moment Cypher alone won't get you what you want.
We can use APOC Procedures for this, as fine control of the direction of types in the expansion, and sequences of relationships, are supported in the path expander procs.
First, though, you'll need to figure out how to address your user-or-business match, either by adding a common label to these nodes by which you can MATCH either type by property, or you can use a subquery with two UNIONed queries, one for :Business nodes, the other for :User nodes, that way you can still take advantage of an index on either, and get possible results in a single variable.
Once you've got that, you can use apoc.path.expandConfig(), passing some options to get what you want:
// assume you've matched to your `start` node already
CALL apoc.path.expandConfig(start, {relationshipFilter:'can>|<allows', labelFilter:'>Business'}) YIELD path
RETURN path
This one doesn't use sequences, but it does restrict the direction of expansion per relationship type. We are also setting the labelFilter such that :Business nodes are the end node of the path and not nodes of any other label.
You can specify the path as follows:
MATCH path = (:User {userId: $id})-[:can]->(:Permission)
<-[:allows]-(:Business))
RETURN path
This should return the results you're after.
I see a good solution has been provided via path expanding APOC procedures.
But I'll focus on your point #2: "Should I model this entirely differently?"
Well, not entirely but I think yes.
The really liberating part of working with Neo4j is that you can change the road you are driving over as easily as you can change your driving strategy: model vs query. And since you are at an early stage in your project, you can experiment with different models. There's a good opportunity to make just a semantic change to make an 'end run' around the problem.
The semantics of a relationship in Neo4j are expressed through
the mandatory TYPE you assign to the relationship, combined with
the direction you choose to point the mandatory arrow
The trick you solved with APOC was how to traverse a path of relationships that alternate between pointing forward and backward along the query's path. But before reaching for a power tool, why not just reverse the direction of either of your relationship types. You can change the model for allows from
<-[:allows]-
to
-[:is_allowed_by]->
and that buys you a lot. Now the directions of both relationships are the same and you can combine both relationships into a single relationship in the match pattern. And the path traversal can be expressed like this, short & sweet:
(u:User)-[:can|is_allowed_by*]->(c:Company)
That will literally go to all lengths to find every user-to-company path, branching included.

Neo4j cyper query: How to travese

I am trying to learn neo4j, so I just took a use case of a travel app to learn but I am not sure about the optimal way to solve it. Any help will be appreciated.
Thanks in advance.
So consider a use case in which I have to travel from one place (PLACE A) to other (PLACE C) by train, but there is no direct connection between the two places. And so we have to change our train in PLACE B.
Two places are connected via a relation IS_CONNECTED relation. refering to green nodes in the image
And then if there is an is_connected relation between two place then there will be an out going relation i.e. CONNECTED_VIA to a common train from both the node which implies how they are connected referring to red nodes in image
my question is how are we suppose to know that we have to change the station from place b
My understanding is:
We will check where the two places are connected via IS_CONNECTED relationship
match (start:place{name:"heidelberg"}), (end:place{name:"frankfurt"})
MATCH path = (start)-[:IS_CONNECTED*..]->(end)
RETURN path
this will show that these two places are connected
Then we will see that if place A and place c are directly connected or not by the query
match (p:place{name:"heidelberg"})-[:CONNECTED_VIA]->(q)<-[:CONNECTED_VIA]-(t:place{name:"frankfurt"})
return q
And this will return nothing because there is no direct connections
My brain stopped functioning after this. I am trying to figure how from past 3 days. I am sorry I look ao confused
Please click here for the image of what i am referring
You'll want to use variable-length relationships in your :CONNECTED_VIA match, and then get the :Place nodes that are in your path. And it's usually a good idea to use an upper bound, whatever makes sense in your graph.
Then we can use a filter on the nodes in your path to only keep the ones that are :Place nodes.
match path = (p:place{name:"heidelberg"})-[:CONNECTED_VIA*..4]-(t:place{name:"frankfurt"})
return path, [node in nodes(path)[1..-1] where node:Place] as connectionPlaces
And if you're only interested in the shortest paths, you may want to check the shortestPath() or shortestPaths() functions.
One last thing to note...when determining if two locations are connected, if all you need is a true or false if they're connected, you can use the EXISTS() function to return whether such a pattern exists:
match (start:place{name:"heidelberg"}), (end:place{name:"frankfurt"})
return exists((start)-[:IS_CONNECTED*..5]->(end))

follow all relationships but specific ones

How can I tell cypher to NOT follow a certain relationship/edge?
E.g. I have a :NODE that is connected to another :NODE via a :BUDDY relationship. Additionally every :NODE is related to :STUFF in arbitrary depth by arbitrary edges which are NOT of type :BUDDY. I now want to add a shortcut relation from each :NODE to its :STUFF. However, I do not include :STUFF of its :BUDDIES.
(:NODE)-[:BUDDY]->(:NODE)
(:NODE)-[*]->(:STUFF)
My current query looks like this:
MATCH (n:Node)-[*]->(s:STUFF) WHERE NOT (n)-[:BUDDY]->()-[*]->(s) CREATE (n)-[:HAS]->(s)
However I have some issues with this query:
1) If I ever add a :BUDDY relationship not directly between :NODE but children of :NODE the query will use that relationship for matching. This might not be intended as I do not want to include buddies at all.
2) Explain tells me that neo4j does the match (:NODE)-[*]->(:STUFF) and then AntiSemiApply the pattern (n)-[:BUDDY]->(). As a result it matches the whole graph to then unmatch most of the found connections. This seems ineffective and the query runs slower than I like (However subjective this might sound).
One (bad) fix is to restrict the depth of (:NODE)-[*]->(:STUFF) via (:NODE)-[*..XX]->(:STUFF). However, I cannot guarantee that depth unless I use a ridiculous high number for worst case scenarios.
I'd actually just like to tell neo4j to just not follow a certain relationship. E.g. MATCH (n:NODE)-[ALLBUT(:BUDDY)*]->(s:STUFF) CREATE (n)-[:HAS]->(s). How can I achieve this without having to enumerate all allowed connections and connect them with a | (which is really fast - but I have to manually keep track of all possible relations)?
One option for this particular schema is to explicitly traverse past the point where the BUDDY relationship is a concern, and then do all the unbounded traversing you like from there. Then you only have to apply the filter to single-step relationships:
MATCH (n:Node) -[r]-> (leaf)
WHERE NOT type(r) = 'BUDDY'
WITH n, leaf
MATCH (leaf) -[*] -> (s:Stuff)
WITH n, COLLECT(DISTINCT leaf) AS leaves, COLLECT(DISTINCT s) AS stuff
RETURN n, [leaf IN leaves WHERE leaf:Stuff] + stuff AS stuffs
The other option is to install apoc and take a look at the path expander procedure, which allows you to 'blacklist' node labels (such as :Node) from your path query, which may work just as well depending on your graph. See here.
My final solution is generating a string from the set of
{relations}\{relations_id_do_not_want}
and use that for matching. As I am using an API and thus can do this generation automatically it is not as much as an inconvenience as I feared, but still an inconvenience. However, this was the only solution I was able to find since posting this question.
You could use a condition o nrelationship type:
MATCH (:Node)-[r:REL]->(:OtherNode)
WHERE NOT type(r) = 'your_unwanted_rel_type'
I have no clues about perf though

Kevin path at neo4j

I am trying to discover how many hops I have to do to know a friend with Cipher. I have these relationships.
(Gutierrez)-[:Conhece]->(Felipe),
(Felipe)-[:Conhece]->(Gutierrez),
(Felipe)-[:Conhece]->(Fernando),
(Fernando)-[:Conhece]->(Felipe),
(Fernando)-[:Conhece]->(Pedro),
(Pedro)-[:Conhece]->(Fernando),
(Pedro)-[:Conhece]->(Arthur),
(Arthur)-[:Conhece]->(Pedro),
(Arthur)-[:Conhece]->(Vitor),
(Vitor)-[:Conhece]->(Arthur),
and when I execute my query it shows Fernando. What I want is to show only Vitor, Pedro and Arthur.
MATCH (n:Leitor)-[r:Conhece]-m
WHERE n.nome='Pedro' OR m.nome='Vitor'
RETURN n,r,m
with my Bacon Path query ->
Ok, I'm adding another answer because I think I understand what you want and it's different than my other answer.
If you want to find everybody that both Pedro and Vitor have met (in this case, just Author):
MATCH (pedro:Leitor)-[:Conhece]-(in_common:Leitor)-[:Conhece]-(vitor:Leitor)
WHERE pedro.nome='Pedro' AND vitor.nome='Vitor'
RETURN in_common
That also might look a bit better like this:
MATCH (pedro:Leitor {nome: 'Pedro'})-[:Conhece]-(in_common:Leitor)-[:Conhece]-(vitor:Leitor {name: 'Vitor'})
RETURN in_common
I also notice from your screenshots that every meeting has two relationships. That may very well be what you want, but if your plan was to always have two relationships whenever two people meet then you can get away with just one relationship. When you query bidirectionally (that is, without specifying direction like in the queries above) then you'll get relationships in either direction.
Normally you only want relationships going in both directions if the direction is important. That could be because your just recording that it goes from one node to another, or it could be because you're storing different values on the relationships.
Here you go:
MATCH shortestPath((n:Leitor)-[rels:Conhece*]-m)
WHERE n.nome IN ['Pedro', 'Vitor']
RETURN n,rels,m,length(rels)
In this case rels will be a collection of relationships because the path is variable length. You can also do:
MATCH path=shortestPath((n:Leitor)-[rels:Conhece*]-m)
WHERE n.nome IN ['Pedro', 'Vitor']
RETURN n,rels,m,length(rels),path,length(path)

Neo4j: Multiple Relationship Combinations Between Nodes

I've been playing around with Neo4j and have a problem for which I do not have a solution, hence my question here.
For my particular problem I'll describe a simplified version that captures the essence. Suppose I have a graph of locations that are connected either directly or via a detour:
direct: (A)-[:GOES_TO]->(B)
indirect: (A)->[:GOES_THROUGH]->(C)-[:COMES_BACK_TO]->(B)
If I want to have everything between "Go" and the "Finish" with a GOES_TO relationship I can easily use the Cypher query:
START a=node:NODE_IDX(Id = "Go"), b=node:NODE_IDX(Id = "Finish)
MATCH a-[r:GOES_TO*]->b
RETURN a,r,b
Here, NODE_IDX is an index on the nodes (Id).
Where I get stuck is when I want to have all the paths between "Go" and "Finish" that are not GOES_TO relationships but rather multiple GOES_THROUGH-->()-->COMES_BACK_TO relationship combinations (of variable depth).
I do not want to filter out the GOES_TO relationships because there are many more relationships among the nodes, and I do not want to accommodate removing all of them (dynamically). Is it possible to have a variable-depth, multi-relationship MATCH that I envisage?
Thanks!
Let me restate what I believe is being asked.
"If there is a path of the form (a)-[:X]->(b), find all other paths from a to b."
The answer is simple:
MATCH p=(a)-[:X]->(b), q=(a)-[r*]->(b)
WHERE p<>q
RETURN r;

Resources