Sub queries in Cypher Query Language - neo4j

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.

Related

I need to count the number of connection between two nodes with a certain property

My database contains informations about the nominations for the accademy awards.
I want to know how many directors have won an oscar for "best director" more than one time.
I can't quite get to the result that i want, a list of nominees.
The closest I've been is with this query:
MATCH (n:Nominee)-[n1:NOMINATED]->(c:Category)
WHERE c.name="Best Director" AND n1.win=true
RETURN count(n1.win), n.name
ORDER BY n.name;
wich returns the directors names and the number of times they won an oscar.
I tried to do something like
MATCH (n:Nominee)-[n1:NOMINATED]->(c:Category)
WHERE c.name="Best Director" AND n1.win=true AND count(n1.win)>1
RETURN n.name;
but got an error that says
Invalid use of aggregating function count(...) in this context (line
2, column 50 (offset: 96)) "WHERE c.name="Best Director" AND
n1.win=true AND count(n1.win)>1"
Can someone help me with this?
Use WITH to aggregate the wins first. According to the docs:
[...] WITH is used to introduce aggregates which can then by used in predicates in WHERE. These aggregate expressions create new bindings in the results. WITH can also, like RETURN, alias expressions that are introduced into the results using the aliases as binding name.
So a query like this should work:
MATCH (n:Nominee)-[n1:NOMINATED]->(c:Category)
WHERE c.name="Best Director" AND n1.win=true
WITH n, count(n1.win) AS winCount
WHERE winCount > 1
RETURN n.name;
See also the docs on WHERE:
WHERE adds constraints to the patterns in a MATCH or OPTIONAL MATCH clause or filters the results of a WITH clause.

How to MATCH and CREATE a node and relationship?

Learning Neo4j and need help with getting the basics right. I am trying to find a Matching candidate then create a company and create a relationship between candidate and the newly created company. So, my query is
MATCH (b:Candidate {name:'Bala'}), CREATE (e:Employer {name:'Yahoo'}),
CREATE (b)-[:WORKED_IN]->(e)
RETURN b,e;
Invalid input '(': expected whitespace, comment, '=', node labels, MapLiteral, a parameter, a relationship pattern, ',', USING, WHERE, LOAD CSV, START, MATCH, UNWIND, MERGE, CREATE, SET, DELETE, REMOVE, FOREACH, WITH, RETURN, UNION, ';' or end of input...
I am using 2.2.5 console.
Remove the two commas before CREATE. The clauses in Cypher are not comma separated, only elements within a clause are. Your query will read
MATCH (b:Candidate {name:'Bala'})
CREATE (e:Employer {name:'Yahoo'})
CREATE (b)-[:WORKED_IN]->(e)
RETURN b,e;

Construct cypher to add many relationships in Neo4j

I wanted to add relationships to existing nodes, so I made cypher like this:
start n1=node:node_auto_index(id='0'),n2=node:node_auto_index(id='1') create n1-[:{quantity:1}]->n2;
start n1=node:node_auto_index(id='1'),n2=node:node_auto_index(id='2') create n1-[:USES_COMPONENT{quantity:7}]->n2;
start n1=node:node_auto_index(id='1'),n2=node:node_auto_index(id='3') create n1-[:USES_COMPONENT{quantity:11}]->n2;
start n1=node:node_auto_index(id='1'),n2=node:node_auto_index(id='4') create n1-[:USES_COMPONENT{quantity:14}]->n2;
but got many error messages (error around> {quantity). When I add only one of those, it says [[ Index node_auto_index does not exist ]].
You can't create a relationship without a type. If you add a type to the first statement, e.g. USES_COMPONENT like the others, that should resolve the error. You can match a relationship without using its type, but you can't create it without a type. If you want to match it without type, then you also need to drop the :, i.e.
n1-[{quantity:1}]->n2
is a valid pattern.
With regards to index, how have you configured your node_auto_index?
The error message means you don't have an index by that name. Usually you could fix this by enabling node auto-indexing -- Which is OK, but there's a better way to write your query:
MATCH (n0), (n1), (n2), (n3), (n4)
WHERE id(n0)=0 AND id(n1)=1 AND id(n2)=2 AND id(n3)=3 AND id(n4)=4
CREATE (n0)-[:USES_COMPONENT { quantity: 1 }]->(n1),
(n1)-[:USES_COMPONENT { quantity: 7 }]->(n2),
(n1)-[:USES_COMPONENT { quantity: 11}]->(n3),
(n1)-[:USES_COMPONENT { quantity: 14}]->(n4)
RETURN n0, n1, n2, n3, n4;
It's easier to use MATCH along with a WHERE clause to find a particular node (in general). Also notice that you can create as many new relationships as you want with a single query.
Double check this query before you use it -- you were re-using the n2 variable in confusing ways, so you need to make sure I got the right IDs bound to the right nodes for you.

Converting a star-topology to a linked list in Neo4j

When I initially created my data set I thought a star topology would be nice, but now that I've gotten some sample data, I'd rather be using a linked list topology. Now I want to do an in-place conversion using Cypher only, and in one call. Is this possible?
To use a simplified example: a Blog node can have several children Post nodes, so I attached them directly like this:
CREATE (a:Blog), (a)-[:HAS]->(b:Post), (a)-[:HAS]->(c:Post)
RETURN a,b,c
Now as my design moves along, I think I'd rather have this as a linked list, something like this (assuming the :FIRST post is already created):
MATCH (a:Blog)-[r:FIRST]->(b:Post)
CREATE (a)-[:FIRST]->(c:Post)-[:PREV]->(b)
DELETE r
RETURN a,b,c
So I can code the linked list fine. What I need to do is to convert the old star topologies to the new linked list format. I tried a bunch of things but nothing worked, so here is my best guess, but is completely non-functional code:
MATCH (b:Blog)-[:HAS]-(p:Post)
WITH b, collect(p) as posts
CREATE b-[:FIRST]->posts[0]
FOREACH( i IN range(1,len(posts)-1) |
CREATE posts[i]-[:PREV]->posts[i-1]
)
RETURN b,p
Indexing collections doesn't seem to work in a CREATE statement (neither inside or outside the FOREACH).
Any way to achieve this translation?
EDIT
I thought an additional way might be to tag the posts with ordinal values and do some iteration over sequential ordinals, but I get a syntax error trying to add the ordinals:
MATCH (b:Blog)-[:HAS]-(p:Post)
WITH b, collect(p) as posts
FOREACH( i in range(0,length(posts)) |
SET posts[i].ordinal = i
}
The syntax error is:
Invalid input '[': expected an identifier character, node labels, a property map, a relationship pattern, '(', '.' or '=' (line 4, column 14)
" SET posts[i].ordinal = i"
^
EDIT/2
Another idea I had was to put the linked-list code inside the FOREACH, but this fails because MATCH cannot be used inside FOREACH. First I created and attached a dummy (b:Blog)-[:FIRST]->(p:Post) relationship, then wrote this query (which doesn't work):
MATCH (f)<-[:FIRST]-(b:Blog)-[:HAS]->(p:Post)
WITH b, collect(p) as posts
FOREACH( x IN posts |
MATCH (b)-[r:FIRST]->(f)
DELETE r
CREATE (b)-[r2:FIRST]->(x), (x)-[:PREV]->(f)
)
RETURN b-[*0..1]-()
As a crutch until you find a better workarund you could try nesting FOREACH (a IN [posts[i]] | FOREACH (b IN [posts[i-1]] | CREATE a-[:PREV]->b)) inside your original foreach loop, as a way to unpack the index slices. For edit 1 try SET (posts[i]).ordinal = i. I think I have a nicer workaround somewhere but can't remember at the moment, if I find it I'll put it up.
Try this Cypher Query
After Edit
MATCH (a:Blog)-[r:HAS]->(p:Post)
WITH a,r,collect(p) as posts,range(1,length(posts)) as rel
MERGE(firstPost:Post {created: head(posts).created , otherprop: head(posts).otherprop})
CREATE a-[:FIRST]->(firstPost)
FOREACH(i IN rel |
MERGE(thisPost:Post {created: (posts[i]).created , otherprop: (posts[i]).otherprop})
MERGE(prevPost:Post {created: (posts[i-1]).created , otherprop: (posts[i-1]).otherprop})
CREATE (prevPost)-[:NEXT]->(thisPost))
WITH a
MATCH a-[r:HAS]->() delete r
This will create something like (Blog)-[FIRST]->(Post)-[NEXT]->(Post)...
By default Cypher sorts the element in COLLECT(p) in natural ascending order. Hence if you have only created property it will sort in the most recent one at last, hence the last node in the next chain starting from BLOG will be your latest POST associated to that BLOG

Neo4j Cypher: Create a relation only if the end node exists

For the following Cypher statement:
start n=node:types(id={typeId}), g=node:groups(id={groupId})
create unique (n)<-[:has_type]-(unit {props})-[:has_group]->(g)
return unit
There are cases when g may be null (i.e. a group with id groupId does not exist).
In such a case, what should I do to make this statement still create the unit, but skip the has_group relation to g?
Right now, unit does not get created, presumably because g is null.
I'm using Neo4j Advanced 1.8
Thanks!
I would suggest to move the definition of g to the where clause, since starting at a non-existing node gives error and thus one can't continue the query to the create phase. Note the '?' which handles the null values in Cypher:
start n=node:types(id={typeId})
create unique (n)<-[:has_type]-(unit {props})-[:has_group]->(g)
where g.id?={groupId}
return unit
the query might need some tweaking, this is just my first untested shot.
edit: After some trying I came to a conclusion, that you might want to do 2 different queries, first for creating the first part of relationships with the unique node which is always and the second to create the relationship to the group which may not happen:
start n=node:types(id={typeId})
create unique (n)<-[:has_type]-(unit {props})
return unit
start unit=node:unitProps({unitPropsValue}) ,g=node:groups(id={groupId})
create unique unit-[:has_group]->g
return g
the second query will fail with an error in case the group does not exist, but that does not matter since you will still reach the target. For some strange reason I couldn't manage to implement some restrictions in the where clause like I tried in the first shot. following query seems to simply jump over the where conditions (maybe a bug?) although in my comprehension of Cypher it shall match the already existing group, but it does create a new g node instead:
start n=node(1)
create unique n-[:TYPE1]-(uniq {uid:333})
with uniq
create unique uniq-[:TYPE2]->g
where has(g.gid) and g.gid=999
return g
You can use WITH clause to achieve this in one query,
start n=node:types(id={typeId})
create unique (n)<-[:has_type]-(unit {props})
WITH unit
START g=node:groups(id={groupId})
create unique (unit)-[:has_group]->(g)
WHERE g <> null
return unit
if g is null, second won't get executed at all. even WHERE g <> null might not be required here. Kindly try and confirm
You can try this
MATCH (g:groups)
WHERE id(g)={groupId}
create unique (unit {props})-[:has_group]->(g)
WITH unit, create unique (unit)-[:has_type]->(n)
return unit
Since this is the only thing I can find related to this, I will add how I am dealing with this since none of the other answers are good enough for my purposes.
MATCH (a:TEST{id:1})
OPTIONAL MATCH (b:TEST)
WHERE b.id IN [2,3,5]
// collect b so that there are no nulls, and rows aren't lost when no match
WITH a, collect(b) AS c
FOREACH(n IN c | CREATE UNIQUE (a)-[:HAS]->(n))
RETURN a
Will work for Cypher 2.3+ (I can't test any earlier than that)
For those with APOC, you can also use CALL apoc.cypher.doIt() to break out the write.

Resources