Count the number of Relationships between two specific Nodes - Neo4j / Cypher - neo4j

I would like to input two specific nodes and return the quantity of relationships that are along the path that connect the specific nodes.
(There is only 1 path possible in every case)
In some cases, two specific nodes are related through two relationships like this:
(Tim)-[]-()-[]-(Bill)
Should return 2 (relationships).
In other cases there are more nodes between my specific start and end nodes. Like this:
(Tim)-[]-()-[]-()-[]-()-[]-(Bill)
Should return 4 (relationships).
I have two types of relationships that could exist between nodes, so I need to avoid being specific about the type of relationship if possible.
New to this and performed an extensive search before asking this question as no one seemed to discuss relationships between specific nodes...
Many thanks for your help!

This query should work:
match p = (:Person {name:'Tim'})-[*]->(:Person {name:'Bill'})
RETURN length(p)
That is: return the length() of path p.

Related

Linking two nodes together if both are connected to three other nodes?

I have a graph with two types of nodes: people nodes and products purchased, with the relationships being a purchase. I want to link two people nodes together if the two nodes share three product nodes, i.e if they have purchased three of the same things. What is the best way to go about doing this?
The graph has a few hundred million nodes. Which way will be the fastest also? In cypher I was thinking maybe something like the following, but it's taking ages and seems to be doing nothing?
MATCH path1 = (p1:PEOPLE)--(product:PRODUCT)--(p2:PEOPLE)
WHERE p1.person_id <> p2.person_id
WITH path1,p1,p2
MATCH path2=(p1)--(:PRODUCT)--(p2) WHERE nodes(path2)<>nodes(path1)
WITH path1,path2,p1,p2
MATCH path3 = (p1)--(:PRODUCT)--(p2)
WHERE nodes(path3)<>nodes(path2) and nodes(path3)<>nodes(path1)
create (p1)-[:LINK]->(p2)
create (p2)-[:LINK]->(p1)
Any suggestions much appreciated!
Instead of using multiple MATCH clauses, use the count aggregating function to check the number of common products between two nodes.
MATCH (p1:PEOPLE)--(product:PRODUCT)--(p2:PEOPLE)
WITH p1, p2, count(product) AS commonProductCount
WHERE commonProductCount >= 3
CREATE (p1)-[:LINK]->(p2)
CREATE (p2)-[:LINK]->(p1)
Note. As a rule of thumb, there is no point creating two LINK relationships in the opposite directions. You should pick a single direction and create the relationship, and use undirected relatioships such as ()-[:LINK]-() in your queries.

How to find relationships of 2 types of nodes in Neo4j?

I have two types of nodes stored in Neo4j db: Person and Group. I defined a relationship "IN" between Person and Group: (p:Person)-[:IN]->(g:Group).
The question is given 2 Person nodes, how can I find the relationships between them?
I know I can use Cypher queries such as
MATCH (p1:Person{pid: '11231'})-[:IN]->(g:Group)<-[:IN]-(p2:Person{pid: '1231231'})
RETURN p1,p2,g;
But how can I describe a multi-hop relationship so that Neo4j can find links between two not directly linked Person nodes? I don't know how many hops are required to link these two Person nodes.
You can use below Cypher query:
MATCH (p1:Person{pid: '11231'})-[:IN*]-(p2:Person{pid: '1231231'}) return p1,p2;
Note: the * in the relationship IN does the trick.
However, this is inefficient approach and you should limit the number of hops like this:
MATCH (p1:Person{pid: '11231'})-[:IN*1..5]-(p2:Person{pid: '1231231'}) return p1,p2;
More relationship match pattern can be found in the cypher refcard patterns section
https://neo4j.com/docs/cypher-refcard/current/

How to filter results by node label in neo4j cypher?

I have a graph database that maps out connections between buildings and bus stations, where the graph contains other connecting pieces like roads and intersections (among many node types).
What I'm trying to figure out is how to filter a path down to only return specific node types. I have two related questions that I'm currently struggling with.
Question 1: How do I return the labels of nodes along a path?
It seems like a logical first step is to determine what type of nodes occur along the path.
I have tried the following:
MATCH p=(a:Building)­-[:CONNECTED_TO*..5]­-(b:Bus)
WITH nodes(p) AS nodes
RETURN DISTINCT labels(nodes);
However, I'm getting a type exception error that labels() expects data of type node and not Collection. I'd like to dynamically know what types of nodes are on my paths so that I can eventually filter my paths.
Question 2: How can I return a subset of the nodes in a path that match a label I identified in the first step?
Say I found that that between (a:Building) and (d1:Bus) and (d2:Bus) I can expect to find (:Intersection) nodes and (:Street) nodes.
This is a simplified model of my graph:
(a:Building)­­--(:Street)­--­(:Street)--­­(b1:Bus)
\­­(:Street)--­­(:Intersection)­­--(:Street)--­­(b2:Bus)
I've written a MATCH statement that would look for all possible paths between (:Building) and (:Bus) nodes. What would I need to do next to filter to selectively return the Street nodes?
MATCH p=(a:Building)-[r:CONNECTED_TO*]-(b:Bus)
// Insert logic to only return (:Street) nodes from p
Any guidance on this would be greatly appreciated!
To get the distinct labels along matching paths:
MATCH p=(a:Building)-[:CONNECTED_TO*..5]-(b:Bus)
WITH NODES(p) AS nodes
UNWIND nodes AS n
WITH LABELS(n) AS ls
UNWIND ls AS label
RETURN DISTINCT label;
To return the nodes that have the Street label.
MATCH p=(a:Building)-[r:CONNECTED_TO*]-(b:Bus)
WITH NODES(p) AS nodes
UNWIND nodes AS n
WITH n
WHERE 'Street' IN LABELS(n)
RETURN n;
Cybersam's answers are good, but their output is simply a column of labels...you lose the path information completely. So if there are multiple paths from a :Building to a :Bus, the first query will only output all labels in all nodes in all patterns, and you can't tell how many paths exist, and since you lose path information, you cannot tell what labels are in some paths but not others, or common between some paths.
Likewise, the second query loses path information, so if there are multiple paths using different streets to get from a :Building to a :Bus, cybersam's query will return all streets involved in all paths. It is possible for it to output all streets in your graph, which doesn't seem very useful.
You need queries that preserve path information.
For 1, finding the distinct labels on nodes on each path I would offer this query:
MATCH p=(:Building)-[:CONNECTED_TO*..5]-(:Bus)
WITH NODES(p) AS nodes
WITH REDUCE(myLabels = [], node in nodes | myLabels + labels(node)) as myLabels
RETURN DISTINCT myLabels
For 2, this query preserves path information:
MATCH p=(:Building)-[:CONNECTED_TO*..5]-(:Bus)
WITH NODES(p) AS nodes
WITH FILTER(node in nodes WHERE (node:Street)) as pathStreets
RETURN pathStreets
Note that these are both expensive operations, as they perform a cartesian product of all buildings and all busses, as in the queries in your description. I highly recommend narrowing down the buildings and busses you're matching upon, hopefully to very few or specific buildings at least.
I also encourage limiting how deep you're looking in your pattern. I get the idea that many, if not most, of your nodes in your graph are connected by :CONNECTED_TO relationships, and if we don't cap that to a reasonable amount, your query could be finding every single path through your entire graph, no matter how long or convoluted or nonsensical, and I don't think that's what you want.

Fixed length path between two nodes without knowing relationship type

I want to find paths of a certain length between two nodes - but I don't know what relationships are involved. The tutorials and examples seems to require I know what type of relationship I want to use. E.g.
MATCH (martin { name:"Martin Sheen" })-[:ACTED_IN*1..3]-(x)
RETURN x
I want to ask the graph:
MATCH (martin { name:"Martin Sheen" })-[:3..3]-(x)
RETURN x
But Neo4J seems to hang (I have 600,000 nodes / 1.3m relationships). How can I find paths of a certain length between two nodes, in a performant manner, provided I have no information about the relationships?
Thanks
First of all, this is not surprising it is really slow. Since Neo4j2.0, the use of labels is almost mandatory for performance reasons.
So make sure to use labels + an indexed property at least for matching your first node.
Secondly, you can just match paths of fixed length without knowing the relationship types.
Assuming your "Martin Sheen" node has a label Person :
Create index for Person/name :
CREATE INDEX ON :Person(name);
Match the person and the path
MATCH p=(martin:Person {name:"Martin Sheen"})-[*3..3]->(x)
RETURN p

Neo4j - Cypher return 1 to 1 relationships

Using neo4j 1.9.2, I'm trying to find all nodes in my graph that have a one to one relationship to another node. Let's say I have persons in my graph and I would like to find all persons, that have exactly one friend (since 2013), and this one friend only has the other person as friend and no one else. As a return, I would like to have all these pairs of "isolated" friends.
I tried the following:
START n=node(*) MATCH n-[r:is_friend]-m-[s:is_friend]-n
WHERE r.since >= 2013 and s.since >= 2013
WITH n, m, count(r), count(s)
WHERE count(r) = 1 AND count(s) = 1
RETURN n, m
But this query does not what it is supposed to do - it simply returns nothing.
Note: There exists just one relation between the two persons. So one friend has a incoming relationship and the other one an outgoing one. Also, these two persons might have some other relations, like "works_in" or so, but I just want to check if there is a 1:1 relation of type *is_friends* between the persons.
EDIT: The suggestion of Stefan works perfect if using node(*) as starting point. But when trying this query for one specific node as start point (e.g. start n=node(42)), it doesn't work. What would the solution look like in this case?
Update: I'm still wondering about a solution for this szenario: How to check if a given start node has a 1-to-1 relation to another node of a specific relationship type. Any ideas?
Here it's crucial to understand the concept of paths in the MATCH clause. A path is a alternating collection of node, relationship, node, relationship, .... node. There is the constraint that the same relationship will never occur twice in the same path - otherwise there would be a danger of having endless loops.
That said, you need to decide if is_friend in your domain is directed. If it is directed you'd distinguish a being friend to b and b being friend to a. From the description I assume is_friend is undirected and the statement should look like:
START n=node(*) MATCH n-[r:is_friend]-()
WHERE r.since >= 2013
WITH n, count(r) as numberOfFriends
WHERE numberOfFriends=1
RETURN n
You don't have to care about the other end, it's traversed nonetheless since you do a node(*). Be aware that node(*) gets obviously more expensive when your graph grows.

Resources