I'm trying to figure out what is the difference between MERGE and CREATE UNIQUE. I know these features:
MERGE
I'm able to create node, if doesn't exist pattern.
MERGE (n { name:"X" }) RETURN n;
This create node "n" with property name, empty node "m" and relationship RELATED.
MERGE (n { name:"X" })-[:RELATED]->(m) RETURN n, m;
CREATE UNIQUE
I'm not able to create node like this.
CREATE UNIQUE (n { name:"X" }) RETURN n;
If exists node "n", create unique makes empty node "m" and relationship RELATED.
MATCH (n { name: 'X' }) CREATE UNIQUE (n)-[:RELATED]->(m) RETURN n, m;
If this pattern exists, nothing created, only returns pattern.
From my point of view, I see MERGE and CREATE UNIQUE are quite same queries, but with CREATE UNIQUE you can't create start node in relationship. I would be grateful, if someone could explain this issue and compare these queries, thx.
CREATE UNIQUE has slightly more obscure semantics than MERGE. MERGE was developed as an alternative with more intuitive behavior than CREATE UNIQUE; if in doubt, MERGE is usually the right choice.
The easiest way to think of MERGE is as a MATCH-or-create. That is, if something in the database would MATCH the pattern you are using in MERGE, then MERGE will just return that pattern. If nothing matches, the MERGE will create all missing elements in the pattern, where a missing element means any unbound identifier.
Given
MATCH (a {uid:123})
MERGE (a)-[r:LIKES]->(b)-[:LIKES]->(c)
"a" is a bound identifier from the perspective of the MERGE. This means cypher somehow already knows which node it represents.
This statement can have two outcomes. Either the whole pattern already exists, and nothing will be created, or parts of the pattern are missing, and a whole new set of relationships and nodes matching the pattern will be created.
Examples
// Before merge:
(a)-[:LIKES]->()-[:LIKES]->()
// After merge:
(a)-[:LIKES]->()-[:LIKES]->()
// Before merge:
(a)-[:LIKES]->()-[:OWNS]->()
// After merge:
(a)-[:LIKES]->()-[:OWNS]->()
(a)-[:LIKES]->()-[:LIKES]->()
// Before merge:
(a)
// After merge:
(a)-[:LIKES]->()-[:LIKES]->()
Related
I'm trying to create a new node with label C and relationships from a-->c and b-->c, but if and only if the whole pattern a-->c,b-->c does exist.
a and b already exist (merged before the rest of the query).
The below query is a portion of the query I want to write to accomplish this.
However, it creates a random empty node devoid of properties and labels and attaches the relationship to that node instead. This shouldn't be possible and is certainty not what I want. How do I stop that from happening?
merge (a: A {id: 1})
merge (b: B {id:1})
with *
call {with a, b
match (a)-[:is_required]->(dummy:C), (a)-[:is_required]->(b)
with count(*) as cnt
where cnt = 0
merge (temp: Temporary {id: 12948125})
merge (a)-[:is_required]->(temp)
return temp
}
return *
Thanks
I think there are a couple of problems here:
There are restrictions on how you can use variables introduced with WITH in a sub-query. This article helps to explain them https://neo4j.com/developer/kb/conditional-cypher-execution/
I think you may be expecting the WHERE to introduce conditional flow like IF does in other languages. WHERE is a filter (maybe FILTER would have been a better choice of keyword than WHERE). In this case you are filtering out 'cnt's where they are 0, but then never reference cnt again, so the merge (temp: Temporary {id: 12948125}) and merge (a)-[:is_required]->(temp) always get executed. The trouble is, due to the above restrictions on using variables inside sub-queries, the (a) node you are trying to reference doesn't exist, it's not the one in the outer query. Neo4j then just creates an empty node, with no properties or labels and links it to the :Temporary node - this is completely valid and why you are getting empty nodes.
This query should result in what you intend:
merge (a: A {id: 1})
merge (b: B {id:1})
with *
// Check if a is connected to b or :C (can't use a again otherwise we'd overwrite it)
optional match(x:A {id: 1}) where exists((a)-[:is_required]->(:C)) or exists((a)-[:is_required]->(b))
with *, count(x) as cnt
// use a case to 'fool' foreach into creating the extra :Temporary node required if a is not related to b or :C
foreach ( i in case when cnt = 0 then [1] else [] end |
merge (temp: Temporary {id: 12948125})
merge (a)-[:is_required]->(temp)
)
with *
// Fetch the :Temporary node if it was created
optional match (a)-[:is_required]->(t:Temporary)
return *
There are apoc procedures you could use to perform conditional query execution (they are mentioned in the linked article). You could also play around with looking for a path from (a) and check its length, rather than introduce a new MATCH and the variable x then checking for the existance of related nodes.
If anyone is having the same problem, the answer is that the Neo4j browser is display nonexistent nodes. The query executes fineā¦
I have a LinkedHashSet which was created from a Seq. I used a LinkedHashSet because I need to keep the order of the Seq, but also ensure uniqueness, like a Set. I need to check this LinkedHashSet against another sequence to verify that various properties within them are the same. I assumed that I could loop through using an index, i, but it appears not. Here is an example of what I would like to accomplish.
var s: Seq[Int] = { 1 to mySeq.size }
return s.forall { i =>
myLHS.indexOf(i).something == mySeq.indexOf(i).something &&
myLHS.indexOf(i).somethingelse == mySeq.indexOf(i).somethingelse
}
So how do I access individual elements of the LHS?
Consider using the zip method on collections to create a collection of pairs (Tuples). The specifics of this depend on your specifics. You may want to do mySeq.zip(myLHS) or myLHS.zip(mySeq), which will create different structures. You probably want mySeq.zip(myLHS), but I'm guessing. Also, if the collections are very large, you may want to take a view first, e.g. mySeq.view.zip(myLHS) so that the pair collection is also non-strict.
Once you have this combined collection, you can use a for-comprehension (or directly, myZip.foreach) to traverse it.
A LinkedHashSet is not necessary in this situation. Since I made it from a Seq, it is already ordered. I do not have to convert it to a LHS in order to also make it unique. Apparently, Seq has the distinct method which will remove duplicates from the sequence. From there, I can access the items via their indexes.
MERGE (n : Person { id : 1000)
MERGE (m : Item { id : 2000})
SET m.name ='xyz'
MERGE (n)-[r:Buy]->(m)
I am trying to upload this kind of data (around 10k+) to Neo4j but the MERGE query is becoming very slow to execute, since for each MERGE it needs a full scan of the node space to verify no other item exists with the given property.
Is there any way to solve this ?
If you define a unique constraint on the id properties of the :Person and :Item nodes, these will become indexed and will then be scanned much more efficiently.
The syntax you'll need will look something like this:
CREATE CONSTRAINT ON (person:Person) ASSERT person:id IS UNIQUE
CREATE CONSTRAINT ON (item:Item) ASSERT item:id IS UNIQUE
You can find out more about unique constraints here:
http://docs.neo4j.org/chunked/stable/query-constraints.html
My use case is:
I have to return whole graph in result but the condition is
If there are more than 1 relationship in between two particular nodes in the same direction then I have to just merge it into 1 relationship. For ex: Lets say there are two nodes 'm' and 'n' and there are 3 relations in between these nodes say r1, r2, r3 (in the same direction) then when I get the result after firing cypher query I should get only 1 relation in between 'n' and 'm'.
I need to perform some operations on top of it like the resultant relation that we got from merging all the relations should contain the properties and their values that I want to retain. Actually I will retain all the properties of any one of the relations that are merging depending upon the timestamp field that is one of the properties in relation.
Note : I have same properties throughout all my relations (The number of properties and name of properties are same across all relations. Values may differ for sure)
Any help would be appreciated. Thanks in advance.
You mean something like this?
Delete all except the first
MATCH (a)-[r]->(b)
WITH a,b,type(r) as type, collect(r) as rels
FOREACH (r in rels[1..] | DELETE r)
Ordering by timestamp first
MATCH (a)-[r]->(b)
WITH a,r,b
ORDER BY r.timestamp DESC
WITH a,b,type(r) as type, collect(r) as rels
FOREACH (r in rels[1..] | DELETE r)
If you want to do all those operations virtually just on query results you'd do them in your programming language of choice.
I have a table called "Tag" which consists of an Id, Name and Description column.
Now lets say I have the tables Character (C), Movie (M), Series (S) etc..
And I want to be able to tag entries in C, M, S with multiple tags and one tag may be used for multiple entries.
So I could realize it like this:
T -> TC <- C
T -> TM <- M
T -> TS <- S
Where TC, TM, TS are the intermediate tables.
I was wondering if I could combine TC, TM, TS into one table with a type column added and still use foreign keys.
As of yet I haven't found a way to do it.
Or is this something I shouldn't be doing?
As the comments above suggested you can't combine multiple table into a single one. If you want to have a single view of the "tag relationships" you can pull the needed information into a View. This way, you only need to write a longer query once and are able to use like a single table. Keep in mind that you can't insert data into a view (there are possibilities to do so, but they are a little advanced)