Create a relationship only if a similar one exist - neo4j - neo4j

I have a linked list such that every node has a 'next' relation pointed to the next node, the first node has another relation : 'first' which pointed to it from some category. I'm trying to write a delete function that uses cypher to do so but I keep getting this error :
Received an unexpected HTTP status when executing the request.
The query was: MATCH (c:Cat {id:{id}})-[:Post]->(p:Post{id:{pid}})
My try :
gclient.Cypher
.Match("(c:Cat {id:{id}})-[:Post]->(p:Post{id:{pid}})")
.Match("(p)-[nn:next]->(np:Post)")
.OptionalMatch("(c)-[old:first]->(p)")
.OptionalMatch("(ppost:Post)-[pn:next]->(p)")
.WithParams((new
{
id = catId,
pid = postId
}))
.Create("(ppost)-[:next]->(np)")
.With("old, p, c, np")
.Where("old IS NOT NULL")
.Create("(c)-[:first]->(np)")
.With("p, old")
.Delete("old")
.Delete("p")
.ExecuteWithoutResults();

Related

Neo4j cypher query fails with unknown syntax error

I have the following paramObj and dbQuery
paramObj = {
email: newUser.email,
mobilenumber: newUser.telephone,
password: newUser.password,
category: newUser.category,
name: newUser.name,
confirmuid: verificationHash,
confirmexpire: expiryDate.valueOf(),
rewardPoints: 0,
emailconfirmed: 'false',
paramVehicles: makeVehicleArray,
paramVehicleProps: vehiclePropsArray
}
dbQuery = `CREATE (user:Person:Owner {email:$email})
SET user += apoc.map.clean(paramObj,
['email','paramVehicles','paramVehiclesProps'],[])
WITH user, $paramVehicles AS vehicles
UNWIND vehicles AS vehicle
MATCH(v:Vehicles {name:vehicle})
CREATE UNIQUE (user)-[r:OWNS {since: timestamp()}]->(v)
RETURN user,r,v`;
Then I tried to execute
commons.session
.run(dbQuery, paramObj)
.then(newUser => {
commons.session.close();
if (!newUser.records[0]) {........
I am getting
Error: {"code":"Neo.ClientError.Statement.SyntaxError","name":"Neo4jError"}
which doesn't direct me anywhere. Can anyone tell me what am I doing wrong here?
This is actually the first time I am using the query format .run(dbQuery, paramObj) but this format is critical to my use case. I am using Neo4j 3.4.5 community with apoc plugin installed.
Ok...so I followed #inversFalcon suggestion to test in browser and came up with following parameters and query that closely match the ones above:
:params paramObj:[{ email:"xyz123#abc.com", mobilenumber:"8711231234",password:"password1", category:"Owner",name:"Michaell",vehicles:["Toyota","BMW","Nissan"],vehicleProps: [] }]
and query
PROFILE
CREATE (user:Person:Owner {email:$email})
SET user += apoc.map.clean($paramObj, ["email","vehicles","vehicleProps"],[])
WITH user, $vehicles AS vehicles
UNWIND vehicles AS vehicle
MATCH(v:Vehicles {name:vehicle})
MERGE (user)-[r:OWNS {since: timestamp()}]->(v)
RETURN user,r,v;
Now I get
Neo.ClientError.Statement.TypeError: Can't coerce `List{Map{name -> String("Michaell"), vehicles -> List{String("Toyota"), String("BMW"), String("Nissan")},.......
I also reverted to neo4j 3.2 (re: an earlier post by Mark Needham) and got the same error.
You should try doing an EXPLAIN of the query using the browser to troubleshoot it.
A few of the things I'm seeing here:
You're referring to paramObj, but it's not a parameter (rather, it's the map of parameters you're passing in, but it itself is not a parameter you can reference in the query). If you need to reference the entire set of parameters being passed in, then you need to use nested maps, and have paramObj be a key in the map that you pass as the parameter map (and when you do use it in the query, you'll need to use $paramObj)
CREATE UNIQUE is deprecated, you should use MERGE instead, though be aware that it does behave in a different manner (see the MERGE documentation as well as our knowledge base article explaining some of the easy-to-miss details of how MERGE works).
I am not sure what caused the coercion error to disappear but it did with the same query and I got a "expected parameter error" this was fixed by using $paramObj.email, etc. so the final query looks like this:
CREATE (user:Person:Owner {email: $paramObj.email})
SET user += apoc.map.clean($queryObj, ["email","vehicles","vehicleProps"],[])
WITH user, $paramObj.vehicles AS vehicles
UNWIND vehicles AS vehicle
MATCH(v:Vehicles {name:vehicle})
MERGE (user)-[r:OWNS {since: timestamp()}]->(v)
RETURN user,r,v;
which fixed my original problem of how to remove properties from a map when using SET += map.

How to create multiple node via looping in py2neo for neo4j

Kindly help me to resolve this issue. I am following the tutorial on this link: https://www.kernix.com/blog/an-efficient-recommender-system-based-on-graph-database_p9 . I am unable to modify the following so that it could comply with the new format of py2neo v3 where graph.run is used instead of graph.cypher.begin(). The purpose of the code below is to Create the nodes relative to Users, each one being identified by its user_id and "MERGE" request : creates a new node if it does not exist already
tx = graph.cypher.begin()
statement = "MERGE (a:`User`{user_id:{A}}) RETURN a"
for u in user['id']:
tx.append(statement, {"A": u})
tx.commit()
Thank you very much in advance
With v3 of py2neo your snippet would look like this:
tx = graph.begin()
statement = "MERGE (a:`User`{user_id:{A}}) RETURN a"
for u in user['id']:
tx.run(statement, {"A": u})
tx.commit()
begin() is a method on the Graph class, which will create a new transaction.
Transaction.run will send a Cypher statement to the server for execution - but not commit the transaction until Transaction.commit is called.

return deleted node after DETACH DELETE

I have a problem to get node record in result after I deleted the node.
My query is:
MATCH (user:User)-[:CREATED]->(comment:Comment)-[:BELONGS_TO]->(post:Post)
WHERE comment.uuid = {commentUUID} AND post.uuid = {postUUID} AND user.uuid = {userUUID} AND NOT EXISTS(user.deleted)
DETACH DELETE comment
RETURN comment
Here I am deleting comment which belongs to some post (BELONGS_TO). I also check if user who wants to delete the comment is also author of this comment (CREATED).
Query works, the deletion of node and relationship happen successfully, but I am not satisfied with returned value.
It shows me this result instead:
{ metadata: { deleted: true, id: 89 },
paged_traverse: 'http://myDatabaseURI/db/data/node/89/paged/traverse/{returnType}{?pageSize,leaseTime}',
outgoing_relationships: 'http://myDatabaseURI/db/data/node/89/relationships/out',
outgoing_typed_relationships: 'http://myDatabaseURI/db/data/node/89/relationships/out/{-list|&|types}',
labels: 'http://myDatabaseURI/db/data/node/89/labels',
create_relationship: 'http://myDatabaseURI/db/data/node/89/relationships',
traverse: 'http://myDatabaseURI/db/data/node/89/traverse/{returnType}',
all_relationships: 'http://myDatabaseURI/db/data/node/89/relationships/all',
all_typed_relationships: 'http://myDatabaseURI/db/data/node/89/relationships/all/{-list|&|types}',
property: 'http://myDatabaseURI/db/data/node/89/properties/{key}',
self: 'http://myDatabaseURI/db/data/node/89',
incoming_relationships: 'http://myDatabaseURI/db/data/node/89/relationships/in',
properties: 'http://myDatabaseURI/db/data/node/89/properties',
incoming_typed_relationships: 'http://myDatabaseURI/db/data/node/89/relationships/in/{-list|&|types}' }
Thanks for help!
Edit:
I hide my db URI in result and replaced it with myDatabaseURI
As far as I can tell, when you delete a node, you cannot return its properties by returning the node itself.
However, you can get the properties of the node before the deletion, and return that after deleting the node itself:
MATCH (user:User)-[:CREATED]->(comment:Comment)-[:BELONGS_TO]->(post:Post)
WHERE comment.uuid = {commentUUID} AND post.uuid = {postUUID} AND user.uuid = {userUUID} AND NOT EXISTS(user.deleted)
WITH comment, properties(comment) as props
DETACH DELETE comment
RETURN props
This won't get you the node id, though, so if you need to return that, you can use map projection to get all properties plus the node id instead.

Cypher query for externalized property values

in our data model we have externalized some of our domain entities values into external nodes. The model for a service object looks like:
ref=node(0),
ref<-[:SERVICE]-subRefNode<-[:SERVICE]-aService-[:HAS_PROPERTY_VALUE]->propValueNode-[:IS_OF_TYPE]->propDefType,
ref<-[:SERVICE]-subRefNode-[:HAS_PROPERTY]->propDefType
The node subRefNode holds a relationship to all services. All possible properties for a service are defined through ref<-[:SERVICE]-subRefNode-[:HAS_PROPERTY]->propDefType. So it could be that a certain node doesn't have a propValueNode yet for a certain property (e.g. a comment) and others might have one but its empty (the user might have entered a comment and then cleared it).
So my question is how do I get the nodes with an empty propValueNode.value and also the ones that do not have a propertyValueNode yet? I thought about something like...
START ref=node(0) MATCH ref<-[:SERVICE]-subRef, aService-[pvRel?:HAS_PROPERTY_VALUE]->propValueNode-[pdRel:IS_OF_TYPE]->propDef<-[:HAS_PROPERTY]-subRef<-[:SERVICE]-aService WHERE (pvRel IS NOT NULL AND propDef.name = 'comment' AND propValueNode.value=~"^$") OR (pvRel IS NULL AND pdRel IS NULL AND propDef.name="comment") RETURN DISTINCT aService.debug
http://console.neo4j.org/r/7zeoay
...but this misses the ones without a propValueNode. Any hints are appreciated!
Regards,
Andi
If http://console.neo4j.org/r/7zeoay is not valid anymore, here's the initial graph setup:
start _0 = node(0) with _0
create
(_1 {type:"SubReferenceNode", name:"SubRef"}),
(_2 {type:"Service", debug:"S0 empty value"}),
(_3 {type:"Service", debug:"S1 missing value node"}),
(_4 {type:"Service", debug:"S2 with value"}),
(_5 {type:"PropertyDefintion", name:"comment"}),
(_6 {type:"PropertyDefintion", name:"name"}),
(_7 {type:"PropertyValue", value:"S0 empty value"}),
(_8 {type:"PropertyValue", value:"S1 missing value node"}),
(_9 {type:"PropertyValue", value:"S2 with value"}),
(_10 {type:"PropertyValue", value:""}),
(_11 {type:"PropertyValue", value:"This is a comment"}),
_0<-[:SERVICE]-_1,
_1<-[:SERVICE]-_2,
_1<-[:SERVICE]-_3,
_1<-[:SERVICE]-_4,
_1-[:HAS_PROPERTY]->_5,
_1-[:HAS_PROPERTY]->_6,
_2-[:HAS_PROPERTY_VALUE]->_7,
_7-[:IS_OF_TYPE]->_6,
_3-[:HAS_PROPERTY_VALUE]->_8,
_8-[:IS_OF_TYPE]->_6,
_4-[:HAS_PROPERTY_VALUE]->_9,
_9-[:IS_OF_TYPE]->_6,
_2-[:HAS_PROPERTY_VALUE]->_10,
_10-[:IS_OF_TYPE]->_5,
_4-[:HAS_PROPERTY_VALUE]->_11,
_11-[:IS_OF_TYPE]->_5
You can combine two queries, one is to return the service which has an empty value for the property "comment", and another returns the service which does not have a propery value that is of the type "comment", in other word, none of the service property values is of the the type "comment".
START ref=node(0)
MATCH ref<-[:SERVICE]-subRef, aService-[pvRel?:HAS_PROPERTY_VALUE]->propValueNode-[pdRel:IS_OF_TYPE]->propDef<-[:HAS_PROPERTY]-subRef<-[:SERVICE]-aService
WHERE (pvRel IS NOT NULL AND propDef.name = 'comment' AND propValueNode.value=~"^$")
RETURN DISTINCT aService.debug
UNION
START ref=node(0)
MATCH ref<-[:SERVICE]-subRef, aService-[pvRel:HAS_PROPERTY_VALUE]->propValueNode-[pdRel:IS_OF_TYPE]->propDef<-[:HAS_PROPERTY]-subRef<-[:SERVICE]-aService
with aService, collect(propDef.name) as propNames
WHERE NONE( propName in propNames where propName = 'comment')
RETURN DISTINCT aService.debug
The alternative solution without using the "UNION" clause is to get all value nodes for each service and returns only those service that either none of its values is of type "comment" or there is one value of the type "comment" and the value is empty, as shown in the "Where" clause below,
START ref=node(0)
MATCH ref<-[:SERVICE]-subRef, aService-[pvRel:HAS_PROPERTY_VALUE]->propValueNode-[pdRel?:IS_OF_TYPE]->propDef<-[:HAS_PROPERTY]-subRef<-[:SERVICE]-aService
WHERE propDef.name = 'comment'
WITH aService, collect(propValueNode) AS valueNodes, propDef
WHERE NONE (v IN valueNodes
WHERE v-[:IS_OF_TYPE]->propDef) OR SINGLE (v IN valueNodes
WHERE v.value=~"^$" AND v-[:IS_OF_TYPE]->propDef)
RETURN aService.debug

py2neo create function creating duplicate nodes

I have a Neo4j database containing information on Congressmen. The problem I'm having is if there is a vacant position. When this happens I am using the same key:value in the "Congressmen" index. I tried the code below because in the py2neo documentation it states that the add function is idempotent
#Check if we have any vacancies and if so if they match the one that we currently want to add
query="start n=node:Congressmen('website:N/A') return n"
result= cypher.execute(graph_db, query.encode('utf-8', errors='ignore'))
#Match what we already have
if str(result[0]) != "[]":
#create is idempotent so will only create a new node if properties are different
rep, = graph_db.create({"name" : userName, "website" : web, "district" : int(district), "state" : child[2].text, "party" : child[4].text, "office" : child[5].text, "phone" : child[6].text, "house" : "House of Representatives"})
cong = graph_db.get_or_create_index(neo4j.Node, "Congressmen")
# add the node to the index
cong.add("website", web, rep)
When I checked the interface after running the code 3 times I have duplicate nodes.
Is it possible to prevent the nodes from duplicating and still be able to index them using the same key/value?
The Index.add method is certainly idempotent: the same entity can only be added once to a particular entry point. The GraphDatabaseService.create method is not however. Each time you run the create method, a new node is created and each run of add appends that new node to the index. You probably want to use the Index.add_if_none, Index.create_if_none or Index.get_or_create method instead.

Resources