关系
如果没有关系,您的类型定义会更像是一组断开的节点,没有太大价值。在您的数据模型中添加关系可以为您的数据提供必要的上下文,以便在图表的各个部分运行复杂查询。
本页介绍如何为简单的连接模型编写类型定义,通过模式插入数据,然后查询数据。
类型定义
以以下图表为例,其中 Person
类型有两种不同的关系类型,可以将其连接到 Movie
类型。
要使用 Neo4j GraphQL 库创建该图表,首先需要定义节点并定义此模型中的两种不同类型。
type Person {
name: String!
born: Int!
}
type Movie {
title: String!
released: Int!
}
然后可以使用 @relationship
指令将这两种类型连接在一起。
type Person {
name: String!
born: Int!
actedInMovies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT)
directedMovies: [Movie!]! @relationship(type: "DIRECTED", direction: OUT)
}
type Movie {
title: String!
released: Int!
actors: [Person!]! @relationship(type: "ACTED_IN", direction: IN)
director: Person! @relationship(type: "DIRECTED", direction: IN)
}
请注意,在此查询中
-
一个
Person
可以出演或执导多部电影,而一部Movie
可以有多个演员。但是,一部Movie
很少会有不止一位导演,因此您可以在类型定义中对这种基数进行建模,以确保数据的准确性。 -
一部
Movie
实际上不是一部Movie
,除非它有导演,并且这已通过将director
字段标记为不可为空来表示。这意味着Movie
必须有一个指向它的DIRECTED
关系才能有效。 -
要确定
@relationship
指令的direction
参数应该是IN
还是OUT
,请像上面的图示一样可视化您的关系,然后建模箭头方向。 -
@relationship
指令是对 Neo4j 关系的引用,而在模式中,使用短语edge(s)
来与 Relay 使用的通用 API 语言保持一致。
关系属性
您可以通过两个步骤将关系属性添加到示例中。
-
添加一个用
@relationshipProperties
指令装饰的类型定义,其中包含所需的属性。 -
在
@relationship
指令的两个“侧”(或您喜欢的任何一侧)都添加一个properties
参数,该参数指向新定义的接口。
例如,假设您想区分演员在电影中扮演的角色
type Person {
name: String!
born: Int!
actedInMovies: [Movie!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: OUT)
directedMovies: [Movie!]! @relationship(type: "DIRECTED", direction: OUT)
}
type Movie {
title: String!
released: Int!
actors: [Person!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: IN)
director: Person! @relationship(type: "DIRECTED", direction: IN)
}
type ActedIn @relationshipProperties {
roles: [String!]
}
@declareRelationship
如果您需要在接口上使用关系,则需要使用新的 @declareRelationship
指令,并定义具体类型中的关系
interface Production {
title: String!
actors: [Actor!]! @declareRelationship
}
type Actor {
name: String!
born: Int!
actedInMovies: [Movie!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: OUT)
}
type Movie implements Production {
title: String!
released: Int!
actors: [Actor!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: IN)
}
type Series implements Production {
title: String!
released: Int!
episodes: Int!
actors: [Actor!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: IN)
}
type ActedIn @relationshipProperties {
roles: [String!]
}
queryDirection
所有关系都有方向。但是,在查询它们时,可以执行 无向查询。要设置关系在查询时的默认行为,可以使用参数 queryDirection
type Person {
name: String!
born: Int!
actedInMovies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT, queryDirection: DEFAULT_DIRECTED)
}
queryDirection
可以具有以下值
-
DEFAULT_DIRECTED
(默认):默认情况下,所有查询都是有向的,但用户可以执行无向查询。 -
DEFAULT_UNDIRECTED
:默认情况下,所有查询都是无向的,但用户可以执行有向查询。 -
DIRECTED_ONLY
:只能对该关系执行有向查询。 -
UNDIRECTED_ONLY
:只能对该关系执行无向查询。
插入数据
嵌套变异意味着您可以通过 GraphQL 模式以多种方式将数据插入数据库中。请考虑前面提到的规则,即不能在不添加导演的情况下创建 Movie
节点。但是,您可以先创建一个导演节点,然后创建并将其连接到 Movie
。另一种选择是在同一变异中创建 Movie
和 Director
,例如
mutation CreateMovieAndDirector {
createMovies(input: [
{
title: "Forrest Gump"
released: 1994
director: {
create: {
node: {
name: "Robert Zemeckis"
born: 1951
}
}
}
}
]) {
movies {
title
released
director {
name
born
}
}
}
}
然后,您需要在本例中创建演员,并将它们连接到新的 Movie
节点,并指定他们扮演的角色
mutation CreateActor {
createPeople(input: [
{
name: "Tom Hanks"
born: 1956
actedInMovies: {
connect: {
where: {
node: { title: "Forrest Gump" }
}
edge: {
roles: ["Forrest"]
}
}
}
}
]) {
movies {
title
released
director {
name
born
}
actorsConnection {
edges {
roles
node {
name
born
}
}
}
}
}
}
请注意 actorsConnection
字段的选择,以便查询 roles
关系属性。
还要注意,在第二次变异中,返回了整个图表。这不是必需的,因为您可以将这些变异压缩为一个插入所有必要数据的单一操作
mutation CreateMovieDirectorAndActor {
createMovies(input: [
{
title: "Forrest Gump"
released: 1994
director: {
create: {
node: {
name: "Robert Zemeckis"
born: 1951
}
}
}
actors: {
create: [
{
node: {
name: "Tom Hanks"
born: 1956
}
edge: {
roles: ["Forrest"]
}
}
]
}
}
]) {
movies {
title
released
director {
name
born
}
actorsConnection {
edges {
roles
node {
name
born
}
}
}
}
}
}
认识到这一点有助于您在一个变异中一次创建更大的子图表,从而更有效地进行操作。