How to update the properties of a node?
lucasfelber opened this issue · comments
I am currently trying to implement simple CRUD for my user nodes but I can't figure out how to properly update a node.
let query = query("MATCH (user:user {uuid: $uuid}) SET $update_set RETURN user")
.param("uuid", uuid)
.param("update_set", update_set);
This is my current approach with uuid
and update_set
being Strings.
update_set
is a String that's being constructed and would look something like: user.email = 'updated@mail.com',user.name = 'Bob'
I can't make much of the error message either being:
thread 'actix-rt|system:0|arbiter:0' panicked at 'called `Result::unwrap()` on an `Err` value: UnexpectedMessage("unexpected response for RUN: Ok(Failure(Failure { metadata: BoltMap { value: {BoltString { value: \"message\" }: String(BoltString { value: \"Invalid input 'RETURN': expected \\\".\\\" (line 1, column 49 (offset: 48))\\n\\\"MATCH (user:user {uuid: $uuid}) SET $update_set RETURN user\\\"\\n ^\" }), BoltString { value: \"code\" }: String(BoltString { value: \"Neo.ClientError.Statement.SyntaxError\" })} } }))")', src\persistence\user_repo.rs:33:49
This code doesn't feel proper so is there a better way of doing things?
For now I fixed this by changing the code to:
let query_string = format!("MATCH (user:user {{uuid: $uuid}}) SET {} RETURN user", update_set);
let query = query(query_string.as_str())
.param("uuid", uuid);
This still doesn't feel right
Hi @lucasfelber,
Cypher parameters cannot be used the way you described, you can read more about it in the Cypher manual
If you have a consistent set of properties, the query would look like
let query = query("MATCH (user:user {uuid: $uuid}) SET user.email = $email, user.name = $name RETURN user")
.param("uuid", uuid)
.param("email", email)
.param("name", name);
If you want to update a variable amount of properties, you could try using +=
let mut update_set = BoltMap::default();
// possibly
update_set.put("email".into(), email.into());
// possibly
update_set.put("name".into(), name.into());
let update_set = BoltType::Map(update_set);
let query = query("MATCH (user:user {uuid: $uuid}) SET user += $update_set RETURN user")
.param("uuid", uuid)
.param("update_set", update_set)
.param("name", name);
Otherwise your workaround is also fine, just be aware that this opens up the possibility of Cypher injection if the name/email values come from an untrusted source. It also would create multiple different queries to plan/cache for the Cypher runtime, which might or might not make a difference in your use-case.
@knutwalker Thank you this is exactly what I need. Though I am having trouble using BoltType and BoltMap since I can't seem to import them.
@lucasfelber We've just released 0.7.0-rc.1
, where this should work now. I can ru this in evcxr
>> :dep neo4rs="0.7.0-rc.1"
// truncate output
Compiling neo4rs v0.7.0-rc.1
>> use neo4rs::{BoltMap,BoltType};
>> let mut update_set = BoltMap::default();
>> update_set.put("email".into(), "email".into());
>> update_set.put("name".into(), "name".into());
>> let update_set = BoltType::Map(update_set);
>> let query = neo4rs::query("MATCH (user:user {uuid: $uuid}) SET user += $update_set RETURN user")
.param("uuid", "uuid")
.param("update_set", update_set)
.param("name", "name");
Could you try with that version and see if it works on your end?
@knutwalker Yes upgrading the version did the trick. Thank you