Adding New Relationships in Neo4j Database using apoc.periodic.iterate - neo4j

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

Related

PatternExpressions are not allowed to introduce new variables

I have two types of relationship that can exist between two same nodes. I want to extract the nodes that only have type1 and not type2 relationship. My query is:
Match (n) where (n)-[:type1]-(m) and (not (n)-[:type2]-(m)) return n
This gives error:
PatternExpressions are not allowed to introduce new variables: 'm'. (line 1, column 32 (offset: 31))
"Match (n) where (n)-[:type1]-(m) and (not (n)-[:type2]-(m)) return n"
^
Neither Googling around nor the documentation Patterns - Neo4j Cypher Manual give me any useful help. Do you know why is this?
How about this.
match(n)-[:type1]-(m)
where not (n)-[:type2]-(m)
return n

Cypher COLLECT clause to build a list where some lists could be empty

I have a database in which I have Entity nodes, User nodes, and a couple of relationships including LIKES, POSTED_BY. I'm trying to write a query to achieve this objective:
Find all Entity nodes that a particular user LIKES or those that have been POSTED_BY that User
Note that I have simplified my query - in real I have a bunch of other conditions similar to the above.
I'm trying to use a COLLECT clause to aggregate the list of all Entity nodes, and build on that line by line.
MATCH (e)<-[:LIKES]-(me:User{id: 'rJVbpcqzf'} )
WITH me, COLLECT(e) AS all_entities
MATCH (e)-[:POSTED_BY]->(me)
WITH me, all_entities + COLLECT(e) AS all_entities
UNWIND all_entities AS e
WITH DISTINCT e
RETURN e;
This seems to be returning the correct list ONLY if there is at least one Entity that the user has liked (i.e., if the first COLLECT returns a non-empty list). However, if there is no Entity that I have liked, the entire query returns empty.
Any suggestions on what I'm missing here?
Use OPTIONAL MATCH:
MATCH (me:User {id: 'rJVbpcqzf'})
OPTIONAL MATCH (me)-[:LIKES|POSTED_BY]->(e)
RETURN collect(DISTINCT e) AS all_entities
Notes:
Instead of collecting and unwinding, you can simply use DISTINCT. You can also use DISTINCT with collect.
You can also use multiple relationship types, i.e. the LIKES|POSTED_BY for the relationship type here.

Sub queries in Cypher Query Language

I want to write a CREATE relationship statement for a project i'm working on. The statement has to be something like this
CREATE (match (p:Halt) where p.name="Ananda College" return p)-[:next_halt {route:['103'],dist:1.45}]->(MATCH (p:Halt) where p.name="Borella" return p)
As you can see i want the start node and end node to have values coming from another CQL statement.
But when i run this query there seems to be a syntax error. I have gone through some tutorials to see where my query is wrong but being a beginner i can't really tell.
Invalid input '(': expected whitespace, comment, node labels, MapLiteral, a parameter, ')' or a relationship pattern (line 1, column 15 (offset: 14))
"CREATE (match (p:Halt) where p.name="Ananda College" return p)-[:next_halt {route:['103'],dist:1.45}]->(MATCH (p:Halt) where p.name="Borella" return p)"
Your syntax is rather mixed up here. Please reread the dev documentation and maybe look at the Cypher cheat sheet.
As for proper syntax, you don't even need nesting to get what you want. First, you match on your start and end nodes, then you can use the bound variables in other parts of your query, such as to create a relationship:
MATCH (start:Halt), (stop:Halt)
WHERE start.name = "Ananda College" AND stop.name="Borella"
CREATE (start)-[:next_halt {route:['103'], dist:1.45}]->(stop)
If you aren't sure if nodes (or the relationship) exist or not, you can use MERGE instead, which will MATCH on existing nodes (or relationships) or create them if they do not exist.

Neo4j load data relations with unknown labels

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.

Cypher query with diverging paths

I would like to query for the following subgraph in my Neo4J database:
(a)-->(b)-->(c)-->(d)
|
| -->(e)
Note: a, b, c, d, e are attribute values (non-unique values) for each of the nodes. There are thousands for these nodes with similar attribute values (a to e) but they are randomly connected to one another.
How can I write the Cyper query to specifically find the particular subgraph (akin to subgraph isomorphism problem) I seek and return (a)? I've tried the following Cyper query but other subgraphs pop up:
START n1=node:SomeIndex(AttrVal="a")
MATCH n1-[]->n2-[]->n3-[]->n4
WHERE n2.AttrVal="b" AND n3.AttrVal="c" and n4.AttrVal="d"
WITH n1, n2
MATCH n2-[]->n5
WHERE n5.AttrVal="e"
RETURN n1
Am I using the WITH and 2nd MATCH clause wrongly?
Thanks!
You can use the comma to combine multiple paths in a single match clause:
START n1=node:SomeIndex(AttrVal="a")
MATCH n1-[]->n2-[]->n3-[]->n4, n2-[]->n5
WHERE n2.AttrVal="b" AND n3.AttrVal="c" and n4.AttrVal="d" and n5.attrVal='e'
RETURN n1
Side note 1:
you can also refactor the statement like this:
START n1=node:SomeIndex(AttrVal="a"), n2=node:SomeIndex(AttrVal="b")
n3=node:SomeIndex(AttrVal="c"), n4=node:SomeIndex(AttrVal="d"),
n5=node:SomeIndex(AttrVal="e")
MATCH n1-[]->n2-[]->n3-[]->n4, n2-[]->n5
RETURN n1
Depending on the structure of your graph the second might be faster.
Side note 2:
When matching an arbitrary relationship type as you did in n1-[]->n2 you can use a shorter and more readable notation: n1-->n2

Resources