neo4j-labs / neo4rs

Neo4j driver for rust

Home Page:https://docs.rs/neo4rs

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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