Neo4j- Avoid multiple relationships getting created between the same nodes in cypher? - neo4j

I want to restrict the relationships getting created , if already created between the existing nodes. Right now its getting created multiple times? Is there any command similar to 'merge' for restricting duplicate relationships?Thanks for any suggestion

User MERGE instead of CREATE while creating relationships.
MERGE (Product)-[:listed_under_category]->(level3)
MERGE (level3)-[:child_of]->(level2)

hey i think the mix up but i need more information. Is that merge, merges the the whole statement. so if he does not find a "match" for the whole statement the database "creates" the whole statement.
So when you type something like merge (a)-[]->(b)-[]->(q)
if you already have (a)-[]->(b)
he will recreate (a)-[]->(b)-[]->(q) and you have in the db (a)-[]->(b)-[]->(q) , (a)-[]->(b)
but if you type merge (a)-[]->(b) merge(b)-[]->(q)
and you know that you have the nodes. Neo4j merge will create the relations if there are no relations. and match if there are relations

If I understand your question, CREATE UNIQUE may be the solution you are looking for. For example:
create (a:Ex {name: 'A'})-[r:inside]->(b:Ex {name: 'B'}) return a, b, r
sets up your original (a)-[]->(b) relationship. Then to extend the relationship in the manner you proposed ....
match (a)-[:inside]->(b) where a.name = 'A' and b.name = 'B'
create unique (a)-[:inside]->(b)-[:inside]->(q:Ex {name: 'Q'})
return a, b, q
CREATE UNIQUE also works if the relationships are different:
match (a)-[:inside]->(b) where a.name = 'A' and b.name = 'B'
create unique (a)-[:inside]->(b)-[:under]->(q:Ex {name: 'Q'})
return a, b, q

The MERGE clause also avoids duplicating relationships.

Related

Neo4j create graph that some nodes have more than one relationships

Over my Neo4j I want to create this graph:
SO I tried to create some nodes and relationships with:
MERGE (D:POINT {NAME:'d'})<-[:LINKS]-(A:POINT {NAME:'a'})-[:LINKS]->(B:POINT {NAME:'b'})-[:LINKS]->(C:POINT {NAME:'c'})
But I cannot find out how I will create the relationships between D and B points also I cannot find out how I will link the A and C as well.
Do you have any Idea how to do that?
In order to avoid unintentionally creating duplicate nodes and/or relationships, you must invoke MERGE on individual nodes and relationships.
To quote the dev manual:
When using MERGE on full patterns, the behavior is that either the
whole pattern matches, or the whole pattern is created. MERGE will not
partially use existing patterns — it’s all or nothing. If partial
matches are needed, this can be accomplished by splitting a pattern up
into multiple MERGE clauses.
For example, to properly create your graph without any duplicate nodes or relationships:
MERGE (A:POINT {NAME:'a'})
MERGE (B:POINT {NAME:'b'})
MERGE (C:POINT {NAME:'c'})
MERGE (D:POINT {NAME:'d'})
MERGE (A)-[:LINKS]->(B)
MERGE (A)-[:LINKS]->(C)
MERGE (A)-[:LINKS]->(D)
MERGE (B)-[:LINKS]->(C)
MERGE (D)-[:LINKS]->(B)
CREATE seems to be the natural way to me for creating nodes and relationships.
CREATE (D:POINT {NAME:'d'})<-[:LINKS]-(A:POINT {NAME:'a'})
, (A)-[:LINKS]->(B:POINT {NAME:'b'})<-[:LINKS]-(D)
, (B)-[:LINKS]->(C:POINT {NAME:'c'})<-[:LINKS]-(A)
You can do it by doing a MATCH before a MERGE eg. FOR the relationship between A and D do:
MATCH (A:POINT {NAME:'a'}),(B:POINT {NAME:'d'}) MERGE (A)-[:LINKS]->(B)

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)

Give a specific pattern from a node during clause

I have the nodes: (a:charlie), (b:economy), and (c:bicycle) . I want to create this pattern:
create (a:charlie)-[x:wants_make]->(b:economy)->[y:by_using]->(c:bicycle)
But it gives me cartesian product. I already thought to skip the creation of the node (b) giving to relation [x:want_make]a property. But node (b) has many other relations in the same context(economic context). What I want to get the pattern above.
Any suggestion?
If your query looks like this:
MATCH (a:charlie), (b:economy), (c:bicycle)
MERGE (a)-[:wants_make]->(b)-[:by_using]->(c);
then it is saying both of these things:
Create a wants_make relationship between every charlie node and every economy node.
Create a by_using relationship between every economy node and every bicycle node.
So, if the number of charlie, economy, and bicycle nodes are C, E, and B -- this results in (C * E * B) merges, which is a Cartesian product of a Cartesian product.
Also, your data model seems to be wrong. For example, it seems much more reasonable to have a Person label instead of a charlie label.
A more reasonable query might look something like this:
MERGE (a:Person {name: 'Charlie Brown'})
MERGE (c:Bicycle {id: 123})
MERGE (a)-[:wants_make]->(b:Economy)
MERGE (b)-[:by_using]->(c);
This query avoids Cartesian products by being more specific about the first and last nodes in the path, and it also avoids creating nodes and relationships that already exist.
And, going even further, you might want to combine wants_make, Economy, and by_using into a single economizes_by_using relationship:
MERGE (a:Person {name: 'Charlie Brown'})
MERGE (c:Bicycle {id: 123})
MERGE (a)-[:economizes_by_using]->(c);
You might need to break up your query a bit:
MATCH (a:charlie), (b:economy), (c:bicycle)
MERGE (a)-[:wants_make]->(b), (b)->[:by_using]->(c)

Cypher: How to use FOREACH on an array of objects to create multiple nodes and multiple relationships

I am having trouble with the fetch described in the title of this question. My attempt is the following string:
FOREACH (_tabobj IN {_tabarray} |
MATCH (a:Superlabel),(b)
WHERE a.id = {_parentid} AND b.id = _tabobj.id
MERGE (a)-[r:INCLUDES]->(b {
name : _tabobj.name
})
I am trying to only create the relationship if it is not already in the database and nothing if the relationship is already in the database. Also trying to only create the b node if it is not already in the database and nothing if the b node is already in the database.
I am very grateful for help you can offer
How about something like this:
MATCH (a:Superlabel {id: {_parentid}})
WITH a, tabarray IN {_tabarray}
UNWIND tabarray AS tabobj
MATCH (b {id: tabobj.id)
MERGE (a)-[r:INCLUDES]->(b {name: tabobj.name})
I generally use FOREACH as a last resort ;)
And a simpler solution:
MATCH (a:Superlabel {id: {_parentid}}), (b)
WHERE b.id IN EXTRACT(tabobj IN {_tabarray} | tabobj.id)
MERGE (a)-[r:INCLUDES]->(b {name: tabobj.name})
An important consideration for both of these queries: When matching the b node on the id property the database index (if you have one) won't be used because indexes are for a label / property pair. Specifying a label for b, if applicable, could help performance.

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.

Resources