Jeroen Steen Jeroen Steen - 2 months ago 33
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
MATCH
, with something like these blocks in it:

NOT MATCH
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))

MERGE(personBind)-[:WORKS_AT]->(houseBind)
)

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

Answer

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 contact.phone AS phoneValue
MERGE(phoneBind:Phone {number: phoneValue.value})
WITH contact, houseBinds, COLLECT(phoneBind) AS phoneBinds
UNWIND contact.email AS emailValue 
    MERGE(emailBind:Email {email: emailValue.value})
WITH contact, houseBinds, phoneBinds, COLLECT(emailBind) AS emailBinds

<NOW FILTER FOR CONTACTS WITH PERSON DATA>

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?

Comments