Cardinality and modeling relationships
|
This is the documentation of the GraphQL Library version 7. For the long-term support (LTS) version 5, refer to GraphQL Library version 5 LTS. |
Irrespective of the data model you have in your Neo4j database, the GraphQL schema should be designed in a way that allows the access to the data that your application needs. This means that there are often multiple ways to model the GraphQL schema given the same type of data in the database, each of which has benefits and tradeoffs.
This page describes the different ways of modeling a Neo4j relationship in a GraphQL schema.
Data model
Consider the following graph where a Person type has two different relationship types, which can connect it to a Movie type.
ACTED_IN and DIRECTED relationsModeling relationships
The data model represents the state of the graph in the database, but there are multiple ways to model the relationships in the GraphQL schema.
The way you model the relationships affects how you can query and manipulate the data, and it is important to choose a model that fits the needs of your application.
The following type definitions do not contain any relationship fields but are equally valid as a GraphQL schema.
However, they do not allow for traversing the relationships between Person and Movie nodes:
type Person @node {
name: String!
}
type Movie @node {
title: String!
released: Int!
}
A relationship does not have to be modeled on both sides of the relationship. While the underlying relationship in the database must have a source and a target node, it is possible for the GraphQL schema to only describe one side of the relationship.
In the following type definitions, a Movie has access to Person nodes through the actors relationship field, but a Person does not have access to Movie nodes:
type Person @node {
name: String!
}
type Movie @node {
title: String!
released: Int!
actors: [Person!]! @relationship(type: "ACTED_IN", direction: IN)
}
Many-to-many relationships
A Person can act in multiple movies, and a Movie can have multiple actors.
Conceptually this is a many-to-many relationship, which is modeled by using list types for the relationship fields on both sides of the relationship.
ACTED_IN a many-to-many relationshiptype Person @node {
name: String!
actedIn: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT)
}
type Movie @node {
title: String!
released: Int!
actors: [Person!]! @relationship(type: "ACTED_IN", direction: IN)
}
|
Both the list type and the type inside the list must be non-nullable: The relationship field must always return an array of values, and that array cannot contain null values. This means there is always a number of related nodes (even if that number is zero), and the related nodes are always valid nodes (and never null). Syntactically, both the array and the type inside require a |
One-to-many relationships
A Person can direct multiple movies but a Movie only has one director.
Conceptually this is a one-to-many relationship, which is modeled by using a list type for the relationship field on the Person type, and a non-list type for the relationship field on the Movie type.
DIRECTED a one-to-many relationshiptype Person @node {
name: String!
actedIn: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT)
directed: [Movie!]! @relationship(type: "DIRECTED", direction: OUT)
}
type Movie @node {
title: String!
released: Int!
actors: [Person!]! @relationship(type: "ACTED_IN", direction: IN)
director: Person @relationship(type: "DIRECTED", direction: IN)
}
|
On the "many" side of the relationship, the list type and the type inside the list must be non-nullable: The relationship field must always return an array of values, and that array cannot contain null values. Syntactically, both the array and the type inside require a On the "one-to" side of the relationship, the type must be nullable: It can return null if there is no related node. |
One-to-one relationships
A Person Ana can marry only one other Person Bob, and that other Person Bob can only be married to that Person Ana.
Conceptually this is a one-to-one relationship, which is modeled by using non-list types for the relationship field on both sides of the relationship.
MARRIED a one-to-one relationshiptype Person @node {
name: String!
spouse: Person @relationship(type: "MARRIED", direction: OUT)
}
In this case, the relationship is between nodes of the same type. This does not need to be the case in general.
|
The relationship field type must be nullable: It can return null if there is no related node. |
Cardinality
A non-list relationship field on a node type (the "one" side of a one-to-one or one-to-many relationship) is called a single relationship field.
Neo4j databases do not currently support constraints on relationship cardinality and therefore users must be mindful of the potential for data integrity issues in the case of APIs that allow for data manipulation on single relationships.