Ignore properties that already exist on the node when SETting a `+=` map: Possible?

Hi, thanks for your response in advance. I just need somebody to confirm that i read the docs correctly and there is no further way to improve this. I have the query below as an example. (Neo4j 5.19)

My goal is to be able to "upsert" a node with a bunch of extra properties ("pfam_comments" etc. in this example) without overwriting the properties that already exist on that node. The += operator works really well for my purpose here, except I read in "Mutate Specific Properties" of the docs that maybe i can pass an "overkill" dict/map to the +=:

  • Any properties that are in both the map and the node or relationship will be replaced in the node or relationship. However, if any property in the map is null, it will be removed from the node or relationship.

So i understand that in my example auth_asym_id=3 will be replaced with auth_asym_id=4 from the map. Is there any way in this setup to instruct cypher to only set properties that DONT already exist on the node? I.e., again, since pfam_* properties aren't on the node already, i'd like them to be added, but since the auth_asym_id DOES exist on the node, i'd like it to be ignored in the += map.

match (n {auth_asym_id:3}) where elementid(n)="4:319085fa-9f6d-4e67-8536-bd4f3202729f:295" 
    set n+={pfam_accessions:["12","12"],
            pfam_comments:['hie', "haw"],
            pfam_descriptions:['dasome edsad'],
            uniprot_accession:["Q3123123"],
            auth_asym_id: 4}, n:Protein
return n

If there isn't such a facility i'd guess it'd be pretty handy to have for "marshalling" nodes and relationships "up" in case the properties that are defined on them are subsets of a objects in people's backend (this is my case). Simply, let's say someone rolls a neo4j to track Customers and PremiumCustomers and this is their anylang schema in backend:

# pseudocode
class Customer
name: str
age: in


class PremiumCustomer(Customer):
name:str
age:str
subscription: uuid

Except these class objects have hundreds of fields/properties. If i want to upgrade my neo4j customer node to a premiumcustomer node, the ideal op for me is just

match (c:Customer) where elementid(c)="..."
set c+=premiumcustomer_dict
return c
```

Using a function from APOC Procedures, you can remove from the map any entries corresponding with an existing property key on the node. Here's an example of use:

WITH {name:'Mr Anderson', theOne:'true'} as map

MATCH (k:Person {name:'Keanu Reeves'})
WITH k, apoc.map.removeKeys(map, keys(k)) as onlyNewPropsMap
SET k += onlyNewPropsMap

The name property will remain as 'Keanu Reeves', as we removed that entry from the map, given that the node already has a 'name' property key.

1 Like

Whoa. Thanks, Andy. This is it.