Neo4j/Cypher 试图解开用户 ID 列表,为所有匹配的用户创建一个通知和关系

Neo4j/Cypher attempting to UNWIND a list of user ids, create one Notification and relationship to all users that were matched

我有这个密码查询:

OPTIONAL MATCH (allMentionedUsers:User)
WHERE allMentionedUsers.id in $mentions
UNWIND allMentionedUsers as mentionedUser
CREATE (n:Notification {
    id: apoc.create.uuid(),
    notificationType: 'MENTION',
    notifierName: u.username,
    body: 'mentioned you',
    createdAt: datetime(),
    updatedAt: datetime()
})
CREATE (p)-[:HAS_NOTIFICATION]->(n)-[:NOTIFIES]->(mentionedUser)

检查变量 $mentions(字符串 ID 列表)以查找可能的用户匹配项,然后解开所有匹配的用户以创建通知和后续关系。 p 是另一个父节点,对于这个问题来说不太重要。 u 在这里也不重要,并且按预期工作。

基本上我希望有一个通知与一个或多个用户节点有 NOTIFIES 关系。

目前,上述语句为每个用户创建了一个单独的通知。有人看到我如何修改它以创建一个通知节点,该节点与匹配的用户具有尽可能多的 NOTIFIES 关系吗?

它正在创建多个通知节点,因为创建是在 UNWIND 中进行的。

使用 Merge 函数而不是 Create。合并不会创建副本,因为如果它发现相同的 node/relationships 存在,那么它会跳过它而不创建另一个。它就像一个 MATCH,如果找不到则创建。同时,CREATE 不会检查是否存在相同的node/relationshps。

OPTIONAL MATCH (allMentionedUsers:User)
WHERE allMentionedUsers.id in $mentions
UNWIND allMentionedUsers as mentionedUser
MERGE (p)-[:HAS_NOTIFICATION]->(n:Notification {
    id: apoc.create.uuid(),
    notificationType: 'MENTION',
    notifierName: u.username,
    body: 'mentioned you',
    createdAt: datetime(),
    updatedAt: datetime()
})-[:NOTIFIES]->(mentionedUser)

您的 UNWIND 实际上没有做任何事情,这里是空操作。 UNWIND 适用于列表,但 allMentionedUsers 不是列表(最好使用单数而不是复数作为变量名称。

您可以先合并通知,然后才创建其余的模式。

...
MERGE (n:Notification {
    id: apoc.create.uuid(),
    notificationType: 'MENTION',
    notifierName: u.username,
    body: 'mentioned you',
    createdAt: datetime(),
    updatedAt: datetime()
})
WITH u, p, n
OPTIONAL MATCH (mentionedUser:User)
WHERE mentionedUser.id in $mentions
MERGE (p)-[:HAS_NOTIFICATION]->(n)
MERGE (n)-[:NOTIFIES]->(mentionedUser)

或者您可以将用户收集到一个列表中并执行相同的操作,这可能会更好一些,因为 MERGE 操作会发生一次而不是对每一行进行:

...
OPTIONAL MATCH (mentionedUser:User)
WHERE mentionedUser.id in $mentions
WITH u, p, collect(mentionedUser) as allMentionedUsers
MERGE (n:Notification {
    id: apoc.create.uuid(),
    notificationType: 'MENTION',
    notifierName: u.username,
    body: 'mentioned you',
    createdAt: datetime(),
    updatedAt: datetime()
})
MERGE (p)-[:HAS_NOTIFICATION]->(n)
FOREACH (mentionedUser IN allMentionedUsers | 
  MERGE (n)-[:NOTIFIES]->(mentionedUser) )

我正在使用 FOREACH 来处理每个提到的用户的通知,因为它会降低基数,如果您没有任何其他操作要对每个提到的用户执行,这一点很重要。

最后一个替代方案是将 allMentionedUsers 从头到尾保留在列表中,但这需要使用列表理解和模式理解,以及使用奇怪的替代模式语法来捕获单个节点的模式多部分模式(因为 Cypher 似乎不认为单节点是模式理解的有效模式)。

...
WITH u, p, [id in $mentions | [(user:User {id:id})-[*0]-() | user][0]] as allMentionedUsers
MERGE (n:Notification {
    id: apoc.create.uuid(),
    notificationType: 'MENTION',
    notifierName: u.username,
    body: 'mentioned you',
    createdAt: datetime(),
    updatedAt: datetime()
})
MERGE (p)-[:HAS_NOTIFICATION]->(n)
FOREACH (mentionedUser IN allMentionedUsers | 
  MERGE (n)-[:NOTIFIES]->(mentionedUser) )