zmactep / hasbolt

Haskell driver for Neo4j 3+ (BOLT protocol)
BSD 3-Clause "New" or "Revised" License
82 stars 13 forks source link

variables as parameters #5

Closed Tshimanga closed 7 years ago

Tshimanga commented 7 years ago

I defined a data type

data A = A B C D type B = Text type C = Bool type D = Int

and i want to build a function that takes an A type and builds a node with label A and properties b,c and d out of it as follows

typeAtoNode pipe (A b c d) = run pipe $ query "create (n:A {B:b, C:c, D:d}) return n"

this however, rather understandably, throws an exception from Neo4j because the values of b,c, and d don't actually get interpolated into the query.

I tried also using queryP and also ran into a Neo4j exception there too as follows

typeAtoNode pipe (A b _ _) = run pipe $ queryP "create (n:A) where n.B contains {B} return n" (fromList [("B", T b)])

*** Exception: user error (code: "Neo.ClientError.Statement.SyntaxError", message: "Invalid input 'h': expected 'i/I' (line 1, column 18 (offset: 17))\n\"create (n:A) where n.B contains {B} return n\"\

How do I correctly use variables to interpolate node/relationship properties in the queries?

I suspect that the issue is with my understanding and not with the package itself, but I figured it'd be best to ask you for the clarification. Thanks for the help in advance!

zmactep commented 7 years ago

Hi, @Tshimanga!

The question is more about Cypher statement system then the driver itself. So, let us try to understand how this things works. There are no any string interpolation or something like this. Driver itself follow the protocol and do not provide any ORM or template system.

Each query is just a constant string, that is sent to the server. So we can run simple queries, that are presented by Text datatype, like "MATCH (n:Person {name: "Carl"}) RETURN n". So, this query will run just the same operation each time it is sent.

But of course we want some dynamic queries, where we can change values as we need:

Neo4j lets us to send a statement and parameters separately. So it will cache our statements and just change some values in them. Parameters are presented as a map with string keys. So, we can place some {hole}s inside our statement and the name of the hole will we a key in such map. The system is pretty reach, as it let us use not only primitive types, but also maps and lists. Here is some examples:

let name = T "Carl"
let paramsMap = M (fromList [("name", name)])
queryP "MATCH (n:Person) WHERE n.name = {name} RETURN n" (fromList [("name", name)])
queryP "MATCH (n:Person {params}) RETURN n" (fromList [("params", paramsMap)])

I hope, it has clarified the situation a bit.

Tshimanga commented 7 years ago

Thanks @zmactep