订阅事件
此页面介绍了 Neo4j GraphQL 库提供的各种订阅选项。
仅通过 |
CREATE
对CREATE
事件的订阅**仅**监听新创建的节点,而不是新的关系。在这种情况下,每个新节点都会触发一个新事件,其中包含其属性。
此操作通过顶级订阅[type]Created
执行,其中包含以下字段
-
event
:触发此订阅的事件(在本例中为CREATE
)。 -
created<typename>
:新创建节点的顶级属性,不包含关系。 -
timestamp
:进行变异的时间戳。如果同一个查询触发多个事件,则它们应具有相同的时间戳。
例如,考虑以下类型定义
type Movie {
title: String
genre: String
}
对Movie
类型任何新创建的节点的订阅应如下所示
subscription {
movieCreated {
createdMovie {
title
genre
}
event
timestamp
}
}
UPDATE
对UPDATE
事件的订阅**仅**监听节点属性更改,而不是其他字段的更新。在这种情况下,每个修改节点顶级属性的变异都会触发一个新事件。
此操作通过顶级订阅[type]Updated
执行,其中包含以下字段
-
event
:触发此订阅的事件(在本例中为UPDATE
)。 -
updated<typename>
:更新节点的顶级属性,不包含关系。 -
previousState
:节点在UPDATE
事件之前的先前顶级属性。 -
timestamp
:进行变异的时间戳。如果同一个查询触发多个事件,则它们应具有相同的时间戳。
例如,考虑以下类型定义
type Movie {
title: String
genre: String
}
对属性最近更新的Movie
类型任何节点的订阅应如下所示
subscription MovieUpdated {
movieUpdated {
event
previousState {
title
genre
}
updatedMovie {
title
}
timestamp
}
}
DELETE
对DELETE
事件的订阅**仅**监听被删除的节点,而不是被删除的关系。此操作通过顶级订阅[type]Deleted
执行,其中包含以下字段
-
event
:触发此订阅的事件(在本例中为DELETE
)。 -
deleted<typename>
:已删除节点的顶级属性,不包含关系。 -
timestamp
:进行变异的时间戳。如果同一个查询触发多个事件,则它们应具有相同的时间戳。
例如,考虑以下类型定义
type Movie {
title: String
genre: String
}
对任何已删除的Movie
类型节点的订阅应如下所示
subscription {
movieDeleted {
deletedMovie {
title
}
event
timestamp
}
}
CREATE_RELATIONSHIP
对CREATE_RELATIONSHIP
事件的订阅监听新创建的关系,并包含有关连接节点的信息。这些事件
-
仅适用于定义关系字段的类型。
-
包含特定于关系的信息,例如关系字段名称和包含指定类型所有关系字段名称的对象。
-
根据创建的关系数量触发相同数量的事件,如果在变异后创建新的关系,并且目标类型负责在模式中定义两个或多个关系。
-
包含关系对象,该对象仅填充一个关系名称的新创建的关系属性(所有其他关系名称应具有空值)。
-
包含通过关系连接的节点的属性,以及新关系的属性(如果有)。
对CREATE_RELATIONSHIP
事件的订阅可以使用顶级订阅[type]RelationshipCreated
进行,其中包含以下字段
-
event
:触发此订阅的事件(在本例中为CREATE_RELATIONSHIP
)。 -
timestamp
:进行变异的时间戳。如果同一个查询触发多个事件,则它们应具有相同的时间戳。 -
<typename>
:目标节点的顶级属性,不包含关系,在触发CREATE_RELATIONSHIP
操作之前。 -
relationshipFieldName
:新创建关系的字段名称。 -
createdRelationship
:一个对象,其中包含受新创建的关系影响的节点的所有字段名称。虽然与relationshipFieldName
无关的任何事件都应该是null
,但相关的事件应包含关系属性(如果已定义)和一个node
键,其中包含关系另一侧节点的属性。仅顶级属性可用,不包含关系,并且它们是在CREATE_RELATIONSHIP
操作发生之前已经存在的属性。
无论数据库中关系的方向如何, |
例如,考虑以下类型定义
type Movie {
title: String
genre: String
actors: [Actor] @relationship(type: "ACTED_IN", direction: IN, properties: "ActedIn")
reviewers: [Reviewer] @relationship(type: "REVIEWED", direction: IN, properties: "Reviewed")
}
type Actor {
name: String
}
type ActedIn @relationshipProperties {
screenTime: Int!
}
type Reviewer {
name: String
reputation: Int
}
type Reviewed @relationshipProperties {
score: Int!
}
现在考虑一个创建名为Tom Hardy
的Actor
和一个名为Jane
的Reviewer
通过关系连接到标题为Inception
的Movie
的变异。在这种情况下,CREATE_RELATIONSHIP
订阅应接收以下事件
{
# 1 - relationship type `ACTED_IN`
event: "CREATE_RELATIONSHIP",
timestamp,
movie: {
title: "Inception",
genre: "Adventure"
},
relationshipFieldName: "actors", # notice the field name specified here is populated below in the `createdRelationship` object
createdRelationship: {
actors: {
screenTime: 1000, # relationship properties for the relationship type `ACTED_IN`
node: { # top-level properties of the node at the other end of the relationship, in this case `Actor` type
name: "Tom Hardy"
}
},
reviewers: null # relationship declared by this field name is not covered by this event, check out the following...
}
}
{
# 2 - relationship type `REVIEWED`
event: "CREATE_RELATIONSHIP",
timestamp,
movie: {
title: "Inception",
genre: "Adventure"
},
relationshipFieldName: "reviewers", # this event covers the relationship declared by this field name
createdRelationship: {
actors: null, # relationship declared by this field name is not covered by this event
reviewers: { # field name equal to `relationshipFieldName`
score: 8,
node: {
name: "Jane",
reputation: 9
}
}
}
}
标准类型
对于另一个示例,这次使用标准类型创建关系,请考虑以下类型定义
type Movie {
title: String
genre: String
actors: [Actor] @relationship(type: "ACTED_IN", direction: IN, properties: "ActedIn")
}
type Actor {
name: String
}
type ActedIn @relationshipProperties {
screenTime: Int!
}
对任何具有新创建关系的Movie
的订阅应如下所示
subscription {
movieRelationshipCreated {
event
timestamp
movie {
title
genre
}
relationshipFieldName
createdRelationship {
actors {
screenTime
node {
name
}
}
}
}
}
抽象类型
在使用带有关系的抽象类型时,您需要在执行订阅操作时指定一个或多个相应的具体类型。
这些类型由库生成,并符合格式[type]EventPayload
,其中[type]
是具体类型。
例如,考虑以下类型定义
type Movie {
title: String
genre: String
directors: [Director!]! @relationship(type: "DIRECTED", properties: "Directed", direction: IN)
}
union Director = Person | Actor
type Actor {
name: String
}
type Person {
name: String
reputation: Int
}
type Directed @relationshipProperties {
year: Int!
}
对任何Movie
新创建关系的订阅应如下所示
subscription {
movieRelationshipCreated {
event
timestamp
movie {
title
genre
}
relationshipFieldName
createdRelationship {
directors {
year
node {
... on PersonEventPayload { # generated type
name
reputation
}
... on ActorEventPayload { # generated type
name
}
}
}
}
}
}
接口
对于关系与接口一起创建的示例,请考虑以下类型定义
type Movie {
title: String
genre: String
reviewers: [Reviewer!]! @relationship(type: "REVIEWED", properties: "Review", direction: IN)
}
interface Reviewer {
reputation: Int!
}
type Magazine implements Reviewer {
title: String
reputation: Int!
}
type Influencer implements Reviewer {
name: String
reputation: Int!
}
type Review @relationshipProperties {
score: Int!
}
对任何Movie
新创建关系的订阅应如下所示
subscription {
movieRelationshipCreated {
event
timestamp
movie {
title
genre
}
relationshipFieldName
createdRelationship {
reviewers {
score
node {
reputation
... on MagazineEventPayload { # generated type
title
reputation
}
... on InfluencerEventPayload { # generated type
name
reputation
}
}
}
}
}
}
非互惠关系
例如,非互惠关系可以描述为类型 A 和类型 B 保持关系,但在 GraphQL 模式中,类型 A 是定义与 B 的关系的一个,而 B 没有定义与 A 的关系。
为了说明这一点,请考虑以下类型定义
type Movie {
title: String
genre: String
actors: [Actor] @relationship(type: "ACTED_IN", direction: IN, properties: "ActedIn")
directors: [Director!]! @relationship(type: "DIRECTED", properties: "Directed", direction: IN)
}
type Actor {
name: String
movies: [Movie!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: OUT)
}
type Person {
name: String
reputation: Int
}
union Director = Person | Actor
type ActedIn @relationshipProperties {
screenTime: Int!
}
type Directed @relationshipProperties {
year: Int!
}
请注意,类型定义包含两个关系
-
ACTED_IN
,它在Movie
和Actor
类型中都定义了相应的字段,因此可以被认为是互惠关系。 -
DIRECTED
,它仅在Movie
类型中定义。Director
类型没有定义匹配的字段,因此可以被认为是非互惠关系。
考虑到前面描述的三种类型(Movie
、Actor
和Person
),仅在Person
类型的情况下,订阅CREATE_RELATIONSHIP
是不可能的,因为它没有定义任何关系。对于其他两种类型,以下是订阅方法
Movie
类型subscription {
movieRelationshipCreated {
event
timestamp
movie {
title
genre
}
relationshipFieldName
createdRelationship {
actors { # corresponds to the `ACTED_IN` relationship type
screenTime
node {
name
}
}
directors { # corresponds to the `DIRECTED` relationship type
year
node {
... on PersonEventPayload {
name
reputation
}
... on ActorEventPayload {
name
}
}
}
}
}
}
Actor
类型subscription {
actorRelationshipCreated {
event
timestamp
actor {
name
}
relationshipFieldName
createdRelationship {
movies { # corresponds to the `ACTED_IN` relationship type
screenTime
node {
title
genre
}
}
# no other field corresponding to the `DIRECTED` relationship type
}
}
}
actorRelationshipCreated
订阅中createdRelationship
内Movie
字段的存在反映了ACTED_IN
类型关系是互惠的这一事实。
因此,当创建这种类型的新关系时,例如通过运行此变异
mutation {
createMovies(
input: [
{
actors: {
create: [
{
node: {
name: "Keanu Reeves"
},
edge: {
screenTime: 420
}
}
]
},
title: "John Wick",
genre: "Action"
}
]
) {
movies {
title
genre
}
}
}
如果已订阅两种类型的CREATE_RELATIONSHIP
事件,则应提示两个事件
{
# from `movieRelationshipCreated`
event: "CREATE_RELATIONSHIP"
timestamp
movie {
title: "John Wick",
genre: "Action"
}
relationshipFieldName: "actors",
createdRelationship {
actors: {
screenTime: 420,
node: {
name: "Keanu Reeves"
}
},
directors: null
}
},
{
# from `actorRelationshipCreated`
event: "CREATE_RELATIONSHIP"
timestamp
actor {
name: "Keanu Reeves"
}
relationshipFieldName: "movies",
createdRelationship {
movies: {
screenTime: 420,
node: {
title: "John Wick",
genre: "Action"
}
}
}
}
现在,由于类型Movie
和Director
之间的DIRECTED
关系**不是**互惠的,因此执行此变异
mutation {
createMovies(
input: [
{
directors: {
Actor: { # relationship 1
create: [
{
node: {
name: "Woody Allen"
},
edge: {
year: 1989
}
}
]
},
Person: { # relationship 2
create: [
{
node: {
name: "Francis Ford Coppola",
reputation: 100
},
edge: {
year: 1989
}
}
]
}
},
title: "New York Stories",
genre: "Comedy"
}
]
) {
movies {
title
genre
}
}
}
如果您已订阅了类型为 Movie
的 CREATE_RELATIONSHIP
事件,则应触发两个事件。
{
# relationship 1 - from `movieRelationshipCreated`
event: "CREATE_RELATIONSHIP"
timestamp
movie {
title: "New York Stories",
genre: "Comedy"
}
relationshipFieldName: "directors",
createdRelationship {
actors: null,
directors: {
year: 1989,
node: {
name: "Woody Allen"
}
}
}
},
{
# relationship 2 - from `movieRelationshipCreated`
event: "CREATE_RELATIONSHIP"
timestamp
movie {
title: "New York Stories",
genre: "Comedy"
}
relationshipFieldName: "directors",
createdRelationship {
actors: null,
directors: {
year: 1989,
node: {
name: "Francis Ford Coppola",
reputation: 100
}
}
}
}
使用相同 Neo4j 标签的类型
需要考虑的一种情况是,Neo4j 标签被特定的 GraphQL 类型覆盖。这可以通过使用 @node
指令并指定 label
参数来实现。但是,在大多数情况下,**不建议**使用这种方法来设计您的 API。
例如,考虑以下类型定义
type Actor @node(label: "Person") {
name: String
movies: [Movie!]! @relationship(type: "PART_OF", direction: OUT)
}
typePerson {
name: String
movies: [Movie!]! @relationship(type: "PART_OF", direction: OUT)
}
type Movie {
title: String
genre: String
people: [Person!]! @relationship(type: "PART_OF", direction: IN)
actors: [Actor!]! @relationship(type: "PART_OF", direction: IN)
}
尽管示例包含 3 个 GraphQL 类型,但在 Neo4j 中,节点类型应该只有 2 种:标记为 Movie
或标记为 Person
。
在数据库级别,Actor
和 Person
之间没有区别。因此,在创建类型为 PART_OF
的新关系时,每种类型都应该有一个事件。
考虑以下订阅
subscription {
movieRelationshipCreated {
event
timestamp
movie {
title
genre
}
relationshipFieldName
createdRelationship {
people { # corresponds to the `PART_OF` relationship type
node {
name
}
}
actors { # corresponds to the `PART_OF` relationship type
node {
name
}
}
}
}
}
subscription {
actorRelationshipCreated {
event
timestamp
actor {
name
}
relationshipFieldName
createdRelationship {
movies { # corresponds to the `PART_OF` relationship type
node {
title
genre
}
}
}
}
}
运行如下所示的 mutation
mutation {
createMovies(
input: [
{
people: { # relationship 1
create: [
{
node: {
name: "John Logan"
}
}
]
},
actors: { # relationship 2
create: [
{
node: {
name: "Johnny Depp"
}
}
]
},
title: "Sweeney Todd",
genre: "Horror"
}
]
) {
movies {
title
genre
}
}
}
应该会得到以下结果
{
# relationship 1 `people` - for GraphQL types `Movie`, `Person`
event: "CREATE_RELATIONSHIP"
timestamp
movie {
title: "Sweeney Todd",
genre: "Horror"
}
relationshipFieldName: "people",
createdRelationship {
people: {
node: {
name: "John Logan"
}
},
actors: null
}
},
{
# relationship 1 `people` - for GraphQL types `Movie`, `Actor`
event: "CREATE_RELATIONSHIP"
timestamp
movie {
title: "Sweeney Todd",
genre: "Horror"
}
relationshipFieldName: "actors",
createdRelationship {
people: null,
actors: {
node: {
name: "John Logan"
}
}
}
},
{
# relationship 1 `movies` - for GraphQL types `Actor`, `Movie`
event: "CREATE_RELATIONSHIP"
timestamp
actor {
name: "John Logan"
}
relationshipFieldName: "movies",
createdRelationship {
movies: {
node: {
title: "Sweeney Todd",
genre: "Horror"
}
}
}
},
{
# relationship 2 `actors` - for GraphQL types `Movie`,`Person`
event: "CREATE_RELATIONSHIP"
timestamp
movie {
title: "Sweeney Todd",
genre: "Horror"
}
relationshipFieldName: "people",
createdRelationship {
people: {
node: {
name: "Johnny Depp"
}
},
actors: null
}
},
{
# relationship 2 `actors` - for GraphQL types `Movie`, `Actor`
event: "CREATE_RELATIONSHIP"
timestamp
movie {
title: "Sweeney Todd",
genre: "Horror"
}
relationshipFieldName: "actors",
createdRelationship {
people: null,
actors: {
node: {
name: "Johnny Depp"
}
}
}
},
{
# relationship 2 `movies` - for GraphQL types `Actor`, `Movie`
event: "CREATE_RELATIONSHIP"
timestamp
actor {
name: "Johnny Depp"
}
relationshipFieldName: "movies",
createdRelationship {
movies: {
node: {
title: "Sweeney Todd",
genre: "Horror"
}
}
}
},
如果您也订阅了 Person
,则应该会收到另外两个事件。
{
# relationship 1 `movies` - for GraphQL types `Person`, `Movie`
event: "CREATE_RELATIONSHIP"
timestamp
actor {
name: "John Logan"
}
relationshipFieldName: "movies",
createdRelationship {
movies: {
node: {
title: "Sweeney Todd",
genre: "Horror"
}
}
}
},
{
# relationship 2 `movies` - for GraphQL types `Person`, `Movie`
event: "CREATE_RELATIONSHIP"
timestamp
actor {
name: "Johnny Depp"
}
relationshipFieldName: "movies",
createdRelationship {
movies: {
node: {
title: "Sweeney Todd",
genre: "Horror"
}
}
}
},
DELETE_RELATIONSHIP
订阅 DELETE_RELATIONSHIP
事件会监听关系被删除,并包含有关指定类型先前连接节点的信息。这种订阅
-
仅适用于定义关系字段的类型。
-
包含特定于关系的信息,例如关系字段名称和包含指定类型所有关系字段名称的对象。此对象应根据已删除的关系填充属性。
-
如果关系在 mutation 后被删除,并且目标类型负责在模式中定义两个或多个关系,则会触发数量相同的事件。
-
包含使用新删除的关系属性填充的关系对象,但仅限于一个关系名称(所有其他关系名称的值应为 null)。
-
包含通过关系连接的节点的属性,以及新删除的关系的属性(如果有)。
在此订阅中不包括可能已在过程中被删除或未被删除的断开连接的节点。要订阅这些节点的更新,请使用 |
可以使用顶级订阅 [type]RelationshipDeleted
订阅 DELETE_RELATIONSHIP
事件,该订阅包含以下字段
-
event
:触发此订阅的事件(在本例中为DELETE_RELATIONSHIP
)。 -
timestamp
:进行变异的时间戳。如果同一个查询触发多个事件,则它们应具有相同的时间戳。 -
<typename>
:目标节点的顶级属性(不包括关系),在触发DELETE_RELATIONSHIP
操作之前。 -
relationshipFieldName
:新删除的关系的字段名称。 -
deletedRelationship
:一个对象,包含受新删除的关系影响的节点的所有字段名称。虽然与relationshipFieldName
无关的任何事件都应为null
,但相关的事件应包含关系属性(如果已定义)和一个包含关系另一侧节点属性的节点键。仅提供顶级属性(不包括关系),并且它们是在DELETE_RELATIONSHIP
操作发生之前已存在的属性。
无论数据库中关系的方向如何, |
例如,考虑以下类型定义
type Movie {
title: String
genre: String
actors: [Actor] @relationship(type: "ACTED_IN", direction: IN, properties: "ActedIn")
reviewers: [Reviewer] @relationship(type: "REVIEWED", direction: IN, properties: "Reviewed")
}
type Actor {s
name: String
}
type ActedIn @relationshipProperties {
screenTime: Int!
}
type Reviewer {
name: String
reputation: Int
}
type Reviewed @relationshipProperties {
score: Int!
}
现在考虑一个 mutation,删除名为 Tom Hardy
的 Actor
和名为 Jane
的 Reviewer
,它们通过关系连接到标题为 Inception
的 Movie
。在这种情况下,DELETE_RELATIONSHIP
订阅应接收以下事件
{
# 1 - relationship type `ACTED_IN`
event: "DELETE_RELATIONSHIP",
timestamp,
movie: {
title: "Inception",
genre: "Adventure"
},
relationshipFieldName: "actors", # notice the field name specified here is populated below in the `createdRelationship` object
deletedRelationship: {
actors: {
screenTime: 1000, # relationship properties for the relationship type `ACTED_IN` that was deleted
node: { # top-level properties of the node at the other end of the relationship, in this case `Actor` type, before the delete occurred
name: "Tom Hardy"
}
},
reviewers: null # relationship declared by this field name is not covered by this event, check out the following...
}
}
{
# 2 - relationship type `REVIEWED`
event: "DELETE_RELATIONSHIP",
timestamp,
movie: {
title: "Inception",
genre: "Adventure"
},
relationshipFieldName: "reviewers", # this event covers the relationship declared by this field name
deletedRelationship: {
actors: null, # relationship declared by this field name is not covered by this event
reviewers: { # field name equal to `relationshipFieldName`
score: 8,
node: {
name: "Jane",
reputation: 9
}
}
}
}
标准类型
例如,考虑以下类型定义
type Movie {
title: String
genre: String
actors: [Actor] @relationship(type: "ACTED_IN", direction: IN, properties: "ActedIn")
}
type Actor {
name: String
}
type ActedIn @relationshipProperties {
screenTime: Int!
}
对任何已删除的 Movie
关系的订阅如下所示
subscription {
movieRelationshipDeleted {
event
timestamp
movie {
title
genre
}
relationshipFieldName
deletedRelationship {
actors {
screenTime
node {
name
}
}
}
}
}
使用抽象类型删除关系
当使用带有关系的抽象类型时,您需要在执行订阅操作时指定一个或多个相应的具体类型。
这些类型由库生成,并符合 [type]EventPayload
格式,其中 [type]
是一个**具体类型**。
联合示例
考虑以下类型定义
type Movie {
title: String
genre: String
directors: [Director!]! @relationship(type: "DIRECTED", properties: "Directed", direction: IN)
}
union Director = Person | Actor
type Actor {
name: String
}
type Person {
name: String
reputation: Int
}
type Directed @relationshipProperties {
year: Int!
}
对已删除的 Movie
关系的订阅如下所示
subscription {
movieRelationshipDeleted {
event
timestamp
movie {
title
genre
}
relationshipFieldName
deletedRelationship {
directors {
year
node {
... on PersonEventPayload { # generated type
name
reputation
}
... on ActorEventPayload { # generated type
name
}
}
}
}
}
}
接口示例
考虑以下类型定义
type Movie {
title: String
genre: String
reviewers: [Reviewer!]! @relationship(type: "REVIEWED", properties: "Review", direction: IN)
}
interface Reviewer {
reputation: Int!
}
type Magazine implements Reviewer {
title: String
reputation: Int!
}
type Influencer implements Reviewer {
name: String
reputation: Int!
}
type Review @relationshipProperties {
score: Int!
}
对已删除的 Movie
关系的订阅如下所示
subscription {
movieRelationshipDeleted {
event
timestamp
movie {
title
genre
}
relationshipFieldName
deletedRelationship {
reviewers {
score
node {
reputation
... on MagazineEventPayload { # generated type
title
reputation
}
... on InfluencerEventPayload { # generated type
name
reputation
}
}
}
}
}
}
非互惠关系
考虑以下类型定义
type Movie {
title: String
genre: String
actors: [Actor] @relationship(type: "ACTED_IN", direction: IN, properties: "ActedIn")
directors: [Director!]! @relationship(type: "DIRECTED", properties: "Directed", direction: IN)
}
type Actor {
name: String
movies: [Movie!]! @relationship(type: "ACTED_IN", properties: "ActedIn", direction: OUT)
}
type Person {
name: String
reputation: Int
}
union Director = Person | Actor
type ActedIn @relationshipProperties {
screenTime: Int!
}
type Directed @relationshipProperties {
year: Int!
}
类型定义包含 2 个关系:类型 ACTED_IN
和 DIRECTED
。
可以观察到,ACTED_IN
关系在 Movie
和 Actor
类型中都定义了相应的字段。因此,我们可以说 ACTED_IN
是一个互惠关系。
另一方面,DIRECTED
仅在 Movie
类型中定义。Director
类型未定义匹配的字段。因此,我们可以说 DIRECTED
**不是**互惠关系。
现在让我们看看如何订阅上面定义的 3 种类型的已删除关系
Movie
subscription {
movieRelationshipDeleted {
event
timestamp
movie {
title
genre
}
relationshipFieldName
deletedRelationship {
actors { # corresponds to the `ACTED_IN` relationship type
screenTime
node {
name
}
}
directors { # corresponds to the `DIRECTED` relationship type
year
node {
... on PersonEventPayload {
name
reputation
}
... on ActorEventPayload {
name
}
}
}
}
}
}
Actor
subscription {
actorRelationshipDeleted {
event
timestamp
actor {
name
}
relationshipFieldName
deletedRelationship {
movies { # corresponds to the `ACTED_IN` relationship type
screenTime
node {
title
genre
}
}
# no other field corresponding to the `DIRECTED` relationship type
}
}
}
actorRelationshipDeleted
订阅中 deletedRelationship
内存在 movie
字段反映了 ACTED_IN
类型关系是互惠的事实。
因此,当此类型的关系被删除时,例如通过运行以下 mutation
mutation {
createMovies(
input: [
{
actors: {
create: [
{
node: {
name: "Keanu Reeves"
},
edge: {
screenTime: 420
}
}
]
},
title: "John Wick",
genre: "Action"
}
]
) {
movies {
title
genre
}
}
}
mutation {
deleteMovies(
where: {
title: "John Wick"
}
) {
nodesDeleted
}
}
将发布两个事件(假设我们已订阅了两种类型的 DELETE_RELATIONSHIP
事件)。
{
# from `movieRelationshipDeleted`
event: "DELETE_RELATIONSHIP"
timestamp
movie {
title: "John Wick",
genre: "Action"
}
relationshipFieldName: "actors",
deletedRelationship {
actors: {
screenTime: 420,
node: {
name: "Keanu Reeves"
}
},
directors: null
}
},
{
# from `actorRelationshipDeleted`
event: "DELETE_RELATIONSHIP"
timestamp
actor {
name: "Keanu Reeves"
}
relationshipFieldName: "movies",
deletedRelationship {
movies: {
screenTime: 420,
node: {
title: "John Wick",
genre: "Action"
}
}
}
}
由于类型 Movie
和 Director
之间的 DIRECTED
关系**不是**互惠关系,因此执行以下 mutation
mutation {
createMovies(
input: [
{
directors: {
Actor: { # relationship 1
create: [
{
node: {
name: "Woody Allen"
},
edge: {
year: 1989
}
}
]
},
Person: { # relationship 2
create: [
{
node: {
name: "Francis Ford Coppola",
reputation: 100
},
edge: {
year: 1989
}
}
]
}
},
title: "New York Stories",
genre: "Comedy"
}
]
) {
movies {
title
genre
}
}
}
mutation {
deleteMovies(
where: {
title: "New York Stories"
}
) {
nodesDeleted
}
}
将发布两个事件(假设我们已订阅了 Movie
类型的 DELETE_RELATIONSHIP
事件)。
{
# relationship 1 - from `movieRelationshipDeleted`
event: "DELETE_RELATIONSHIP"
timestamp
movie {
title: "New York Stories",
genre: "Comedy"
}
relationshipFieldName: "directors",
deletedRelationship {
actors: null,
directors: {
year: 1989,
node: {
name: "Woody Allen"
}
}
}
},
{
# relationship 2 - from `movieRelationshipDeleted`
event: "DELETE_RELATIONSHIP"
timestamp
movie {
title: "New York Stories",
genre: "Comedy"
}
relationshipFieldName: "directors",
deletedRelationship {
actors: null,
directors: {
year: 1989,
node: {
name: "Francis Ford Coppola",
reputation: 100
}
}
}
}
特殊注意事项
使用相同 Neo4j 标签的类型
一个需要特别考虑的情况是,覆盖特定 GraphQL 类型的 Neo4j 中的标签。这可以通过使用 @node
指令并指定 label
参数来实现。
虽然本节起到了信息作用,但需要说明的是,在大多数情况下,**不建议**使用这种方法来设计您的 API。 |
考虑以下类型定义
type Actor @node(label: "Person") {
name: String
movies: [Movie!]! @relationship(type: "PART_OF", direction: OUT)
}
typePerson {
name: String
movies: [Movie!]! @relationship(type: "PART_OF", direction: OUT)
}
type Movie {
title: String
genre: String
people: [Person!]! @relationship(type: "PART_OF", direction: IN)
actors: [Actor!]! @relationship(type: "PART_OF", direction: IN)
}
尽管我们有 3 个 GraphQL 类型,但在 Neo4j 中,节点类型应该只有 2 种:标记为 Movie
或标记为 Person
。
在数据库级别,Actor
和 Person
之间没有区别。因此,在删除类型为 PART_OF
的关系时,每种类型都应该有一个事件。
考虑以下订阅
subscription {
movieRelationshipDeleted {
event
timestamp
movie {
title
genre
}
relationshipFieldName
deletedRelationship {
people { # corresponds to the `PART_OF` relationship type
node {
name
}
}
actors { # corresponds to the `PART_OF` relationship type
node {
name
}
}
}
}
}
subscription {
actorRelationshipDeleted {
event
timestamp
actor {
name
}
relationshipFieldName
deletedRelationship {
movies { # corresponds to the `PART_OF` relationship type
node {
title
genre
}
}
}
}
}
运行以下 mutation
mutation {
createMovies(
input: [
{
people: { # relationship 1
create: [
{
node: {
name: "John Logan"
}
}
]
},
actors: { # relationship 2
create: [
{
node: {
name: "Johnny Depp"
}
}
]
},
title: "Sweeney Todd",
genre: "Horror"
}
]
) {
movies {
title
genre
}
}
}
mutation {
deleteMovies(
where: {
title: "Sweeney Todd"
}
) {
nodesDeleted
}
}
导致以下事件
{
# relationship 1 `people` - for GraphQL types `Movie`, `Person`
event: "DELETE_RELATIONSHIP"
timestamp
movie {
title: "Sweeney Todd",
genre: "Horror"
}
relationshipFieldName: "people",
deletedRelationship {
people: {
node: {
name: "John Logan"
}
},
actors: null
}
},
{
# relationship 1 `people` - for GraphQL types `Movie`, `Actor`
event: "DELETE_RELATIONSHIP"
timestamp
movie {
title: "Sweeney Todd",
genre: "Horror"
}
relationshipFieldName: "actors",
deletedRelationship {
people: null,
actors: {
node: {
name: "John Logan"
}
}
}
},
{
# relationship 1 `movies` - for GraphQL types `Actor`, `Movie`
event: "DELETE_RELATIONSHIP"
timestamp
actor {
name: "John Logan"
}
relationshipFieldName: "movies",
deletedRelationship {
movies: {
node: {
title: "Sweeney Todd",
genre: "Horror"
}
}
}
},
{
# relationship 2 `actors` - for GraphQL types `Movie`,`Person`
event: "DELETE_RELATIONSHIP"
timestamp
movie {
title: "Sweeney Todd",
genre: "Horror"
}
relationshipFieldName: "people",
deletedRelationship {
people: {
node: {
name: "Johnny Depp"
}
},
actors: null
}
},
{
# relationship 2 `actors` - for GraphQL types `Movie`, `Actor`
event: "DELETE_RELATIONSHIP"
timestamp
movie {
title: "Sweeney Todd",
genre: "Horror"
}
relationshipFieldName: "actors",
deletedRelationship {
people: null,
actors: {
node: {
name: "Johnny Depp"
}
}
}
},
{
# relationship 2 `movies` - for GraphQL types `Actor`, `Movie`
event: "DELETE_RELATIONSHIP"
timestamp
actor {
name: "Johnny Depp"
}
relationshipFieldName: "movies",
deletedRelationship {
movies: {
node: {
title: "Sweeney Todd",
genre: "Horror"
}
}
}
},
如果我们也订阅了 Person
,则会收到另外两个事件。
{
# relationship 1 `movies` - for GraphQL types `Person`, `Movie`
event: "DELETE_RELATIONSHIP"
timestamp
actor {
name: "John Logan"
}
relationshipFieldName: "movies",
deletedRelationship {
movies: {
node: {
title: "Sweeney Todd",
genre: "Horror"
}
}
}
},
{
# relationship 2 `movies` - for GraphQL types `Person`, `Movie`
event: "DELETE_RELATIONSHIP"
timestamp
actor {
name: "Johnny Depp"
}
relationshipFieldName: "movies",
deletedRelationship {
movies: {
node: {
title: "Sweeney Todd",
genre: "Horror"
}
}
}
},