Jeroen Steen Jeroen Steen - 1 year ago 178
JSON Question

MATCH blocks for Neo4j, to prevent "null property value"-error

I'm creating Person labelled nodes from a JSON, in Neo4j, with something like this (JSON is blank because of privacy/to much data):

WITH [] AS contacts
UNWIND contacts AS contact

But sometimes the person data doesn't have a first name and or last name,
so this query fails:

MERGE(personBind:Person {first_name: contact.first_name, last_name: contact.last_name})

It gives this error:

Cannot merge node using null property value

Is it possible to have a
, with something like these blocks in it:

CANT CREATE (Don't do something with person, but make other stuff)
CAN CREATE (Make person and relations to person, and make other stuff)
ON MATCH (Make relations to person, and make other stuff)

This is the other code, that I need to use,
but is to "attached" to person (Sometimes will fail):

FOREACH (addr IN contact.addrs |
MERGE addrPath=((zipBind:ZipCode {zipcode: addr.zipcode})-[nz:NUMBER_IN_ZIPCODE]->(houseBind:House {number: addr.housenumber})<-[ns:NUMBER_IN_STREET]-(streetBind:Street))


FOREACH( phoneValue IN | MERGE(phoneBind:Phone {number: phoneValue.value}) MERGE(personBind)-[:REACHABLE_BY]->(phoneBind) )
FOREACH( emailValue IN | MERGE(emailBind:Email {email: emailValue.value}) MERGE(personBind)-[:REACHABLE_BY]->(emailBind) )

Answer Source

Break your query up into two parts: first, address the universal parts (the contact data), then filter your query for people who have a first and last name and merge the other components.

UNWIND [] AS contact
WITH contact
UNWIND contact.addrs  AS addr
MERGE (zipBind:ZipCode {zipcode: addr.zipcode})
MERGE (streetBind:Street)  # does this need properties, like a name?
MERGE (streetBind) - [:NUMBER_IN_STREET] ->(houseBind:House {number: addr.housenumber})
MERGE (zipBind) -[:NUMBER_IN_ZIPCODE]-> (houseBind)
WITH contact, COLLECT(houseBind) AS houseBinds
UNWIND AS phoneValue
MERGE(phoneBind:Phone {number: phoneValue.value})
WITH contact, houseBinds, COLLECT(phoneBind) AS phoneBinds
UNWIND AS emailValue 
    MERGE(emailBind:Email {email: emailValue.value})
WITH contact, houseBinds, phoneBinds, COLLECT(emailBind) AS emailBinds


WHERE exists(contact.first_name) AND EXISTS(contact.last_name)
MERGE(personBind:Person {first_name: contact.first_name, last_name: contact.last_name})

FOREACH( housebind IN housebinds|MERGE(personBind)-[:WORKS_AT]->(houseBind))

    FOREACH( phoneBind IN phoneBinds | MERGE(personBind)-[:REACHABLE_BY]->(phoneBind) )

    FOREACH( emailBind IN emailBinds | ) MERGE(personBind)-[:REACHABLE_BY]->(emailBind) )

If you need rows that don't have Person names later in the query, you can change the filter step to create two lists, one that has names, one that doesn't, but that's more complicated than your question requires.

EDITED: Your Street nodes have no properties, so this query is going to find the same street for every row. Is there something you want to assign there?

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download