Neo4j Browser - Bug on create node? - neo4j

I dont know if it cpuld be an issue/bug.
When you create a node without variable, server creates "zombie gray" nodes
Example: CREATE (Expedient {id_exp: 'MAPSAN_0004', name: 'contractual', dateCreation: '16/03/2022', dateExpiration:'16/03/2023'})
and run it 2 o 3 times.
You will see in console result for each command:
Created 1 node, set 4 properties, completed after 2 ms.
But when you want to match the nodes, per example:
MATCH (e:Expedient) RETURN e
you will not find de "zombie" nodes, only if run:
MATCH (e) RETURN e
you will see all nodes.
Is it a good behaviour?
Thx in advance community!

You are almost there, just missing a colon to define the node label.
CREATE (:Expedient
{id_exp: 'MAPSAN_0004', name: 'contractual', dateCreation: '16/03/2022', dateExpiration:'16/03/2023'})
If you don't include the colon, the Cypher treats it as a reference variable that you could refer to later in the same statement:
CREATE (e:Expedient {id_exp: 'MAPSAN_0004', name: 'contractual', dateCreation: '16/03/2022', dateExpiration:'16/03/2023'})
CREATE (e)-[:REL]->(:Node)
Another thing, you might want to store the dates as date format and not string.

Related

Auto increment id Neo4j to retrieve elements in insert order

Recently, I am experimenting Neo4j. I like the idea but I am facing a problem that I have never faced with relational databases.
I want to perform these inserts and then return them exactly in the insertion order.
Insert elements:
create(p1:Person {name:"Marc"})
create(p2:Person {name:"John"})
create(p3:Person {name:"Paul"})
create(p4:Person {name:"Steve"})
create(p5:Person {name:"Andrew"})
create(p6:Person {name:"Alice"})
create(p7:Person {name:"Bob"})
While to return them:
match(p:Person) return p order by id(p)
I receive the elements in the following order:
Paul
Andrew
Marc
John
Steve
Alice
Bob
I note that these elements are not returned respecting the query insertion order (through the id function).
In fact the id of my elements are the following:
Marc: 18221
John: 18222
Paul: 18208
Steve: 18223
Andrew: 18209
Alice: 18224
Bob: 18225
How does the Neo4j id function work? I read that it generates an auto incremental id but it seems a little strange his mechanism. How do I return items respecting the query insertion order? I thought about creating a timestamp attribute for each node but I don't think it's the best choice
If you're looking to generate sequence numbers in Neo4j then you need to manage this yourself using a strategy that works best in your application.
In ours we maintain sequence numbers in key/value pair nodes where Scope is the application name given to the sequence number range, and Value is the last sequence number used. When we generate a node of a given type, such as Product, then we increment the sequence number and assign it to our new node.
MERGE (n:Sequence {Scope: 'Product'})
SET n.Value = COALESCE(n.Value, 0) + 1
WITH n.Value AS seq
CREATE (product:Product)
SET product.UniqueId = seq
With this you can create as many sequence numbers you need just by creating sequence nodes with unique scope names.
For more examples and tests see the AutoInc.Neo4j project https://github.com/neildobson-au/AutoInc/blob/master/src/AutoInc.Neo4j/Neo4jUniqueIdGenerator.cs
The id of Neo4j is maintained internally, which your business code should not depend on.
Generally it's auto incrementally, but if there is delete operation, you may reuse the deleted id according to the Reuse Policy of Neo4j Server.

Retrieve last node in list for further processing

I trying to set up a scheme for web-clicks, where each node is a (:Click), which links to the click that precedes it by a [:PREV]-edge and the (:Session) that owns it by a [:GEN]-edge. In the end this should happen procedural, a new transaction/insert when a new click is made. While I have no problem generating the involved objects, I cannot figure out how to dynamically select last (:Click) and link it to the current created one.
Generate a session with 2 clicks:
CREATE (s:Session {name:'S0'})
CREATE (c1:Click {name:'C1', click:1}), (c1)<-[:GEN]-(s)
CREATE (c2:Click {name:'C2', click:2}), (c2)<-[:GEN]-(s), (c1)<-[:PREV]-(c2);
generate one other click in separated transaction:
MERGE (s:Session {name:'S0'})
CREATE (c3:Click {name:'C3', click:3}),
(c3)<-[:GEN]-(s) //(c2)<-[:PREV]-(c3);
for the commented out link, I cannot use the c2-variable as it is scope-local to the previous transaction.
Now I thought to try something like this to dynamically find the last generated node on the same session and link it
MERGE (s:Session {name:'S0'})
CREATE (c3:Click {name:'C3', click:3}), (c3)<-[:GEN]-(s)
MATCH (s)-[:GEN]->(c_prevs:Click)
WITH c_prevs
ORDER BY c_prevs.click DESC LIMIT 1
CREATE (head(c_prevs))<-[:PREV]-(c3)
Unfortunately this won't work for me with any Cypher-construct I came up with so far.
If I understand you can get the last :Click node on the same session this way:
match (:Session {name:'S0'})-[:GEN]->(c:Click)
where not (:Click)-[:PREV]->(c)
return c
That is: Get the node from the same session that does not have an incoming [PREV] relationship. Will return c2
╒═══════════════════════╕
│"c" │
╞═══════════════════════╡
│{"name":"C2","click":2}│
└───────────────────────┘
For your specific case a query like the following should work:
merge (s:Session {name:'S0'})
with s
match (s)-[:GEN]->(last:Click)
where not (:Click)-[:PREV]->(last)
create (c3:Click {name:'C3', click:3}),
(c3)<-[:GEN]-(s),
(last)<-[:PREV]-(c3)
I found the answer to my question to be the following
MATCH (s:Session {name:'S0'})
CREATE (c3:Click {name:'C3', click:3})
WITH s, c3
MATCH (s)-[:GEN]->(c_prev:Click)
WITH c_prev, c3, s
ORDER BY c_prev.click DESC LIMIT 1
WITH c_prev, c3, s
CREATE (c_prev)<-[:PREV]-(c3), (c3)<-[:GEN]-(s)
which is chaining through the nodes as variables s, c3 and last_c with the WITH keyword. Unfortunately this involves a lot of repetition, as every WITH in principle is a part-separator in the query, so I learned.
This also allows to carry over already MERGED/CREATED nodes, which might help to ensure their existence.
EDIT:
This problem seems to be even more complicated if clicks should be generated prozedural, thus using one cypher-statement to insert and link any click.
my solution looks like the following
MERGE (s:Session {name: $session_name})
WITH s
CREATE (c:Click {name: $click_name, click: $click_count})
WITH s, c
OPTIONAL MATCH (s)-[:GEN]->(c_prev:Click)
WITH c_prev, c, s
ORDER BY c_prev.click DESC LIMIT 1
WITH c_prev, c, s
FOREACH (o IN CASE WHEN c_prev IS NOT NULL THEN ['1'] ELSE [] END |
CREATE (c_prev)<-[:PREV]-(c)
)
WITH s, c
CREATE (c)<-[:GEN]-(s)
with executing this statement for {$session_name, $click_name, $click_count} =[{'AAA', 'C1', 1}, {'AAA', 'C2', 2}, {'AAA', 'C3', 3}].
Notice that I had to work around the returning empty node-list by explicitly catching this condition and then not executing the subsequent connection statement with the FOREACH-loop on an empty list. This does not only look very ugly, I sincerely think there should be a better way to expressively specify this desired behavior through Cypher in the near future.

How to create a relation between two node having same node name in neo4j

I want to create a friends relation between abcd node and vbnm node having same node-name - Student
neo4j graph database visualization
I execute the following query, It doesn't show me any error but this query doesn't create any relation
match(Student:Stu),(Student:Stu)where Student.name="abcd" AND Student.name="vbnm" create(Student)-[fr:friends]->(Student)
You need use different variable name:
match(Student1:Stu),(Student2:Stu)
where Student1.name="abcd" AND
Student2.name="vbnm"
create(Student1)-[fr:friends]->(Student2)
I think you are confused by the syntax a little bit. Let me give you an example of a MATCH query syntax.
MATCH (variable1:Label),(variable2:Label) where variable1.foo = variable2.foo
You mixed label and variable in your query and each entity should have its own variable (variable1 and variable2) so you can interact with them.
So in your case the optimal query looks something like:
MATCH (s1:Student),(s2:Student ) where s1.name="abcd" AND s2.name="vbnm"
CREATE (s1)-[:friends]->(s2)
Note that you do not need to assign a variable to [:friends] relationship as you do not interact with it later in the same query.

How to set all relationships of a certain type -- replacing the old ones if necessary -- in Neo4j?

I have a node id for an event, and list of node ids for users that are hosting the event. I want to update these (:USER)-[:HOSTS]->(:EVENT) relationships. I dont just want to add the new ones, I want to remove the old ones as well.
NOTE: this is coffeescript syntax where #{} is string interpolation, and str() will escape any characters for me.
Right now I'm querying all the hosts:
MATCH (u:USER)-[:HOSTS]->(:EVENT {id:#{str(eventId)}})
RETURN u.id
Then I'm determining which hosts are new and need to be added and which ones are old and need to be removed. For the old ones, I remove them
MATCH (:HOST {id:#{str(host.id)}})-[h:HOSTS]->(:EVENT {id:#{str(eventId)}})
DELETE h
And for the new ones, I add them:
MATCH (e:EVENT {id: #{str(eventId)}})
MERGE (u:USER {id:#{str(id)}})
SET u.name =#{str(name)}
MERGE (u)-[:HOSTS]->(e)
So my question is, can I do this more efficiently all in one query? I want want to set the new relationships, getting rid of any previous relationships that arent in the new set.
If I understand your question correctly, you can achieve your objective in a single query by introducing WITH and FOREACH. On a sample graph created by
CREATE (_1:User { name:"Peter" }),(_2:User { name:"Paul" }),(_3:User { name:"Mary" })
CREATE (_4:Event { name:"End of the world" })
CREATE _1-[:HOSTS]->_4, _2-[:HOSTS]->_4
you can remove the no longer relevant hosts, and add the new hosts, as such
WITH ["Peter", "Mary"] AS hosts, "End of the world" AS eventId
MATCH (event:Event { name:eventId })<-[r:HOSTS]-(u:User)
WHERE NOT u.name IN hosts
DELETE r
WITH COLLECT(u.name) AS oldHosts, hosts, event
WITH FILTER(h IN hosts
WHERE NOT h IN oldHosts) AS newHosts, event, oldHosts
FOREACH (n IN newHosts |
MERGE (nh:User { name:n })
MERGE nh-[:HOSTS]->event
)
I have made some assumptions, at least including
The new host (:User) of the event may already exists, therefore MERGE (nh:User { name:n }) and not CREATE.
The old [:HOSTS]s should be disconnected from the event, but not removed from the database.
Your coffee script stuff can be translated into parameters, and you can translate my pseudo-parameters into parameters. In my sample query I simulate parameters with the first line, but you may need to adapt the syntax according to how you actually pass the parameters to the query (I can't turn Coffee into Cypher).
Click here to test the query. Change the contents of the hosts array to ["Peter", "Paul"], or to ["Peter", "Dragon"], or whatever value makes sense to you, and rerun the query to see how it works. I've used name rather than id to catch the nodes, and again, I've simulated parameters, but you might be able to translate the query to the context from which you want to execute it.
Edit:
Re comment, if you want the query to also match events that don't have any hosts you need to make the -[:HOSTS]- part of the pattern optional. Do so by braking the MATCH clause in two:
MATCH (event:Event { name:eventId })
OPTIONAL MATCH event<-[r:HOSTS]-(u:User)
The rest of the query is the same.

Neo4j Cypher: How to stop duplicate SET if multiple CREATES

I have a complex cypher query that creates multiple nodes and increments some counters on those nodes. For sake of example here is a simplified version of what I am trying to do:
START a = node(1), e = node(2)
CREATE a-[r1]->(b {})-[r2]->(c {}), e-[r3]->b-[r4]->(d{})
SET a.first=a.first+1, e.second=e.second+1
RETURN b
The issue is that because there are two CREATE commands the SET commands run twice and the values are incremented by 2 instead of 1 as intended. I have looked to see if I can merge the multiple CREATE statements and I cannot.
My initial idea is to separate out the different creates into a batch query, however I was wondering if there is another option.
Where are you executing this query? What version of neo4j are you using?
I went to console.neo4j.org and successfully ran the following and it correctly added one to both a.first and e.second:
START a = node(1), e = node(2)
CREATE a-[r:KNOWS]->b-[r2:KNOWS]->c, e-[:KNOWS]->b-[:KNOWS]->d
SET a.first=a.first+1, e.second=e.second+1
RETURN b

Resources