Reputation: 586
I'm working on a small recommendation system to recommend players for the next game. The recommendation consists of first the followed players and then the players who the player has played before ordered by the number of games.
The result would be Player, is_followed (true|false), GameIds
I have Player
and Game
nodes where are related as Game -[:HAS_PLAYER]-> Player
and
the Player
nodes are directly related as Player -[:FOLLOWS]-> Player
.
My test DB:
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)
;
With this data, I want to recommend to Batman a player for his next game and the result has to be:
superman true [game-1, game-2]
catwoman true []
aquaman false [game-1]
flash false [game-1]
joker false [game-2]
lex false [game-2]
UPDATE: I have the next query but the followed players are missing:
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
Thank you!
UPDATE 2
This approach could be more 'readable' but the performance is much worse than the accepted answer.
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
Upvotes: 0
Views: 214
Reputation: 224
you do not get catwoman because you only match :Player related to batman through a :Game node, you need to also match (u:Player {playerID:"batman"})-[:FOLLOWS]->(p:Player)
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
(could not think of a way to do it wouthout union, but it might be possible)
Upvotes: 1