简体   繁体   中英

CREATE/SET a property that came from a parameter object in Neo4j

In a query, I supply the following parameters:

id : 'some unique ID',
used : [an array of md5 checksums]

with the following query:

MATCH (a {id:{id}}) , (b)
WHERE b.md5 IN {used}
CREATE UNIQUE (a)-[]->(b)

and everything is wonderful. If there are 10 MD5 checksums in the used array, 10 relationships will be made to them from node "a". Cool.

But now say I have the need to add a property to that relationship - and that property will depend on the node b.

So now I have an extra parameter, an object, that looks like this:

info : {
'5fb1be1279031c1f1c65a928eb823e51': 'yolo',
'0aab9f8e81684ec778f8c0c5717f37c2': 'swag',
...
}

The MD5 keys in this object match the MD5 strings in the used array.

My first instinct would be to do this:

MATCH (a {id:{id}}) , (b)
WHERE b.md5 IN {used}
CREATE UNIQUE (a)-[{ meme:{info}[b.md5] }]->(b)

Because that doesn't work. I get the error:

{ [neo4j.ClientError: [Neo.ClientError.Statement.InvalidType] Expected e1701806eda7d3ab52b143cc03d94e75 to be a java.lang.Number, but it was a java.lang.String] message: '[Neo.ClientError.Statement.InvalidType] Expected e1701806eda7d3ab52b143cc03d94e75 to be a java.lang.Number, but it was a java.lang.String', neo4j: { code: 'Neo.ClientError.Statement.InvalidType', message: 'Expected e1701806eda7d3ab52b143cc03d94e75 to be a java.lang.Number, but it was a java.lang.String' }, name: 'neo4j.ClientError' }

If anyone could help i would be immensely grateful because I'm totally and utterly stuck on this :/

After taking a good look at Stefan's blog, I found I could use the following Cypher to achieve what I need. It's not pretty, but until there is an easier way to conditionally CREATE things than FOREACH/CASE tricks, it will have to do:

First split your object of key/value pairs into two arrays:

> fileMD5  : ['5fb1be1279031c1f1c65a928eb823e51','0aab9f8e81684ec778f8c0c5717f37c2'..]
> fileInfo : ['yolo','swag'...]

Then write the expression as so:

MATCH (e:event {id:{id}}),(r:resource)
WHERE r.md5 IN {used}
FOREACH(
  idx in RANGE(0,SIZE({fileMD5})-1) |
  FOREACH( 
    filePath IN CASE WHEN r.md5 = {fileMD5}[idx] THEN [{fileInfo}[idx]] ELSE [] END |
    CREATE UNIQUE (r)-[:USED_BY {filePath:filePath }]->(e)
  )
)

I wrote this question 8 hours ago, which means these few little lines took 8 hours of hair-pulling to get. I hope someone else finds it useful :P

EDIT: A bit of an explanation as to how/why it works...

The first FOREACH iterates a RANGE from 0 to (the length of either the key or value array -1), with the idx variable being the result. This is a common trick for iterating two arrays of the same length simultaneously.

The second FOREACH is a bit more complicated. It iterates an array, which is created by the CASE, with the result being called filePath. The CASE however only ever returns either an array with nothing in it [], or and array with the value we want to set in our CREATE. So depending on the CASE, the FOREACH will either do something once, or do nothing at all.

The CASE is very simple. When the index pulls out a key that matches what we want (r.md5 = {fileMD5}[idx]) then it returns an array with one value - the value in the value array using the same index as what matched in the key array.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM