Constraining a node to unique but only for the relationship with another node

So for the project that I am working on, I was working on desigined the address to be split into several nodes (for ex city node, state node, country node and zip node)

Now the country nodes themselves are unique so that part is simple. However you can have cities with the same name in the world but not at state level. Also zipcodes are unique but they are unique within a country. I have been trying to figure out how I could implement that design.

Here is what I though of so far when limited to just the zip code and country:
type Country {
name : String!

type ZIPCode {
zipId: ID! (This is handled by apoc.create.uuid)
zipcode: String!
country : Country @cypher(
MATCH (this)-[IN_COUNTRY]->(c:Country) RETURN c LIMIT 1

Im not sure how to proceed from this point when dealing with creating a new zip code. Ideally I should merge if a zipcode exists within the same country but is that possible ? Or is this design flawed ?

The reason i have it set up as seperate nodes is so that I can query a specific node for addresses belong to nearby areas (For eg: Match all address - [havezip]->zipcode -[incountry]->country over searching address{zip: zip country:country} ) I expect this to be a very common type of query for my app so its what I want to optimize towards.

Having a clear understanding of the role of MERGE will help you here. You don't really 'merge if it exists' as much as you just 'merge', which means 'make this pattern exist exactly like this, but don't create it again if it already does'. Worth having a read of the docs.

With that in mind, it's certainly valid to run:

MERGE (zc:ZIPCode {zipcode:'12345'})-[:IN_COUNTRY]->(c:Country {name:'xyz'})

and you will have exactly one instance of zipcode 12345 in country xyz, whether it was already there or not.

You can add a uniqueness constraint to node properties such as zipcode, but as far as I'm aware you cannot constrain a node's relationships in this way, so merge carefully.

Ah Thanks a lot that helped. I read some more examples and that helped my understanding of merge. I do need to add the extra step of adding a match/merge on country so as not to recreate it but that seems trivial enough. Thanks a lot.

That's the power of merge - you probably don't need to do that. The statement:

MERGE (zc:ZIPCode {zipcode:'12345'})-[:IN_COUNTRY]->(c:Country {name:'xyz'})

will ensure there is a ZIPCode node with that exact id, a Country node with that exact name, and a relationship between them. MERGE works against the entire pattern, not just one specific node/relationship.

Does that graphQL create uniqueness constraints on the Zipcode label in the graph to have a zipcode and country name?
Or does it open up the potential for Zipcode 1 being connected to multiple Country nodes via other data access vectors?