Cypher not able to create edges in loop - neo4j

I want to write query which will upsert(update/insert) one node and connect this node with related nodes.
I have some params for example like this
:params {namedEntities: [{"value": "value1","category": "category1",}, {"value": "value2", "category":"category2"}],
namedEntityAmounts: [2,3],
articleParams: {"id": "1","title": "3333"}
}
And using this params I want to do query like this:
MATCH (oldA:Article {id: $articleParams.id})
DETACH DELETE oldA
CREATE (a:Article)
SET a=properties($articleParams)
WITH a
UNWIND range(0, size($namedEntities)-1) as i
WITH $namedEntities[i] as nes, $namedEntityAmounts[i] as amount, a
MATCH (ne:NamedEntity {value: nes.value, category: nes.category})
CREATE (a)<-[r:OCCURS {amount: amount}]-(ne)
RETURN a,ne, type(r)
First four lines of this query are working. If query founds article with such a id, this article is being detach and delete and after this new node is being created with new params.
Hovewer I struggle with next part. Where I want to find all NamedEntity nodes whitch are identified by (value+category) and then I want to connect newly created Article node with all NamedEntity nodes. But this query seems to ingore last 3 lines and just prints out
Added 1 label, created 1 node, deleted 1 node, set 3 properties, completed after 6ms.
What I am doing wrong here?

Related

Is there a way to make a relation when creating a node, on a certain condition? Cypher 3.5

I am new to Neo4j and graph databases.
I am looking for a way create a node while connecting it to another node matching that has a field that matches a certain parameter.
Here's a diagram to get the idea:
Let's say my parameter is :params {friendNodeId: 2}
In my Cypher query, I would like to create my new node with its field name: "my brand new node". Then if there is a node having uniqueId = $params.friendNodeId create a relation between this node and my new node.
My approach is to optimize the entire process by running a single query and not having to make an unnecessary match in a second query to get the newly created node.
If you think that it doesn't make sense at all don't hesitate to come up with another proposition.
Thanks for your help.
From your example in the comments, if the node creation is mandatory, but the relationship creation is optional (depending on if there is a node that matches), then you can just move the CREATE to before your MATCH:
CREATE (new:Node {uId: 4})
SET new.name = "name 4"
WITH new
MATCH (other:Node {uId: $uniqueId})
CREATE (new)-[r:FRIEND]->(other)
RETURN r

neo4j relationships between PK and FK

i have two csv files the first one is like
movies.csv
movieId | title | genres
links.csv
movieId | tmdbId | imdbId
ive tried this cypher query
USING PERIODIC COMMIT 500
LOAD CSV WITH HEADERS FROM "file:///links.csv" AS row
WITH row
MATCH (movie:Movie {id: toInt(row.movieId)})
MERGE (link)-[r:LINK]->(movie)
ON CREATE SET r.tmdbId = toInt(row.tmdbId)
this didnt work for me, it doesnt create new label "LINK" or form the relationship correctly,,
i want to be able when i have a movieId to get its corresponding tmdbId
t've tried several methods but none of them worked, im new to neo4k and still familiarised with sql
Your usage of link is as a variable, not a label (you would use :Link if you wanted to create a new node with that label), and it's not really clear what your link is supposed to be, as you don't have any ids or any properties on it. It's also not clear what you need other nodes for, as a movie node can easily have properties for related ids (so you look up the :Movie node by its movieId and then get the tmdbId from that node).
If you could provide more details about your use cases, and what you want to model and how it's connected, that would help.
EDIT
Okay, so it sounds like you're modeling :Movies, and you also want :Link nodes that hold both tmdbId and imdbId properties. As mentioned above, in reality you should just set the properties on the :Movie node itself and not bother with :Link nodes at all, but this is for the sake of getting used to neo4j, so okay.
First of all, to make sure our matches are fast as we build these relationships, we need unique constraints on nodes through their unique IDs.
CREATE CONSTRAINT ON (m:Movie)
ASSERT m.id IS UNIQUE
CREATE CONSTRAINT ON (l:Link)
ASSERT l.tmdbId IS UNIQUE
CREATE CONSTRAINT ON (l:Link)
ASSERT l.imdbId IS UNIQUE
Your import would be:
USING PERIODIC COMMIT 500
LOAD CSV WITH HEADERS FROM "file:///links.csv" AS row
WITH row
MATCH (movie:Movie {id: toInt(row.movieId)})
MERGE (link:Link{imdbId: toInt(row.imdbId), tmdbId: toInt(row.tmdbId)})
MERGE (link)-[:LINK]->(movie)
And a query to get the movie by one id would be:
MATCH (link:Link)-[:LINK]->(movie:Movie)
WHERE link.imdbId = 123
RETURN movie
You should be able to infer the query for going the opposite direction, starting with a movieId and traversing the :LINK relationship to the :Link node (you may want to change one of these, as having the same name for a node label and a relationship type might get confusing since you're new to this) to get the relevant ID.

How to use cypher to create a relationship between items in an array and another node

I would like to use cypher to create a relationship between items in an array and another node.
The result from this query was a list of empty nodes connected to each other.
MATCH (person:person),(preference:preference)
UNWIND person.preferences AS p
WITH p
WHERE NOT (person)-[:likes]->(preference) AND
p = preference.name CREATE (person)-[r:likes]->(preference)
Where person.preferences contains an array of preference names.
Obviously I am doing something wrong. I am new to neo4j and any help with above would be much appreciated.
Properties are attributes of a nodes while relationships involve one or two nodes. As such, it's not possible to create a relationship between properties of two nodes. You'd need to split the properties into their own collection of nodes, and then create a relationship between the respective nodes.
You can do all that in one statement - like so:
create (:Person {name: "John"})-[:LIKES]->(:Preference {food: "ice cream"})
For other people, you don't want to create duplicate Preferences, so you'd look up the preference, create the :Person node, and then create the relationship, like so:
match (preference:Preference {food: "ice cream"})
create (person:Person {name: "Jane"})
create (person)-[:LIKES]->(preference)
The bottom line for your use case is you'll need to split the preference arrays into a set of nodes and then create relationships between the people nodes and your new preference nodes.
One thing....
MATCH (person:person),(preference:preference)
Creates a Cartesian product (inefficient and causes weird things)
Try this...
// Get all persons
MATCH (person:person)
// unwind preference list, (table is now person | preference0, person | preference1)
UNWIND person.preferences AS p
// For each row, Match on prefrence
MATCH (preference:preference)
// Filter on preference column
WHERE preference.name=p
// MERGE instead of CREATE to "create if doesn't exist"
MERGE (person)-[:likes]->(preference)
RETURN person,preference
If this doesn't work, could you supply your sample data and noe4j version? (As far as I can tell, your query should technically work)

Cypher, create unique relationship for one node

I want to add a "created by" relationship on nodes in my database. Any node should be able of having this relationship but there can never be more than one.
Right now my query looks something like this:
MATCH (u:User {email: 'my#mail.com'})
MERGE (n:Node {name: 'Node name'})
ON CREATE SET n.name='Node name', n.attribute='value'
CREATE UNIQUE (n)-[:CREATED_BY {date: '2015-02-23'}]->(u)
RETURN n
As I have understood Cypher there is no way to achieve what I want, the current query will only make sure there are no unique relationships based on TWO nodes, not ONE. So, this will create more CREATED_BY relationships when run for another User and I want to limit the outgoing CREATED_BY relationship to just one for all nodes.
Is there a way to achieve this without running multiple queries involving program logic?
Thanks.
Update
I tried to simplyfy the query by removing implementation details, if it helps here's the updated query based on cybersams response.
MERGE (c:Company {name: 'Test Company'})
ON CREATE SET c.uuid='db764628-5695-40ee-92a7-6b750854ebfa', c.created_at='2015-02-23 23:08:15', c.updated_at='2015-02-23 23:08:15'
WITH c
OPTIONAL MATCH (c)
WHERE NOT (c)-[:CREATED_BY]-()
CREATE (c)-[:CREATED_BY {date: '2015-02-23 23:08:15'}]->(u:User {token: '32ba9d2a2367131cecc53c310cfcdd62413bf18e8048c496ea69257822c0ee53'})
RETURN c
Still not working as expected.
Update #2
I ended up splitting this into two queries.
The problem I found was that there was two possible outcomes as I noticed.
The CREATED_BY relationship was created and (n) was returned using OPTIONAL MATCH, this relationship would always be created if it didn't already exist between (n) and (u), so when changing the email attribute it would re-create the relationship.
The Node (n) was not found (because of not using OPTIONAL MATCH and the WHERE NOT (c)-[:CREATED_BY]-() clause), resulting in no relationship created (yay!) but without getting the (n) back the MERGE query looses all it's meaning I think.
My Solution was the following two queries:
MERGE (n:Node {name: 'Name'})
ON CREATE SET
SET n.attribute='value'
WITH n
OPTIONAL MATCH (n)-[r:CREATED_BY]-()
RETURN c, r
Then I had program logic check the value of r, if there was no relationship I would run the second query.
MATCH (n:Node {name: 'Name'})
MATCH (u:User {email: 'my#email.com'})
CREATE UNIQUE (n)-[:CREATED_BY {date: '2015-02-23'}]->(u)
RETURN n
Unfortunately I couldn't find any real solution to combining this in one single query with Cypher. Sam, thanks! I've selected your answer even though it didn't quite solve my problem, but it was very close.
This should work for you:
MERGE (n:Node {name: 'Node name'})
ON CREATE SET n.attribute='value'
WITH n
OPTIONAL MATCH (n)
WHERE NOT (n)-[:CREATED_BY]->()
CREATE UNIQUE (n)-[:CREATED_BY {date: '2015-02-23'}]->(:User {email: 'my#mail.com'})
RETURN n;
I've removed the starting MATCH clause (because I presume you want to create a CREATED_BY relationship even when that User does not yet exist in the DB), and simplified the ON CREATE to remove the redundant setting of the name property.
I have also added an OPTIONAL MATCH that will only match an n node that does not already have an outgoing CREATED_BY relationship, followed by a CREATE UNIQUE clause that fully specifies the User node.

How to create an ordered chain linked to a node?

I have a set of HeadNodes which has field id and I have a set of TailNodes which are not related to each other and to HeadNodes and have fields id and date in milliseconds.
I want to write the query which takes:
Match (p: TailNodes) where not (p)-[:RELATED_TO]->()
that are not joined to HeadNode directly or through another TailNodes take their id number and look though HeadNodes for this id. When I found it (it's guaranteed to be there) I looked for a place to put it (in order of date time).
For example:
we have 1 HeadNode{id: 1} and 3 TailNodes: {id: 1, datetime: 111}, {id: 1, datetime: 115}, and {id: 1, datetime: 113} without any relationships.
At first step it takes first TailNode {id: 1, datetime: 111} and creates a relationship:
(head:HeadNode{id: 1})<-[:RELATED_TO]-(tail:TainNodes{id:1, datetime:111})
At second step it takes second Tailnode and finds out that 115 is greater than 111, so it deletes the previous relationship and creates 2 new relationships, and a chain that looks like this:
(head:HeadNode{id: 1})<-[:RELATED_TO]-(tail1:TainNodes{id:1, datetime:115})<-[:RELATED_TO]-(tail2:TainNodes{id:1, datetime:111})
At third step it founds out that 113 is greater than 111 but lesser than 115 and deletes relationship between datetime:115 and datetime:111; and then creates two new relationships finally getting the following:
(head:HeadNode{id: 1})<-[:RELATED_TO]-(tail1:TainNodes{id:1, datetime:115})<-[:RELATED_TO]-(tail2:TainNodes{id:1, datetime:113})<-[:RELATED_TO]-(tail3:TainNodes{id:1, datetime:111})
I hope it was clear explanation. Thanks in advance.
ok, first cut... out of time to create a more robust example but will take another shot later.
I started with a case where there were already nodes in teh list
H<--(T {dt:112})<--(T {dt:114})
I realize i create these in ascending order and not descending order too.
// match the orphaned tail nodes floating around
match (p:Tail)
where not(p-->())
with p
// match the strand with the same name and the tail nodes that are connected
// where one datetime (dt) is greater and one is less than my orphaned tail nodes
match (t1:Tail)<-[r:RELATED_TO]-(t2:Tail)
where t1.name = p.name
and t2.name = p.name
and t1.dt < p.dt
and t2.dt > p.dt
// break the relationship between the two nodes i want to insert between
delete r
// create new relationships from the orphan to the two previously connected tails
with t1, t2, p
create p-[:RELATED_TO]->t1
create t2-[:RELATED_TO]->p
return *
The case just needs to be extended for a tailless head and an orphan with a datetime greater than the last tail (i.e not in between two existing).

Resources