过滤

本页面介绍如何在 Neo4j GraphQL 库中对订阅应用过滤器。但请注意,

  • 过滤只能应用于订阅操作的根节点。

  • 不支持订阅类型的聚合,目前没有可用方法。

可以创建订阅来针对节点 (create/update/delete) 或关系 (create/delete) 的更改。

虽然格式略有不同,取决于订阅是针对节点还是关系,但提供 where 参数允许对返回给订阅的事件进行过滤。

运算符

在创建订阅时,可以在 where 参数中使用多种运算符来处理不同的类型。

等式运算符

所有类型都可以测试其是否相等或不相等。对于 Boolean 类型,这些是唯一可用的比较运算符。

数值运算符

以下比较运算符适用于数值类型 (IntFloatBigInt)

  • _LT

  • _LTE

  • _GTE

  • _GT

目前不支持对 时间类型空间类型 进行过滤。

字符串比较

以下区分大小写的比较运算符仅适用于 StringID 类型

  • _STARTS_WITH

  • _ENDS_WITH

  • _CONTAINS

数组比较

以下运算符适用于非数组字段,并接受数组参数

  • _IN

反之,以下运算符适用于数组字段,并接受单个参数

  • _INCLUDES

这些运算符适用于除 Boolean 之外的所有类型。

AND/OR 运算符

可以使用 AND/ OR 运算符来组合复杂的运算符。它们接受一个参数,该参数是与 where 参数格式相同的项目的数组。

请查看 组合运算符 以了解示例。

订阅节点事件

where 参数允许指定针对目标节点的顶层属性的过滤器。仅与这些属性和类型匹配的事件将返回给订阅。

例如,请考虑以下类型定义

type Movie {
    title: String
    genre: String
    averageRating: Float
    releasedIn: Int
}

现在,以下是创建订阅时如何应用过滤器的示例

CREATE

要过滤对电影的 create 操作的订阅(按其类型进行过滤),请执行以下操作

subscription {
    movieCreated(where: {genre: "Drama"}) {
        createdMovie {
            title
        }
    }
}

这样,只有新创建的类型为 "Drama" 的电影才会将事件触发到此订阅。

where 参数仅按创建时的属性进行过滤。

UPDATE

以下是过滤对平均评分大于 8 的电影的 update 操作的订阅的示例

subscription {
    movieUpdate(where: {averageRating_GT: 8}) {
        updatedMovie {
            title
        }
    }
}

这样,只有当修改平均评分大于 8 的电影时,您才会收到由这些电影触发的事件。

where 参数仅过滤更新 **之前** 已存在的属性。

这些事件可能如下所示

mutation {
    makeTheMatrix: createMovies(input: {title: "The Matrix", averageRating: 8.7}) {
        title
        averageRating
    },
    makeResurrections: createMovies(input: {title: "The Matrix Resurrections", averageRating: 5.7}) {
        title
        averageRating
    },
}

mutation {
    updateTheMatrix: updateMovie(
        where: {title: "The Matrix"}
        update: {averageRating: 7.9}
    ) {
        title
    },
    updateResurrections: updateMovie(
        where: {title: "The Matrix Resurrections"}
        update: {averageRating: 8.9}
    ) {
        title
    }
}

因此,鉴于之前描述的订阅,这些 GraphQL 操作应仅针对 "The Matrix" 电影触发。

DELETE

以下是过滤对电影的 delete 操作的订阅的示例,方法是使用 NOT 过滤器按其类型进行过滤

subscription {
    movieDeleted(where: { NOT: { genre: "Comedy" } }) {
        deletedMovie {
            title
        }
    }
}

这样,只有所有类型(除 "Comedy" 之外)的已删除电影才会将事件触发到此订阅。

where 参数仅过滤删除过程之前已存在的属性。

组合运算符

所有前面提到的运算符都可以使用 ANDORNOT 运算符组合起来。它们接受一个参数,该参数是与 where 参数格式相同的项目的数组,这意味着它们也可以嵌套以形成复杂的组合。

例如,考虑一个喜欢喜剧电影,但不喜欢 2000 年初的浪漫喜剧,并且将黑客帝国三部曲作为他们最喜欢的电影的用户。他们可以订阅以下涵盖这组特定兴趣的任何更新

subscription {
    movieUpdated(where: {
        OR: [
            { title_CONTAINS: "Matrix" },
            { genre: "comedy" },
            { AND: [
                { NOT: { genre: "romantic comedy" } },
                { releasedIn_GT: 2000 },
                { releasedIn_LTE: 2005 }
            ] },
        ]
    }) {
        updatedMovie {
            title
        }
    }
}

订阅关系事件

在订阅关系事件时,where 参数仍然允许指定针对目标节点的顶层属性的过滤器。它还支持指定针对关系属性 (edge) 和关系另一端节点的顶层属性 (node) 的过滤器。这可以通过使用前面描述的运算符来完成,其用法与 订阅节点事件 非常相似。

但是,通过关系事件进行过滤是一种更强大的逻辑。这是因为这些过滤器还可以表达预期的关系字段,或者关系另一端的预期具体类型(假设它们连接到抽象类型)。

请注意,每个指定的关联字段都与其他关联字段使用 逻辑 OR 组合。订阅中仅返回与这些关系字段名称匹配的事件。

您可以进一步通过节点和关系属性过滤每个关系字段。这些字段在生成的过滤器中使用 逻辑 AND 组合在一起。

例如,在以下类型定义中

type Movie {
    title: String
    genre: String
    actors: [Actor!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: IN)
}

type ActedIn @relationshipProperties {
    screenTime: Int!
}

type Actor {
    name: String
}

where 参数的格式为

{
    movie: {
        # top-level properties of the node targeted for the subscription operation, supports operators
        title_IN: ["The Matrix", "Fight Club"]
    },
    createdRelationship: {
        actors: { # field name corresponding to a relationship in the type definition of the node targeted for the subscription operation
            edge: {
                 # properties of the relationship, supports operators
                screenTime_GT: 10,
            },
            node: {
                # top-level properties of the node on the other end of the relationship, supports operators
                name_STARTS_WITH: "Brad"
            }
        }
    }
}

以下部分介绍了在创建对关系事件的订阅时如何应用过滤器的示例。

新创建的关系

以下示例过滤了对新创建关系的订阅,这些关系将来自除“剧情片”以外类型的电影连接到屏幕时间超过 10 分钟的演员。

subscription {
    movieRelationshipCreated(where: { movie: { NOT: { genre: "Drama" } }, createdRelationship: { actors: { edge: { screenTime_GT: 10 } } } }) {
        movie {
            title
        }
        createdRelationship {
            actors {
                screenTime
                node {
                    name
                }
            }
        }
    }
}

where 参数目前仅过滤关系创建时已存在的属性。

新删除的关系

以下示例过滤了对已删除关系的订阅,这些关系将类型为“喜剧片”或“冒险片”的电影连接到名为“金·凯瑞”的演员。

subscription {
    movieRelationshipDeleted(where: { movie: { genre_IN: ["Comedy", "Adventure"] }, createdRelationship: { actors: { node: { name: "Jim Carrey" } } } }) {
        movie {
            title
        }
        deletedRelationship {
            actors {
                screenTime
                node {
                    name
                }
            }
        }
    }
}

where 参数仅过滤在关系删除之前已存在的属性。

除了过滤节点或关系属性之外,与关系相关的过滤逻辑更强大。这是因为这些过滤器还可以表示预期关系字段,或关系另一端预期的具体类型,前提是它们连接到抽象类型。

以下示例适用于 CREATE_RELATIONSHIPDELETE_RELATIONSHIP 事件。它们的目的是说明订阅关系事件的各种过滤方式。

考虑以下类型定义

type Movie {
    title: String
    genre: String
    actors: [Actor!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: IN)
    directors: [Director!]! @relationship(type: "DIRECTED", properties: "Directed", direction: IN)
    reviewers: [Reviewer!]! @relationship(type: "REVIEWED", properties: "Review", direction: IN)
}

type ActedIn @relationshipProperties {
    screenTime: Int!
}

type Actor {
    name: String
}

type Person implements Reviewer {
    name: String
    reputation: Int
}

union Director = Person | Actor

type Directed @relationshipProperties {
    year: Int!
}

interface Reviewer {
    reputation: Int!
}

type Magazine implements Reviewer {
    title: String
    reputation: Int!
}

type Review @relationshipProperties {
    score: Int!
}

以及基本的订阅操作

subscription MovieRelationshipDeleted($where: MovieRelationshipDeletedSubscriptionWhere) {
    movieRelationshipDeleted(where: $where) {
        movie {
            title
        }
        deletedRelationship {
            actors {
                screenTime
                node {
                    name
                }
            }
            directors {
                year
                node {
                    ... on PersonEventPayload { # generated type
                        name
                        reputation
                    }
                    ... on ActorEventPayload { # generated type
                        name
                    }
                }
            }
            reviewers {
                score
                node {
                    reputation
                    ... on MagazineEventPayload { # generated type
                        title
                        reputation
                    }
                    ... on PersonEventPayload { # generated type
                        name
                        reputation
                    }
                }
            }
        }
    }
}

您可以使用以下 where 输入在 GraphQL 变量值中获取不同的结果

通过隐式/显式声明进行过滤

隐式或显式声明用于过滤预期返回给订阅的特定关系类型。

例如,当订阅到电影的已创建或已删除关系时,用户可能只对 ACTED_IN 类型的关系感兴趣,但对演员节点的属性或连接到它的其他关系不感兴趣。在这种情况下,此关系的对应字段名为 actors

通过显式指定 actors 字段名,您可以过滤掉对其他关系属性的事件

{
    where: {
        deletedRelationship: {
            actors: {} # no properties specified here, therefore all relationships to this field name will be returned
        }
    }
}

如果您对符合某些过滤器(例如,名称以字母“A”开头的)的演员节点感兴趣,则过程与 订阅节点事件 没有区别

{
    where: {
        deletedRelationship: {
            actors: {
                node: { # use operations to specify filers on the top-level properties of the node at the other end of the relationship
                    name_STARTS_WITH: "A"
                }
            }
        }
    }
}

如果您也对关系本身符合某些过滤器感兴趣,例如演员在电影中的时间不超过 40 分钟,则查询可能如下所示

{
    where: {
        deletedRelationship: {
            actors: {
                edge: { # use operations to specify filers on the top-level properties of the relationship
                    screenTime_LT: 40,
                }
                node: {
                    name: "Alvin"
                }
            }
        }
    }
}

通过显式指定相应的字段名,还可以将多种关系类型包含在返回的订阅中。例如

{
    where: {
        deletedRelationship: {
            actors: {}, # include all relationships corresponding of type `ACTED_IN`
            directors: {} # include all relationships corresponding of type `DIRECTED`
            # exclude relationships of type `REVIEWED`
        }
    }
}

现在,如果您对所有关系类型感兴趣,您可以通过不指定任何内容来隐式表示此关系

{
    where: {
        deletedRelationship: {} # include all relationships of all types
    }
}

或者通过显式指定连接到目标订阅类型的字段名来进行显式表示

{
    where: {
        deletedRelationship: {
            # include all relationships of all types
            # subscription target type is `Movie`, which has the following relationship field names:
            actors: {},
            directors: {},
            reviewers: {}
        }
    }
}

但是,请注意,由于对任何关系应用了任何过滤器,因此显式包含您希望订阅的关系是强制性的步骤。

例如,如果应该返回所有关系,但您希望过滤掉得分低于 7 的 REVIEWED 关系,则您的查询可能如下所示

{
    where: {
        deletedRelationship: {
            actors: {}, # include all relationships of type `ACTED_IN`
            directors: {}, # include all relationships of type `DIRECTED`
            reviewers: { # include all relationships of type `REVIEWED`, with the score property greater than 7
                edge: {
                    score_GT: 7
                }
            }
        }
    }
}

不同的过滤器也可以应用于不同的关系,没有任何限制。例如

{
    where: {
        deletedRelationship: {
            actors: { # include some relationships of type `ACTED_IN`, filtered by relationship property `screenTime` and node property `name`
                edge: {
                    screenTime_LT: 60,
                },
                node: {
                    name_IN: ["Tom Hardy", "George Clooney"]
                }
            },
            directors: {}, # include all relationships of type `DIRECTED`
            reviewers: { # include some relationships of type `REVIEWED`, filtered by relationship property `score` only
                edge: {
                    score_GT: 7
                }
            }
        }
    }
}

在前面的示例中,actorsdirectorsreviewers 关系字段之间存在隐式逻辑 OR。也就是说,类型为 ACTED_IN 或类型为 DIRECTED 或类型为 REVIEWED 的关系应触发先前描述的订阅。

actors 关系字段内,edgenode 字段之间存在隐式逻辑 AND。换句话说,类型为 ACTED_IN 且属性 screenTime 小于 60 并且目标节点的名称为 ["Tom Hardy", "George Clooney"] 的关系应触发订阅。

抽象类型

以下部分描述了如何使用抽象类型过滤订阅。

联合类型

本示例说明了如何过滤关系另一端节点,当该节点为联合类型时

{
    where: {
        deletedRelationship: {
            directors: { # relationship to a union type
                Person: { # concrete type that makes up the union type
                    edge: {
                        year_GT: 2010
                    },
                    node: {
                        name: "John Doe",
                        reputation: 10
                    }
                },
                Actor: { # concrete type that makes up the union type
                    edge: {
                        year_LT: 2005
                    },
                    node: {
                        name: "Tom Hardy"
                    }
                }
            },
        }
    }
}

结果是,只有 DIRECTED 类型的关系返回给订阅,其中导演为名为“John Doe”的 Person,他在 2010 年之后执导了电影,或者导演为名为“Tom Hardy”的 Actor,他在 2005 年之前执导了电影。

请注意,关系字段名被分成多个部分,每个部分对应于构成联合类型的具体类型之一。关系属性不存在于这些部分之外,即使属性相同。

现在,考虑另一个没有显式指定具体类型的示例

{
    where: {
        deletedRelationship: {
            directors: {}, # include all relationships of type `DIRECTED`
        }
    }
}

遵循与关系字段名相同的逻辑:当没有明确提供任何内容时,则接受所有内容。因此,类型为 DIRECTED 且在电影和构成联合类型 Director 的任何具体类型之间建立的关系将返回给订阅。

它等效于以下内容

{
    where: {
        deletedRelationship: {
            directors: { # include all relationships of type `DIRECTED`
                Actor: {},
                Person: {}
            }
        }
    }
}

请注意,显式指定具体类型会将其他类型从返回的事件中排除

{
    where: {
        deletedRelationship: {
            directors: {
                Actor: {} # include all relationships of type `DIRECTED` to an `Actor` type
            }
        }
    }
}

在这种情况下,只有电影和演员之间类型为 DIRECTED 的关系会返回给订阅。电影和 Person 之间的关系被过滤掉。

这样做的一种原因是,在 Actor 类型上包含一些过滤器

{
    where: {
        deletedRelationship: {
            directors: {
                Actor: { # include some relationships of type `DIRECTED` to an `Actor` type, that conform to the filters
                    node: {
                        NOT: { name: "Tom Hardy" }
                    }
                }
            }
        }
    }
}

要包含 Actor 类型上的过滤器,但也要将 Person 类型包含在结果中,您需要明确说明意图

{
    where: {
        deletedRelationship: {
            directors: {
                Actor: { # include some relationships of type `DIRECTED` to an `Actor` type, that conform to the filters
                    node: {
                        NOT: { name: "Tom Hardy" }
                    }
                },
                Person: {} # include all relationships of type `DIRECTED` to a `Person` type
            }
        }
    }
}

接口类型

以下示例说明了如何过滤关系另一端节点,当该节点为接口类型时

{
    where: {
        deletedRelationship: {
            reviewers: { # relationship to an interface type
                edge: {
                    # relationship properties of a relationship of type `REVIEWED`
                    score_GT: 7
                },
                node: {
                    # common fields declared by the interface
                    reputation_GTE: 8
                }
            },
        }
    }
}

本示例返回类型为 MovieReviewer 之间关系的事件,其中得分高于 7,并且 Reviewer 的信誉大于或等于 7。