Neo4J (Cypher) 按节点对多个关系进行分组
Neo4J (Cypher) Group the multiple relationships by node
我正在开发一个小型推荐系统,为下一场比赛推荐玩家。推荐顺序是先关注的玩家,再是之前玩过的玩家,按游戏次数排序。
结果会是Player, is_followed (true|false), GameIds
我有 Player
和 Game
节点,它们的关系为 Game -[:HAS_PLAYER]-> Player
和
Player
个节点直接相关为 Player -[:FOLLOWS]-> Player
。
我的测试数据库:
CREATE (superman:Player {playerID: 'superman', name:'Superman', sanitizedName: 'superman'})
CREATE (batman:Player {playerID: 'batman', name:'Batman', sanitizedName: 'batman'})
CREATE (catwoman:Player {playerID: 'catwoman', name:'Catwoman', sanitizedName: 'catwoman'})
CREATE (lois:Player {playerID: 'lois', name:'Lois Lane', sanitizedName: 'lois lane'})
CREATE (wonderwoman:Player {playerID: 'wonderwoman', name:'Wonder Woman', sanitizedName: 'wonder woman'})
CREATE (aquaman:Player {playerID: 'aquaman', name:'Aquaman', sanitizedName: 'aquaman'})
CREATE (flash:Player {playerID: 'flash', name:'Flash', sanitizedName: 'flash'})
CREATE (cyborg:Player {playerID: 'cyborg', name:'Cyborg', sanitizedName: 'cyborg'})
CREATE (joker:Player {playerID: 'joker', name:'Joker', sanitizedName: 'joker'})
CREATE (lex:Player {playerID: 'lex', name:'Lex Luthor', sanitizedName: 'lex luthor'})
CREATE (green:Player {playerID: 'green', name:'Green Lantern', sanitizedName: 'green lantern'})
CREATE (g1:Game {gameId:"game-1"})
CREATE (g1)-[:HAS_PLAYERS {teamId:"0"}]->(superman)
CREATE (g1)-[:HAS_PLAYERS {teamId:"0"}]->(aquaman)
CREATE (g1)-[:HAS_PLAYERS {teamId:"1"}]->(batman)
CREATE (g1)-[:HAS_PLAYERS {teamId:"1"}]->(flash)
CREATE (g2:Game {gameId:"game-2"})
CREATE (g2)-[:HAS_PLAYERS {teamId:"0"}]->(superman)
CREATE (g2)-[:HAS_PLAYERS {teamId:"0"}]->(batman)
CREATE (g2)-[:HAS_PLAYERS {teamId:"1"}]->(joker)
CREATE (g2)-[:HAS_PLAYERS {teamId:"1"}]->(lex)
CREATE (g3:Game {gameId:"game-3"})
CREATE (g3)-[:HAS_PLAYERS {teamId:"0"}]->(flash)
CREATE (g3)-[:HAS_PLAYERS {teamId:"0"}]->(lois)
CREATE (g3)-[:HAS_PLAYERS {teamId:"1"}]->(wonderwoman)
CREATE (g3)-[:HAS_PLAYERS {teamId:"1"}]->(aquaman)
CREATE (g4:Game {gameId:"game-4"})
CREATE (g4)-[:HAS_PLAYERS {teamId:"0"}]->(joker)
CREATE (g4)-[:HAS_PLAYERS {teamId:"0"}]->(cyborg)
CREATE (g4)-[:HAS_PLAYERS {teamId:"1"}]->(wonderwoman)
CREATE (g4)-[:HAS_PLAYERS {teamId:"1"}]->(green)
CREATE (g5:Game {gameId:"game-5"})
CREATE (g5)-[:HAS_PLAYERS {teamId:"0"}]->(catwoman)
CREATE (g5)-[:HAS_PLAYERS {teamId:"1"}]->(green)
CREATE (batman)-[:FOLLOWS]->(superman)
CREATE (batman)-[:FOLLOWS]->(catwoman)
CREATE (lex)-[:FOLLOWS]->(joker)
CREATE (joker)-[:FOLLOWS]->(lex)
;
根据这些数据,我想向蝙蝠侠推荐下一场比赛的球员,结果必须是:
superman true [game-1, game-2]
catwoman true []
aquaman false [game-1]
flash false [game-1]
joker false [game-2]
lex false [game-2]
更新:
我有下一个查询,但缺少以下玩家:
MATCH (u:Player{playerID:"batman"})
OPTIONAL MATCH (u)<-[:HAS_PLAYERS]-(g:Game)-[:HAS_PLAYERS]->(p:Player)
RETURN p, EXISTS((u)-[:FOLLOWS]->(p)) AS is_followed, COUNT(g) AS num_games, collect(g) AS games
ORDER BY num_games DESC
谢谢!
更新 2
这种方法可能更 'readable' 但性能比公认的答案差得多。
MATCH (u:Player{playerID:"batman"})
CALL {
WITH u
MATCH (u)<-[:HAS_PLAYERS]-(g:Game)-[:HAS_PLAYERS]->(p:Player)
RETURN p
UNION
WITH u
MATCH (u)-[:FOLLOWS]->(p:Player)
RETURN p
}
WITH p,
EXISTS((u)-[:FOLLOWS]->(p)) AS is_followed,
[(u)<-[:HAS_PLAYERS]-(g:Game)-[:HAS_PLAYERS]->(p) | g.gameId] AS games
RETURN p.name, is_followed, size(games) AS num_games, games
ORDER BY num_games DESC
你没有得到猫女,因为你只通过 :Game 节点匹配与蝙蝠侠相关的 :Player,你还需要匹配 (u:Player {playerID:"batman"})-[:FOLLOWS]->( p:玩家)
MATCH (u:Player{playerID:"batman"})
OPTIONAL MATCH (u)<-[:HAS_PLAYERS]-(g:Game)-[:HAS_PLAYERS]->(p:Player)
RETURN p.playerID, EXISTS((u)-[:FOLLOWS]->(p)) AS is_followed, COUNT(g) AS num_games, collect(g.gameId) AS games
ORDER BY num_games DESC
union
match (u:Player{playerID:"batman"})-[:FOLLOWS]->(p:Player)
where not exists( (u)<-[:HAS_PLAYERS]-(:Game)-[:HAS_PLAYERS]->(p:Player) )
RETURN p.playerID, true AS is_followed, 0 AS num_games, [] AS games
(想不出没有联合的方法,但它可能是可能的)
我正在开发一个小型推荐系统,为下一场比赛推荐玩家。推荐顺序是先关注的玩家,再是之前玩过的玩家,按游戏次数排序。
结果会是Player, is_followed (true|false), GameIds
我有 Player
和 Game
节点,它们的关系为 Game -[:HAS_PLAYER]-> Player
和
Player
个节点直接相关为 Player -[:FOLLOWS]-> Player
。
我的测试数据库:
CREATE (superman:Player {playerID: 'superman', name:'Superman', sanitizedName: 'superman'})
CREATE (batman:Player {playerID: 'batman', name:'Batman', sanitizedName: 'batman'})
CREATE (catwoman:Player {playerID: 'catwoman', name:'Catwoman', sanitizedName: 'catwoman'})
CREATE (lois:Player {playerID: 'lois', name:'Lois Lane', sanitizedName: 'lois lane'})
CREATE (wonderwoman:Player {playerID: 'wonderwoman', name:'Wonder Woman', sanitizedName: 'wonder woman'})
CREATE (aquaman:Player {playerID: 'aquaman', name:'Aquaman', sanitizedName: 'aquaman'})
CREATE (flash:Player {playerID: 'flash', name:'Flash', sanitizedName: 'flash'})
CREATE (cyborg:Player {playerID: 'cyborg', name:'Cyborg', sanitizedName: 'cyborg'})
CREATE (joker:Player {playerID: 'joker', name:'Joker', sanitizedName: 'joker'})
CREATE (lex:Player {playerID: 'lex', name:'Lex Luthor', sanitizedName: 'lex luthor'})
CREATE (green:Player {playerID: 'green', name:'Green Lantern', sanitizedName: 'green lantern'})
CREATE (g1:Game {gameId:"game-1"})
CREATE (g1)-[:HAS_PLAYERS {teamId:"0"}]->(superman)
CREATE (g1)-[:HAS_PLAYERS {teamId:"0"}]->(aquaman)
CREATE (g1)-[:HAS_PLAYERS {teamId:"1"}]->(batman)
CREATE (g1)-[:HAS_PLAYERS {teamId:"1"}]->(flash)
CREATE (g2:Game {gameId:"game-2"})
CREATE (g2)-[:HAS_PLAYERS {teamId:"0"}]->(superman)
CREATE (g2)-[:HAS_PLAYERS {teamId:"0"}]->(batman)
CREATE (g2)-[:HAS_PLAYERS {teamId:"1"}]->(joker)
CREATE (g2)-[:HAS_PLAYERS {teamId:"1"}]->(lex)
CREATE (g3:Game {gameId:"game-3"})
CREATE (g3)-[:HAS_PLAYERS {teamId:"0"}]->(flash)
CREATE (g3)-[:HAS_PLAYERS {teamId:"0"}]->(lois)
CREATE (g3)-[:HAS_PLAYERS {teamId:"1"}]->(wonderwoman)
CREATE (g3)-[:HAS_PLAYERS {teamId:"1"}]->(aquaman)
CREATE (g4:Game {gameId:"game-4"})
CREATE (g4)-[:HAS_PLAYERS {teamId:"0"}]->(joker)
CREATE (g4)-[:HAS_PLAYERS {teamId:"0"}]->(cyborg)
CREATE (g4)-[:HAS_PLAYERS {teamId:"1"}]->(wonderwoman)
CREATE (g4)-[:HAS_PLAYERS {teamId:"1"}]->(green)
CREATE (g5:Game {gameId:"game-5"})
CREATE (g5)-[:HAS_PLAYERS {teamId:"0"}]->(catwoman)
CREATE (g5)-[:HAS_PLAYERS {teamId:"1"}]->(green)
CREATE (batman)-[:FOLLOWS]->(superman)
CREATE (batman)-[:FOLLOWS]->(catwoman)
CREATE (lex)-[:FOLLOWS]->(joker)
CREATE (joker)-[:FOLLOWS]->(lex)
;
根据这些数据,我想向蝙蝠侠推荐下一场比赛的球员,结果必须是:
superman true [game-1, game-2]
catwoman true []
aquaman false [game-1]
flash false [game-1]
joker false [game-2]
lex false [game-2]
更新: 我有下一个查询,但缺少以下玩家:
MATCH (u:Player{playerID:"batman"})
OPTIONAL MATCH (u)<-[:HAS_PLAYERS]-(g:Game)-[:HAS_PLAYERS]->(p:Player)
RETURN p, EXISTS((u)-[:FOLLOWS]->(p)) AS is_followed, COUNT(g) AS num_games, collect(g) AS games
ORDER BY num_games DESC
谢谢!
更新 2
这种方法可能更 'readable' 但性能比公认的答案差得多。
MATCH (u:Player{playerID:"batman"})
CALL {
WITH u
MATCH (u)<-[:HAS_PLAYERS]-(g:Game)-[:HAS_PLAYERS]->(p:Player)
RETURN p
UNION
WITH u
MATCH (u)-[:FOLLOWS]->(p:Player)
RETURN p
}
WITH p,
EXISTS((u)-[:FOLLOWS]->(p)) AS is_followed,
[(u)<-[:HAS_PLAYERS]-(g:Game)-[:HAS_PLAYERS]->(p) | g.gameId] AS games
RETURN p.name, is_followed, size(games) AS num_games, games
ORDER BY num_games DESC
你没有得到猫女,因为你只通过 :Game 节点匹配与蝙蝠侠相关的 :Player,你还需要匹配 (u:Player {playerID:"batman"})-[:FOLLOWS]->( p:玩家)
MATCH (u:Player{playerID:"batman"})
OPTIONAL MATCH (u)<-[:HAS_PLAYERS]-(g:Game)-[:HAS_PLAYERS]->(p:Player)
RETURN p.playerID, EXISTS((u)-[:FOLLOWS]->(p)) AS is_followed, COUNT(g) AS num_games, collect(g.gameId) AS games
ORDER BY num_games DESC
union
match (u:Player{playerID:"batman"})-[:FOLLOWS]->(p:Player)
where not exists( (u)<-[:HAS_PLAYERS]-(:Game)-[:HAS_PLAYERS]->(p:Player) )
RETURN p.playerID, true AS is_followed, 0 AS num_games, [] AS games
(想不出没有联合的方法,但它可能是可能的)