联合类型

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

本页介绍了如何在关系上下文中使用联合类型,并举例说明了如何在查询和变更(Mutation)中使用它们。

数据模型

以如下图形为例,其中 User 类型通过 HAS_CONTENT 关系连接到 Content 类型,而 ContentBlogPost 两个节点类型的联合。

relationship union
图 1. User 节点模型,通过 HAS_CONTENT 关系连接到 Blog 和 Post 节点。HAS_CONTENT 关系的属性在两个目标节点类型之间有所不同。

示例数据库数据

考虑一个由以下示例数据组成的数据库,它遵循上述数据模型。

Person and Blog Post Union
图 2. 示例数据库数据:一个 Blog 节点(蓝色)通过 HAS_POST 关系连接到两个 Post(橙色)。User 节点(绿色)通过 HAS_CONTENT 关系连接到 Blog 和 Post 节点。

编写类型定义

从概念上讲,BlogPost 都是归属于 User 的“内容”类型。然而,它们是根本不同的内容类型,具有不同的字段,因此可以通过联合类型将它们组合在一起。

定义带有关系字段的联合类型和节点类型
union Content = Blog | Post

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

type Post @node {
    content: String
}

type User @node {
    name: String
    content: [Content!]! @relationship(type: "HAS_CONTENT", direction: OUT)
}

查询联合类型

联合类型对于建模那些指向多个不共享公共字段的不同节点类型的关系非常有用。

查询联合类型的关系字段时,必须使用内联片段(inline fragments)来指定联合中每种类型要返回的字段。

示例 1. 获取具有关联 Content 节点的 User 节点,并为所有具体成员类型使用内联片段

注意对 Content 联合的所有成员类型(在本例中为 BlogPost)使用了内联片段。

query {
    users {
        name
        content {
            ... on Blog {
                title
            }
            ... on Post {
                content
            }
        }
    }
}

如果查询中未包含对应于联合成员类型的内联片段,则不会返回该类型的任何字段。相反,响应将是一个空对象。

示例 2. 获取具有关联 Content 节点的 User 节点,省略 Post 类型的内联片段

以下查询仅包含 Blog 类型的内联片段,而省略了 Post 类型的内联片段

query {
    users {
        name
        content {
            ... on Blog {
                title
            }
        }
    }
}

此查询的响应中包含 Post 类型的空对象,因为查询中未为其指定任何字段

{
    "data": {
        "users": [
            {
                "name": "Alice",
                "content": [{ "title": "Alice's Blog" }, {}]
            },
            {
                "name": "Bob",
                "content": [{}]
            }
        ]
    }
}

过滤特定的联合成员类型

要过滤特定的联合成员类型,可以使用关系字段上的 where 参数。

示例 3. 获取按 Blog 类型过滤并具有关联 Content 节点的 User 节点

注意,查询中不需要包含对应于 Post 类型的内联片段,因为它们已被 where 参数过滤掉

query {
    users {
        name
        content(where: { Blog: { NOT: { title: { eq: null } } }}) {
            ... on Blog {
                title
            }
        }
    }
}

创建联合关系

要创建上图中显示的示例数据库数据,一种方法是先创建 User 节点。

User Alice 通过 HAS_CONTENT 关系连接到一个 Blog 节点,该 Blog 节点有两个通过 HAS_POST 关系连接的 Post 节点。对 Content 联合的关系可以通过嵌套变更(nested mutation)同时创建。

示例 4. 创建具有到 Blog 节点的 HAS_CONTENT 关系的 User 节点

注意在 create 输入中指定了 Content 联合的具体类型(Blog)。

mutation {
    createUsers(
        input: [
            {
                name: "Bob"
            },
            {
                name: "Alice"
                content: {
                    Blog: {
                        create: [
                            {
                                node: {
                                    title: "Our Blog"
                                    posts: {
                                        create: [
                                            { node: { content: "Alice's Post" } },
                                            { node: { content: "Bob's Post" } }
                                        ]
                                    }
                                }
                            }
                        ]
                    }
                }
            }
        ]
    ) {
        users {
            name
        }
    }
}

在空数据库上执行此变更将创建如下图形

Person and Blog Post Union partial
图 3. 示例数据库数据:User 节点(绿色)和 Blog 节点(蓝色),Blog 节点通过 HAS_POST 关系连接到两个 Post(橙色)。User 和 Post 节点之间的 HAS_CONTENT 关系缺失。

更新联合关系

您可以更新 User 节点并创建它们与作为 Content 联合一部分的 Post 节点之间的 HAS_POST 关系。

示例 5. 更新具有到 Post 节点的 HAS_CONTENT 关系的 User 节点

注意在 update 输入中指定了 Content 联合的具体类型(Post)。

mutation {
    mutation {
        updateUsers(
           where: { name: { eq: "Alice" } }
           update: {
               content: {
                    Post: [
                        {
                            connect: [
                                {
                                    where: { node: { content: { eq: "Alice's Post" } } }
                                }
                            ]
                        }
                    ]
                }
            }
        ) {
            users {
                name
            }
        }
    }
}

针对 User Bob 再次执行相同的变更,并调整过滤条件。