I have 4 Labels (A, B, C, D). All of them have a single Property {id}.
Now I have a file with relations which I would like to load. Every row has this structure:
{id_1}, {type_of_relations}, {id_2}
How can I create the relations?
My non-working guess is:
USING PERIODIC COMMIT
LOAD CSV WITH HEADERS FROM "file:/data.csv" AS line
FIELDTERMINATOR ','
MATCH (a:A{id:line.id_1} OR a:B{id:line.id_1} OR a:C{id:line.id_1} OR a:D{id:line.id_1})
MATCH (b:A{id:line.id_2} OR b:B{id:line.id_2} OR b:C{id:line.id_2} OR b:D{id:line.id_2})
MERGE (a)-[:line.type_of_relations]->(b)
You cannot parameterize the relationship type in Cypher.
However, you can do this using the apoc.create.relationship procedure in Neo4j apoc procedures:
USING PERIODIC COMMIT
LOAD CSV WITH HEADERS FROM "file:///file.csv" AS row
MATCH (a) WHERE a.id = row.id_1
MATCH (b) WHERE b.id = row.id_2
CALL apoc.create.relationship(a, row.type_of_relations, {}, b) YIELD rel
RETURN count(*) AS num
The procedure takes a parameter for the relationship type which allows for creating dynamic relationship types.
I don't think you can do that. For a number of reasons.
create (f:bar {name:'NewUserA'})
create (f:foo {name:'NewUserA'})
match (f:foo {name:'NewUserA'} or f:bar {name:'NewUserA'}) return f;
Code
Invalid input 'o': expected whitespace, comment, ')' or a relationship pattern (line 1, column 32 (offset: 31))
"match (f:foo {name:'NewUserA'} or f:bar {name:'NewUserA'}) return f".
So there is a problem on the match at any rate.
If the id is globally unique then you can ignore the label and just match on the id. that will take care of your 'or' problem.
match (f) where f.name='NewUserA' match (t) where t.name='NewUserA' return f,t
would give you the nodes.
That being said, when coding parameterized queries RELATIONSHIP_TYPE is one of the items you cannot parameterize. From the docs:
5.5. Parameters
[..]
Parameters can not be used as for property names, relationship types and labels, since these patterns are part of the query structure that is compiled into a query plan.
[..]
So you may need to look to ways of building your MERGE as a string somewhere else (awk is your friend) and then running that in the shell.
Related
I have a Neo4j database with two kinds of nodes - Authors and Articles. Some of the articles have more than one author. I am trying to create an undirected relationship between the authors who worked together on an article. My current non-functional query is this:
CALL apoc.periodic.iterate(
"MATCH (a:Author) WHERE (a)-[:WROTE]->()<-[:WROTE]-(b:Author) RETURN a,b",
"WITH {a} AS a, {b} as b CREATE (a)-[r:COAUTHOR]-(b)", {batchSize:10000, parallel:true})
I get the following error:
Failed to invoke procedure apoc.periodic.iterate: Caused by: org.neo4j.exceptions.SyntaxException: PatternExpressions are not allowed to introduce new variables: 'b'. (line 1, column 60 (offset: 59))
"EXPLAIN MATCH (a:Author) WHERE (a)-[:WROTE]->()<-[:WROTE]-(b:Author) RETURN a,b"
I can see that the issue is that I am trying to do too much in the first MATCH statemetn, but I'm new to Cypher and am having trouble breaking it up.
Thanks very much,
John
The problem stems from your first statement:
MATCH (a:Author)
WHERE (a)-[:WROTE]->()<-[:WROTE]-(b:Author)
RETURN a,b
Cypher does not allow introducing new variables on the WHERE part of the query, so your (b:Author) will not be allowed since it's located on the WHERE clause.
To fix, move (b:Author) to the MATCH statement. Also, if you are finding a pattern on the WHERE clause, you should use the exists() function in order to return a boolean.
MATCH (a:Author), (b:Author)
WHERE exists((a)-[:WROTE]->()<-[:WROTE]-(b))
RETURN a, b
I have been created a graph having a constraint on primary id. In my csv a primary id is duplicate but the other proprieties are different. Based on the other properties I want to create relationships.
I tried multiple times to change the code but it does not do what I need.
USING PERIODIC COMMIT
LOAD CSV WITH HEADERS FROM 'file:///Trial.csv' AS line FIELDTERMINATOR '\t'
MATCH (n:Trial {id: line.primary_id})
with line.cui= cui
MATCH (m:Intervention)
where m.id = cui
MERGE (n)-[:HAS_INTERVENTION]->(m);
I already have the nodes Intervention in the graph as well as the trials. So what I am trying to do is to match a trial with the id from intervention and create only the relationship. Instead is creating me also the nodes.
This is a sample of my data, so the same primary id, having different cuis and I am trying to match on cui:
You can refer the following query which finds Trial and Intervention nodes by primary_id and cui respectively and creates the relationship between them.
USING PERIODIC COMMIT
LOAD CSV WITH HEADERS FROM 'file:///Trial.csv' AS line FIELDTERMINATOR '\t'
MATCH (n:Trial {id: line.primary_id}), (m:Intervention {id: line.cui})
MERGE (n)-[:HAS_INTERVENTION]->(m);
The behavior you observed is caused by 2 aspects of the Cypher language:
The WITH clause drops all existing variables except for the ones explicitly specified in the clause. Therefore, since your WITH clause does not specify the n node, n becomes an unbound variable after the clause.
The MERGE clause will create its entire pattern if any part of the pattern does not already exist. Since n is not bound to anything, the MERGE clause would go ahead and create the entire pattern (including the 2 nodes).
So, you could have fixed the issue by simply specifying the n variable in the WITH clause, as in:
WITH n, line.cui= cui
But #Raj's query is even better, avoiding the need for WITH entirely.
I can load CSV into Neo4j for a specific label (say PERSON) and the nodes are created under the label PERSON.
I also have another CSV to illustrate the relationships between the person and it looks like:
name1, relation, name2
a, LOVE, b
a, HATE, c
I want to create a relationship between these pairs and the relationship thus created should be "LOVE", "HATE", etc, instead of a rigid RELATION as done by the below script:
load csv with headers from "file:///d:/Resources/Neo4j/person-rel.csv" as p
match (a:PERSON) where a.name=p.name1
match (b:PERSON) where b.name=p.name2
merge (a)-[r:REL {relation: p.REL}]->(b)
By doing this, I have a bunch of REL-type relations but not LOVE- and HATE-relations.
In another word, I want the REL in the last line of the script to be dynamically assigned. And then I can query out all the relationship types using Neo4j API.
Is this possible?
You can install the APOC library and then use apoc.merge.relationship
apoc.merge.relationship(startNode, relType, {key:value, ...}, {key:value, ...}, endNode) - merge relationship with dynamic type
load csv with headers from "file:///d:/Resources/Neo4j/person-rel.csv" as p
match (a:PERSON) where a.name=p.name1
match (b:PERSON) where b.name=p.name2
call apoc.merge.relationship(a,p.REL,{},{},b) yield rel
return count(*);
Hi Have a CSV file and I want to create nodes and relationships simultaneously
Im using below query to create nodes
using PERIODIC COMMIT 1000
load csv from "file:///home/gaurav/sharing/dataframe6.txt" as line fieldterminator" "
MERGE (A :concept{name:line[0]})
WITH line, A
MERGE (B :concept{name:line[1]})
WITH line, A, B
create (A)-[:line[3]]->(B); // This is trouble part
but when I try to create relationship between imported nodes then I get error
Invalid input '[': expected an identifier character, whitespace, '|', a length specification, a property map or ']' (line 7, column 18 (offset: 218))
"create (A)-[:line[3]]->(B);"
If you truly want to create relationship in a dynamic fashion you need to use an APOC procedure, specifically apoc.create.relationship.
using PERIODIC COMMIT 1000
load csv from "file:///home/gaurav/sharing/dataframe6.txt" as line fieldterminator" "
MERGE (A :concept{name:line[0]})
WITH line, A
MERGE (B :concept{name:line[1]})
WITH line, A, B
CALL apoc.create.relationship(A, line[3], {}, B) YIELD rel
RETURN A,B,rel
A relationship can not contain square brackets as its type name. You're trying to create a "line[3]" relationship between the nodes A and B.
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.