Find specific unknown depth grandchildren in neo4j cypher - neo4j

I am looking to find any root node that has a specific (great? great?) grand child at an unknown depth, that passes through multiple nodes and relationships of different types before it finally reaches the ListItem nodes that is always related to its parent with HAS_SELECTED.
I have tried both:
MATCH (r:Root)-[HAS_SELECTED*]->(l:ListItem {alias: 'test'})
RETURN r
and
MATCH (r:Root)-[HAS_SELECTED*]->(l:ListItem)
WHERE l.alias = 'test'
RETURN r
An example graph could be
r r r r
| | |
c ListItem c
| |
c c
| |
ListItem(alias = test) ListItem(alias=somethingElse)
How would i find the r that ends up being related to the ListItem that has the alias of "test" without knowing all the intermediate relationships and nodes, or even how many intermediate relationships and nodes there may be?

This should work:
MATCH (r:Root)-[*0..]->()-[:HAS_SELECTED]->(l:ListItem)
WHERE l.alias = 'test'
RETURN r
A variable-length relationship can use a zero lower bound to indicate that the relationship is optional. (When the relationship does not exist, the nodes on both sides of it in the pattern would actually correspond to the same node).
(Also, in a MATCH pattern you need to put a colon before a relationship type name.)

Related

Get entire subgraph starting from one node in Neo4J?

I want to extract (retrieve) all the nodes and relationships in a graph starting from a specific node.
I have tried something like:
MATCH (n:Resource {resource_id: "R208997"})
MATCH p=(n)-[*]->(m)
RETURN p
This fetches all the paths from the node I have, but It is not really what I want.
What I want is to have a table showing the following:
From | Rel. | To
----------------
x | r | y
z | r2 | g
I am using version: 3.4.12 Community edition of Neo4J, and the data that I'm working on looks like this:
Best approach is to use APOC Procedures, we have some path expander procs for doing this efficiently.
You can use apoc.path.subgraphAll() for this, YIELDing relationships which you can alias accordingly:
MATCH (n:Resource {resource_id: "R208997"})
CALL apoc.path.subgraphAll(n, {relationshipFilter:'>'}) YIELD relationships
UNWIND relationships as rel
RETURN startNode(rel) as from, type(rel) as rel, endNode(rel) as to
If you need to output only certain properties from the nodes rather than the node itself, then you can modify that in your RETURN accordingly.

Complex neo4j cypher query to traverse a graph and extract nodes of a specific label and use them in optional match

I have a huge database of size 260GB, which is storing a ton of transaction information. It has Agent, Customer,Phone,ID_Card as the nodes. Relationships are as follows:
Agent_Send, Customer_Send,Customer_at_Agent, Customer_used_Phone,Customer_used_ID.
A single agent is connected to many customers .And hence hitting the agent node while querying a path is not feasible. Below is my query:
match p=((ph: Phone {Phone_ID : "3851308.0"})-[r:Customer_Send
| Customer_used_ID | Customer_used_Phone *1..5]-(n2))
with nodes(p) as ns
return extract (node in ns | Labels(node) ) as Labels
I am starting with a phone number and trying to extract a big "Customer" network. I am intentionally not touching the "Customer_at_Agent" relationship in the above networked query as it is not optimal as far as performance is concerned.
So, the idea is to extract all the "Customer" labeled nodes from the path and match it with [Customer_at_Agent] relationship.
For instance , something like:
match p=((ph: Phone {Phone_ID : "3851308.0"})-[r:Customer_Send
| Customer_used_ID | Customer_used_Phone *1..5]-(n2))
with nodes(p) as ns
return extract (node in ns | Labels(node) ) as Labels
of "type customer as c "
optional match (c)-[r1:Customer_at_Agent]-(n3)
return distinct p,r1
I am still new to neo4j and cypher and I am not able to figure out a hack to extract only "customer" nodes from the path and use that in the optional match.
Thanks in advance.
Use filter notation instead of extract and you can drop any nodes that aren't labelled right. Try out this query instead:
MATCH p = (ph:Phone {Phone_ID : "3851308.0"}) - [:Customer_Send|:Customer_used_ID|:Customer_used_Phone*1..5] - ()
WITH ph, [node IN NODES(p) WHERE node:Customer] AS customer_nodes
UNWIND customer_nodes AS c_node
OPTIONAL MATCH (c_node) - [r1:Customer_at_Agent] - ()
RETURN ph, COLLECT(DISTINCT r1)
So the second line takes the phone number and the path generated and gives you a list of nodes that have the Customer label as customer_nodes. You then unwind this list so you have individual nodes you can use in path matching. Line 4 performs your optional match and finds the r1 you're interested in, then line 5 will return the phone number node you started with and a collection of all of the r1 relationships that you found on customer nodes hooked up to that phone number.
UPDATE: I added some modifications to clean up your first query line as well. If you aren't going to use an alias (like r or n2 in the first line), then don't assign them in the first place; they can affect performance and cause confusion. Empty nodes and relationships are totally fine if you don't actually have any restrictions to place on them. You also don't need parentheses to mark off a path; they are used as a core part of Cypher's ASCII art to signify nodes, so I find they are more confusing than helpful.

Cypher: Find relationships of nodes with connected parents

I'm hoping this diagram will be sufficient to explain what I'm after:
true
a--------------------b
| |
parent | | parent
| |
a_e------------------b_e
experimental
nodes a_e and b_e are experimental observations that each have only one parent, a and b, respectively. I know a true relationship exists between a and b, and I want to find cases where experimental relationships were observed between a_e and b_e. Among other things, I tried the following:
MATCH (n)-[:true]-(m)
WITH n,m
MATCH (n)-[:parent]-(i)
MATCH (m)-[:parent]-(j)
WITH i,j
OPTIONAL MATCH (i)-[r]-(j)
RETURN r
but this returns no rows. I'm thinking of this like a nested loop, matching all possible relationships between all i's and all j's. Is this type of query possible?
Something like
match (n)-[:true]-(m)
match (n)-[:parent]->(n_child)-[:experimental]-(m_child)<-[:PARENT]-(m)
return n_child,m_child
(not tested)
Assuming this is an example and you have labels etc. on your nodes.

Neo4j - changing relationship type not working in web interface data browser

I have some nodes (n1) and (n2) that have the same name but unique properties and share a node-pair relationship (s1)-[r]->(e1) as follows:
I am trying to do a one-off where I create new relationships between s1 and e1 so that the new relationships are unique, based on id of n1 and n2, but the relationships have the same type as the previous relationship, and then delete the old relationship:
I found the following: neo4j cypher: how to change the type of a relationship which guided me toward the following query:
MATCH (e1)<-[:ENDS_WITH]-(n {name:"same"})-[:STARTS_WITH]->(s1),
(s1)-[r]->(e1)
CREATE (s1)-[r2:NEWREL]->(e1)
// copy properties and set n_id unique
SET r2 = r, r2.n_id = n.n_id
WITH r
DELETE r
Makes perfect sense. However, when I attempt this in the Neo4j web interface data browser, the n_id property gets set properly, but the type(r2) gets set as string "NEWREL" rather than the expected "REL"
I tried back ticks around the NEWREL and passing a temporary variable from type(r). Not working.
[UPDATED]
Cypher only allows you to create a relationship using a hardcoded type, so in general there is no way for you to achieve what you want.
However, if you know beforehand all the possible relationship types, there is a workaround. In the following example, suppose the possible relationship types are REL1, REL2, REL3, and REL4:
MATCH (e1)<-[:ENDS_WITH]-(n {name:"same"})-[:STARTS_WITH]->(s1)-[r]->(e1)
WITH s1, e1, r, n,
CASE TYPE(r)
WHEN 'REL1' THEN {rel1:[1]}
WHEN 'REL2' THEN {rel2:[1]}
WHEN 'REL3' THEN {rel3:[1]}
WHEN 'REL4' THEN {rel4:[1]}
END AS todo
FOREACH(x IN todo.rel1 | CREATE (s1)-[rr:REL1]->(e1) SET rr = r, rr.n_id = n.n_id)
FOREACH(x IN todo.rel2 | CREATE (s1)-[rr:REL2]->(e1) SET rr = r, rr.n_id = n.n_id)
FOREACH(x IN todo.rel3 | CREATE (s1)-[rr:REL3]->(e1) SET rr = r, rr.n_id = n.n_id)
FOREACH(x IN todo.rel4 | CREATE (s1)-[rr:REL4]->(e1) SET rr = r, rr.n_id = n.n_id)
DELETE r
Only one of the FOREACH clauses will actually create a new relationship (of the correct type) and copy the existing properties.
Caveat
The above solution works in neo4j 3.0, but neo4j 2.3 seems to have a bug that makes the query fail. If you do not have any properties on the r relationship worth copying, changing all the SET rr = r, rr.n_id = n.n_id clauses to SET rr.n_id = n.n_id should avoid that issue on 2.3.
So the problem is that thee relationship type is NEWREL instead of REL? In line 3 you are specifying that type: CREATE (s1)-[r2:NEWREL]->(e1). Just change that to CREATE (s1)-[r2:REL]->(e1).

neo4j collecting nodes and relations type b-->a<--c,a<--d

I am extending maxdemarzi's excellent graph visualisation example (http://maxdemarzi.com/2013/07/03/the-last-mile/) using VivaGraph backed by neo4j.
I want to display relationships of the type
a-->b<--c,b<--d
I tried the query
MATCH p = (a)--(b:X)--(c),(b:X)--(d)
RETURN EXTRACT(n in nodes(p) | {id:ID(n), name:COALESCE(n.name, n.title, ID(n)), type:LABELS(n)}) AS nodes,
EXTRACT(r in relationships(p)| {source:ID(startNode(r)) , target:ID(endNode(r))}) AS rels
It looks like the named query picks up only a-->b<--c pattern and omits the b<--d patterns.
Am i missing something... can i not add multiple patterns in a named query?
The most immediate problem is that the comma in the MATCH clause separates the first pattern from the second. The variable 'p' only stores the first pattern. This is why you aren't getting the results you desire. Independent of that, you are at risk of having a 'loose binding' by putting a label on both of your nodes named 'b' in the two patterns. The second 'b' node should not have a label.
So here is a version of your query that should work.
MATCH p1=(a)-->(b:X)<--(c), p2=(b)<--(d)
WITH nodes(p1) + d AS ns, relationships(p1) + relationships(p2) AS rs
RETURN EXTRACT(n IN ns | {id:ID(n), name:COALESCE(n.name, n.title, ID(n)), type:LABELS(n)}) AS nodes,
EXTRACT(r in rs| {source:ID(startNode(r)) , target:ID(endNode(r))}) AS rels
Capture both paths, then build collections from the nodes and relationships of both paths. The collection of nodes actually only extracts the nodes from p1 and adds the 'd' node. You could write that part as
nodes(p1) + nodes(p2) as ns
but then the 'b' node will appear in the list twice.

Resources