关系

这是 GraphQL 库版本 7 的文档。对于长期支持 (LTS) 版本 5,请参阅 GraphQL 库版本 5 LTS

如果没有关系,您的类型定义更像是断开连接的节点集合,价值不大。将关系添加到您的数据模型中,可以为您的数据提供所需的上下文,以便在图的广泛部分运行复杂的查询。

本页描述了如何为简单的连接模型编写类型定义、通过 schema 插入数据,然后查询数据。

类型定义

以上图为例,其中 Person 类型有两种不同的关系类型,可以将其连接到 Movie 类型。

relationships
图 1. 示例图

要使用 Neo4j GraphQL 库创建该图,首先需要定义节点并定义此模型中的两种不同类型

type Person @node {
    name: String!
    born: Int!
}

type Movie @node {
    title: String!
    released: Int!
}

然后可以使用 @relationship 指令将这两种类型连接起来

type Person @node {
    name: String!
    born: Int!
    actedInMovies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT)
    directedMovies: [Movie!]! @relationship(type: "DIRECTED", direction: OUT)
}

type Movie @node {
    title: String!
    released: Int!
    actors: [Person!]! @relationship(type: "ACTED_IN", direction: IN)
    directors: [Person!]! @relationship(type: "DIRECTED", direction: IN)
}

请注意,在此查询中

  • 一个 Person 可以出演导演多部电影,一部 Movie 可以有多个演员。虽然一部 Movie 很少有多个导演,但 Neo4j GraphQL 库要求所有关系都建模为“多对多”关系(使用列表)。

  • 即使是概念上的一对一关系,例如一部电影只有一个导演,也必须表示为数组(例如,directors: [Person!]!),因为 Neo4j 无法可靠地强制节点之间的一对一基数。

  • 要确定 @relationship 指令的 direction 参数应该是 IN 还是 OUT,请像上图一样可视化您的关系,然后建模箭头的方向。

  • @relationship 指令是对 Neo4j 关系的引用,而在 schema 中,使用短语 edge(s) 是为了与 Relay 使用的通用 API 语言保持一致。

Neo4j GraphQL 基数限制

重要的是要理解 Neo4j 无法可靠地强制图数据库中节点之间的一对一基数。因此,Neo4j GraphQL 中的所有关系都建模为“多对多”关系(使用列表),即使它们概念上表示一对一关系。作为替代,您可以使用 @cypher 指令定义关系,但 Neo4j GraphQL 库提供的一些功能将不可用。

关系属性

您可以通过两个步骤向示例添加关系属性

  1. 添加一个用 @relationshipProperties 指令装饰的类型定义,其中包含所需的关系属性。

  2. @relationship 指令的两侧(或仅一侧,如果您愿意)添加一个 properties 参数,该参数指向新定义的接口。

关系属性字段只能是原始类型或其列表变体。您不能将复杂类型(如对象类型)映射到建模关系属性的类型。

例如,假设您想区分演员在电影中扮演的角色

type Person @node {
    name: String!
    born: Int!
    actedInMovies: [Movie!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: OUT)
    directedMovies: [Movie!]! @relationship(type: "DIRECTED", direction: OUT)
}

type Movie @node {
    title: String!
    released: Int!
    actors: [Person!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: IN)
    directors: [Person!]! @relationship(type: "DIRECTED", direction: IN)
}

type ActedIn @relationshipProperties {
    roles: [String!]
}

@declareRelationship

如果您需要在接口上提供关系,则需要改用新的 @declareRelationship 指令,并在具体类型中定义关系

interface Production {
    title: String!
    actors: [Actor!]! @declareRelationship
}

type Actor @node {
    name: String!
    born: Int!
    actedInMovies: [Movie!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: OUT)
}

type Movie implements Production @node {
    title: String!
    released: Int!
    actors: [Actor!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: IN)
}

type Series implements Production @node {
    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 @node {
    name: String!
    born: Int!
    actedInMovies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT, queryDirection: DEFAULT_DIRECTED)
}

queryDirection 可以有以下值: * DIRECTED:只能对该关系执行有向查询。 * UNDIRECTED:只能对该关系执行无向查询。

插入数据

嵌套突变意味着您可以通过 GraphQL schema 以多种方式将数据插入数据库。考虑前面提到的规则,即不能在不添加导演的情况下创建 Movie 节点。但是,您可以先创建一个导演节点,然后创建并将其连接到 Movie。另一种选择是在同一个突变中创建 MovieDirector,例如

mutation CreateMovieAndDirector {
    createMovies(input: [
        {
            title: "Forrest Gump"
            released: 1994
            directors: {
                create: [{
                    node: {
                        name: "Robert Zemeckis"
                        born: 1951
                    }
                }]
            }
        }
    ]) {
        movies {
            title
            released
            directors {
                name
                born
            }
        }
    }
}

然后您需要在此示例中创建演员,并将其连接到新的 Movie 节点,同时指定他们扮演的角色

mutation CreateActor {
  createPeople(
    input: [
      {
        name: "Tom Hanks"
        born: 1956
        actedInMovies: {
          connect: {
            where: { node: { title: { eq: "Forrest Gump" } } }
            edge: { roles: ["Forrest"] }
          }
        }
      }
    ]
  ) {
    people {
      name
      born
      actedInMovies {
        title
        released
        actorsConnection {
          edges {
            properties {
              roles
            }
            node {
              name
              born
            }
          }
        }
      }
    }
  }
}

请注意选择 actorsConnection 字段以查询 roles 关系属性。

另请注意,在第二个突变中,返回了整个图。这没有必要,因为您可以将这些突变压缩成一个操作,一次性插入所需的所有数据

mutation CreateMovieDirectorAndActor {
    createMovies(input: [
        {
            title: "Forrest Gump"
            released: 1994
            directors: {
                create: {
                    node: {
                        name: "Robert Zemeckis"
                        born: 1951
                    }
                }
            }
            actors: {
                create: [
                    {
                        node: {
                            name: "Tom Hanks"
                            born: 1956
                        }
                        edge: {
                            roles: ["Forrest"]
                        }
                    }
                ]
            }
        }
    ]) {
        movies {
            title
            released
            directors {
                name
                born
            }
            actorsConnection {
                edges {
                    properties {
                      roles
                    }
                    node {
                        name
                        born
                    }
                }
            }
        }
    }
}

认识到这一点有助于您一次性更高效地创建更大的子图。

获取数据

现在您的数据库中有了 Movie 信息,您可以如下所示一起查询所有内容

query {
    movies(where: { title: { eq: "Forrest Gump" } }) {
        title
        released
        directors {
            name
            born
        }
        actorsConnection {
            edges {
                properties {
                    roles
                }
                node {
                    name
                    born
                }
            }
        }
    }
}

基数

Neo4j GraphQL 库对“多对多”关系有类型定义要求。例如

type User @node {
    name: String!
    posts: [Post!]! @relationship(type: "HAS_POST", direction: OUT)
}

type Post @node {
    name: String!
}

User.posts 处的该关系被视为“多对多”关系,这意味着它应该始终是 NonNullListTypeNonNullNamedType 类型。换句话说,“多对多”关系中的数组和类型都应该有一个 !

© . All rights reserved.