Neo4J Cypher: create relationship only if destination node exists - neo4j

I am trying to load a csv file that and as part of import try to create a relationship if the foreign key fields are resolved.
OPTIONAL MATCH (d1:Destination1 { id: line[2], name: line[3] })
OPTIONAL MATCH (d2:Destination2 { id: line[4], name: line[5] })
MERGE (n:Source {id: line[0])-[:MY_RELATIONSHIP]->(d1)
MERGE (n:Source {id: line[0])-[:MY_RELATIONSHIP]->(d2)
this query fails when either of d1 or d2 are not resolved. Is there a way to have a IF THEN option to create a link only if the destination exists. I see even WHERE clause is not allowed in the MERGE statement.
UPDATE 1
Looks like I over simplified my query, to give better context, here is what I am planning to achieve:
LOAD CSV FROM 'file:///MyData.csv' AS line
MATCH (n:BOM { Material: line[0]})
WITH n, line
MATCH (s:Supplier { name:line[2]})
MATCH (t:Type { name: line[7]})
MATCH (g1:Grade { number: line[6]})
MATCH (g2:Grade { number: line[5]})
MERGE (n)-[:HAS_SUPPLIER]->(s)
MERGE (n)-[:MATERIAL_TYPE]->(t)
MERGE (n)-[:HAS_GRADE{priority:1}]->(g1)
MERGE (n)-[:HAS_GRADE{priority:2}]->(g2)
I notice that if the query fails to find either g1 or g2 it fully fails to update other relationships. I had to resort to Optional Match but then the Merge fails

If the only thing you are doing is creating a relationship between existing nodes, then simply change the OPTIONAL MATCH to MATCH clause
MATCH (d1:Destination1 { id: line[2], name: line[3] })
MATCH (d2:Destination2 { id: line[4], name: line[5] })
....
You can think of it as this, if a MATCH clause fails to retrieve any data, then the query execution stops for that particular row. In a sense it is doing the same as:
OPTIONAL MATCH (d1:Destination1 { id: line[2], name: line[3] })
OPTIONAL MATCH (d2:Destination2 { id: line[4], name: line[5] })
WITH d1, d2
WHERE d1 IS NOT NULL and d2 IS NOT NULL
....
If you need to do other things as well, you can resort to FOREACH tricks or subqueries.
LOAD CSV FROM 'file:///MyData.csv' AS line
MATCH (n:BOM { Material: line[0]})
WITH n, line
MATCH (s:Supplier { name:line[2]})
MATCH (t:Type { name: line[7]})
OPTIONAL MATCH (g1:Grade { number: line[6]})
OPTIONAL MATCH (g2:Grade { number: line[5]})
MERGE (n)-[:HAS_SUPPLIER]->(s)
MERGE (n)-[:MATERIAL_TYPE]->(t)
FOREACH (_ in CASE WHEN g2 is not null and g1 is not null THEN [1] ELSE [] END |
MERGE (n)-[:HAS_GRADE{priority:1}]->(g1)
MERGE (n)-[:HAS_GRADE{priority:2}]->(g2))
FOREACH trick has been around since forewer (https://data-xtractor.com/blog/databases/neo4j-cypher-hacks/#4_Conditional_Execution_with_8230FOREACH)
Maybe a more clean version would be to use a subquery. I think the following should work:
LOAD CSV FROM 'file:///MyData.csv' AS line
MATCH (n:BOM { Material: line[0]})
WITH n, line
MATCH (s:Supplier { name:line[2]})
MATCH (t:Type { name: line[7]})
OPTIONAL MATCH (g1:Grade { number: line[6]})
OPTIONAL MATCH (g2:Grade { number: line[5]})
MERGE (n)-[:HAS_SUPPLIER]->(s)
MERGE (n)-[:MATERIAL_TYPE]->(t)
WITH n, g1, g2
CALL {
WITH n, g1, g2
WITH *
WHERE g1 IS NOT NULL and g2 IS NOT NULL
MERGE (n)-[:HAS_GRADE{priority:1}]->(g1)
MERGE (n)-[:HAS_GRADE{priority:2}]->(g2)}
You can also obviously create two FOREACH subqueries or two subqueries if you want to link to grade1 if it exists but grade 2 does not exists.
LOAD CSV FROM 'file:///MyData.csv' AS line
MATCH (n:BOM { Material: line[0]})
WITH n, line
MATCH (s:Supplier { name:line[2]})
MATCH (t:Type { name: line[7]})
OPTIONAL MATCH (g1:Grade { number: line[6]})
OPTIONAL MATCH (g2:Grade { number: line[5]})
MERGE (n)-[:HAS_SUPPLIER]->(s)
MERGE (n)-[:MATERIAL_TYPE]->(t)
FOREACH (_ in CASE WHEN g1 is not null THEN [1] ELSE [] END |
MERGE (n)-[:HAS_GRADE{priority:1}]->(g1))
FOREACH (_ in CASE WHEN g2 is not null THEN [1] ELSE [] END |
MERGE (n)-[:HAS_GRADE{priority:2}]->(g2))

Related

Shortest Path using Neo4j, with relationships of Start and End Node

I'm fairly new to Neo4j. I'm trying to get the shortest path for default movie database.
However, along with it I also need the start and end node(of type Person) to display there first level relation to any other node and if any person node on the path have a relation to the start and end node via some other node. Its better to explain with below pictures.
Normal shortestpath will look, something like this.
MATCH (p1:Person { name: 'Kevin Bacon' }),(p2:Person { name: 'Meg Ryan' }),
p = shortestPath((p1)-[*..15]-(p2))
return p
However my desired output is this one.
I tried below cypher. However i cannot understand, how to do it. I'm getting below result, which is incorrect.
MATCH (p1:Person { name: 'Kevin Bacon' }),(p2:Person { name: 'Meg Ryan' }),
p = shortestPath((p1)-[*..15]-(p2))
MATCH (p1:Person)-[r]-(b) // here i need foreach node on path for type person, get there relationships? However can't do that
return p,p1,r,b
Appreciate any help. Thanks.
You can get all the movies in the start and end nodes of shortest path. Then add the movies with the path.
MATCH p = shortestPath((p1:Person { name: 'Kevin Bacon' })-[*..15]-(p2:Person { name: 'Meg Ryan' }))
WITH p1, p2, p
MATCH (p1)-[:ACTED_IN]->(m1)
MATCH (p2)-[:ACTED_IN]->(m2)
RETURN p, m1, m2
Thanks for the reply Jose. It's close. I don''t want to restrict the relation just to "ACTED_IN" with (Movie). I just did a small modification.
MATCH p = shortestPath((p1:Person { name: 'Kevin Bacon' })-[*..15]-
(p2:Person { name: 'Meg Ryan' }))
UNWIND nodes(p) as n
MATCH (n)-[*]->(q)
RETURN n, q
However, i don't need the nodes highlighted in the box as they are not related to Meg and Kevin. Not sure how to exclude them.

Cypher: using OPTIONAL MATCH in combination with collect returns default item when there is none

I'm using Neo4J to retrieve a person and their skills. This is my Cypher query:
MATCH (p:Person {id: "1"})
OPTIONAL MATCH (p) -[exp:HAS_EXPERIENCE]->(s:Skill)
WITH collect(distinct {id: s.id, name: s.name}) as skills, p
RETURN p.id as id, skills
This is the result:
{
"id": "1",
"skills": [
{
"name": null,
"id": null,
}
]
}
As you can see, the list of skills contains a 'default' item. However, in this particular case the person has no skills.
Why does the result contain an array item? How to I adjust the query so that an empty array is returned?
Using Neo4J 3.1.1.
This should work:
MATCH (p:Person {id: "1"})
OPTIONAL MATCH (p)-[exp:HAS_EXPERIENCE]->(s:Skill)
RETURN p.id AS id,
CASE WHEN s IS NULL THEN [] ELSE COLLECT(distinct {id: s.id, name: s.name}) END as skills;
s would only be NULL if the OPTIONAL MATCH does not match anything.
You may wish to consider the following:
MATCH (p:Person {id: "1"})
OPTIONAL MATCH (p) -[exp:HAS_EXPERIENCE]->(s:Skill)
RETURN p {id: p.id, skills: COLLECT(distinct s {.*})}
Assuming that you wish to get all fields from the Skill. Otherwise
... collect(distinct s {id:s.id, name:s.name})

How to report named properties in JSON format from a cypher query?

I have graph in which nodes are linked to other nodes containing properties. For instance:
(:Person {Name: 'A'}) -[:OWNS]-> (:Object {Name: 'Car', Value: 2})
(:Person {Name: 'A'}) -[:OWNS]-> (:Object {Name: 'Computer', Value: 1})
Is it possible (how) to report tagged data from a cypher query. Expected format would preferably be:
{Person: 'A', owns: {Car: 2, Computer: 1}}
My current query is:
match (p:Person) -[:OWNS]-> (o:Object)
return {Person: p.Name, owns: collect({name: o.Name, value: o.Value})
this returns
{Person: 'A', owns: [{name: 'Car', value: 2}, {name: 'Computer', value: 1}]}
Unfortunately, I can't write something like:
return {Person: p.Name, owns: collect({o.Name: o.Value})
Assuming that a person can have any number of cars and computers (including 0):
OPTIONAL MATCH (p:Person)-[:OWNS]->(car:Object { Name: 'Car' })
OPTIONAL MATCH (p)-[:OWNS]->(comp:Object { Name: 'Computer' })
RETURN { Person: p.Name, owns: { Cars: COLLECT(car.Value), Computers: COLLECT(comp.Value)}} AS result;
Here is a sample result for 2 cars and 0 computers for person A:
{Person:"A", owns:{"Cars":[2,3],"Computers":[]}}
1: Fetch all nodes: - START n=node (*) RETURN n; or match (n) return n
2: Displays the nodes and the relationships: - MATCH (n) MATCH (n)-[r]-() RETURN n,r Or START n=node(*) MATCH (n)-[r]->(m) RETURN n,r,m
3: Match nodes and relationships: - MATCH (a:Policy)-[:APPLIES_TO]-(Cluster) WHERE a.name = "pol-1nils" RETURN a, Cluster
4: Get all object of particular nodes: - MATCH (list:Policy) return list
5: bound to the entities between two nodes: - match (a:WorkLoad)-[b:APPLIES_TO]->(c:Policy) where c.name = "shamshad" return a,b,c;

neo4j optional match and null

Perhaps this approach is wrong but I've built a cypher query using optional matches and collect. If there is data everything is fine, if not, collect returns null for the properties specified. It looks like this is expected as per the docs.
Ideally I'd like collect to return an empty array or null when there no match is made. I'm using the following...
MATCH (p) WHERE id(p) = 11
OPTIONAL MATCH (p) -[:car]- (c)
OPTIONAL MATCH (p) -[:driver]- (u)
RETURN {
_id: id(p), name: p.name, type: p.type,
cars: collect({_id: id(c), name: c.name}),
drivers: collect({_id: id(u), name: u.email})
} AS place
Try like this
MATCH (p) WHERE id(p) = 11
OPTIONAL MATCH (p) -[:car]- (c)
OPTIONAL MATCH (p) -[:driver]- (u)
RETURN {
_id: id(p), name: p.name, type: p.type,
cars: CASE WHEN c IS NOT NULL THEN collect({_id: id(c), name: c.name}) ELSE NULL END,
drivers: CASE WHEN u IS NOT NULL THEN collect({_id: id(u), name: u.email}) ELSE NULL END
} AS place
This will check whether data is present to collect or not if present then it will return else null value will be returned

Match Command in Qlikview

I have created only one graph in Neo4j using the create statement. Now I want to retrieve data from the same graph using the MATCH command. It is giving me the correct count of all the nodes and relationships. Now I have run the same create query again without any changes and now if I retrieve the count using the same MATCH then I'm getting the double value.If run it again then triple value. If I give the return statement in the create statement then the count is correct, if I give it separately then the count is getting multiplied.
Why it is so.
And how to get the correct count if I run the same graph multiple times.
LOAD CSV FROM "file:D:\\Neo4j\\demo2dbcopy.csv" AS emp
MERGE (root:SubGraph {source: emp[7]})
MERGE (cname:Cmpname {name: emp[0]})
MERGE (clusters:ClustName { name: emp [1]})
MERGE (dom:domains { name: emp [2]})
MERGE (Hd:Head { name: emp [3]})
MERGE (DelHd:DeliveryHead { name: emp [4]})
MERGE (Mgr:Managers { name: emp [5]})
MERGE (Emp:Employees { name: emp [6]})
CREATE (root)<-[:PART_OF_SUB_GRAPH]-(cname)-[:has]->(clusters),(clusters)-[:contains]->(dom)<-[:headedby]-(Hd),
(Hd)-[:equals]->(DelHd),
(DelHd)-[:assistedby]->(Mgr),
(Mgr)-[:Dividesinto]->(Emp)
return root,cname,clusters,dom,Hd,count(Hd);
and the MATCH statement is
MATCH (root:SubGraph {source: "demo2db"})<--(cname:Cmpname)-->(clusters:ClustName)-->(dom:domains {name:"BIDW"})<-[:headedby]-(Hd:Head) RETURN dom.name, count (Hd);
Thanks in advance...
The create command is
LOAD CSV FROM "file:D:\\Neo4j\\demo2dbcopy.csv" AS emp
MERGE (root:SubGraph {source: emp[7]})
MERGE (cname:Cmpname {name: emp[0]})
MERGE (clusters:ClustName { name: emp [1]})
MERGE (dom:domains { name: emp [2]})
MERGE (Hd:Head { name: emp [3]})
MERGE (DelHd:DeliveryHead { name: emp [4]})
MERGE (Mgr:Managers { name: emp [5]})
MERGE (Emp:Employees { name: emp [6]})
CREATE (root)<-[:PART_OF_SUB_GRAPH]-(cname)-[:has]->(clusters),(clusters)-[:contains]->(dom)<-[:headedby]-(Hd),
(Hd)-[:equals]->(DelHd),
(DelHd)-[:assistedby]->(Mgr),
(Mgr)-[:Dividesinto]->(Emp)
return root,cname,clusters,dom,Hd,count(Hd);
and the MATCH statement is
MATCH (root:SubGraph {source: "demo2db"})<--(cname:Cmpname)-->(clusters:ClustName)-->(dom:domains {name:"BIDW"})<-[:headedby]-(Hd:Head) RETURN count (Hd);

Resources