Skip to content

Instantly share code, notes, and snippets.

@lfo
Last active August 29, 2015 14:02
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lfo/f896ce05e3046adfe4e3 to your computer and use it in GitHub Desktop.
Save lfo/f896ce05e3046adfe4e3 to your computer and use it in GitHub Desktop.
Song recommendation
digraph songGraph {
node [shape = circle, border = solid];
Artist [ style = filled, color="#f25a29" ];
Song [ style = filled, color="#30b6af" ];
Person [ style = filled, color="#ad62ce" ];
Artist -> Song [ label = "RECORDED " ];
Person -> Song [ label = "LOVES " ];
}
== Song Recommendation
According to this simple following graph :
image::https://gist.github.com/lfo/f896ce05e3046adfe4e3/raw/f676b878e0cbe6389b2a5b4b7f987d781a7de77b/model.png[]
I would like to get recommendation for songs and artists. Indeed, Artist has recorded songs that some persons love.
If somebody love the same song than mine, I will more probably love its other loved songs.
In order to illustrate, I do the following setup :
//setup
[source,cypher]
----
CREATE (SuzanneVega:Artist{name:'Suzanne Vega'}),
(PinkFloyd:Artist{name:'Pink Floyd'}),
(TheThe:Artist{name:'The The'}),
(Pink:Artist{name:'Pink'})
CREATE SuzanneVega -[:RECORDED]-> (Luka:Song{title:'Luka'}),
SuzanneVega -[:RECORDED]-> (TomsDiner:Song{title:'Tom\'s Diner'}),
PinkFloyd -[:RECORDED]-> (HighHopes:Song{title:'High Hopes'}),
PinkFloyd -[:RECORDED]-> (Mother:Song{title:'Mother'}),
PinkFloyd -[:RECORDED]-> (HeyYou:Song{title:'HeyYou'}),
TheThe -[:RECORDED]-> (SoulCatcher:Song{title:'Soul Catcher'}),
Pink -[:RECORDED]-> (Sober:Song{title:'Sober'}),
Pink -[:RECORDED]-> (SoWhat:Song{title:'SoWhat'})
CREATE (Me:Person{login:'Laurent F.'}),
(Frederic:Person{login:'Frédéric C.'}),
(Benoit:Person{login:'Benoit P.'}),
(Fabrice:Person{login:'Fabrice A.'}),
(Lilian:Person{login:'Lilian B.'}),
(Laurent:Person{login:'Laurent B.'}),
(Vincent:Person{login:'Vincent Van S.'}),
(Damien:Person{login:'Damien R.'})
CREATE
Me-[:LOVES]->Luka,
Me-[:LOVES]->TomsDiner,
Me-[:LOVES]->HighHopes,
Me-[:LOVES]->Mother,
Me-[:LOVES]->HeyYou,
Me-[:LOVES]->SoulCatcher,
Me-[:LOVES]->Sober,
Me-[:LOVES]->SoWhat,
Frederic-[:LOVES]->HighHopes,
Frederic-[:LOVES]->Mother,
Frederic-[:LOVES]->HeyYou,
Fabrice-[:LOVES]->SoulCatcher,
Lilian-[:LOVES]->Sober,
Lilian-[:LOVES]->SoWhat,
Benoit -[:LOVES]-> SoWhat,
Laurent -[:LOVES]->Sober,
Laurent -[:LOVES]-> SoulCatcher,
Vincent -[:LOVES]->Sober,
Vincent -[:LOVES]->Mother
----
//graph
== What are the most loved song ==
[source,cypher]
----
MATCH ()-[loves:LOVES]->(song:Song)
WITH song.title AS title, count(loves) AS counter
RETURN title, counter
ORDER BY counter DESC , title
----
//table
=== The same result with Artist name ===
[source,cypher]
----
MATCH ()-[loves:LOVES]->(song:Song)<-[RECORDED]- (artist:Artist)
WITH artist.name AS artist, song.title AS title, count(loves) AS counter
RETURN title, artist, counter
ORDER BY counter DESC , artist
----
//table
== Get one recommendation for Benoit ==
Get all the song that is loved by other person, knowing that Benoit love the same song than this other person, ordered by the most loved song.
[source,cypher]
----
MATCH (benoit:Person)-[:LOVES]-> (song:Song) <-[:LOVES]- (otherPerson:Person)-[:LOVES]->(otherSong:Song)<-[otherLoves:LOVES]-()
WHERE benoit.login = 'Benoit P.'
WITH otherSong.title AS title, count(distinct otherLoves) as counter
RETURN title, counter
ORDER BY counter DESC, title
LIMIT 3;
----
//table
In order to understand, the result we can construct our query step by step :
=== Step One ===
Get all LOVES relation from benoit
[source,cypher]
----
MATCH (benoit:Person)-[loves:LOVES]-> (song:Song)
where benoit.login = 'Benoit P.'
return loves, song.title;
----
//table
=== Step Two ===
Get all LOVES relation from persons that also have the previous LOVES relation. That means, we are looking for all loved song by people who loved also love benoit's loved song.
[source,cypher]
----
MATCH (benoit:Person)-[:LOVES]->(song:Song)<-[:LOVES]-(otherPerson:Person)-[otherLoves:LOVES]->(otherSong:Song)
WHERE benoit.login = 'Benoit P.'
RETURN otherLoves;
----
//table
== Get Recommendation for Damien ==
[source,cypher]
----
MATCH (damien:Person)-[:LOVES]-> (song:Song) <-[:LOVES]- (otherPerson:Person)-[:LOVES]->(otherSong:Song)<-[otherLoves:LOVES]-()
WHERE damien.login = 'Damien R.'
WITH otherSong.title AS title, count(distinct otherLoves) as counter
RETURN title, counter
ORDER BY counter DESC, title
LIMIT 3;
----
//table
Damien does not love any song at all for instance, so in order to give him some suggestions, maybe we can use one of the previous query. That means we can use the most loved song query, and use the union operator.
[source,cypher]
----
MATCH (damien:Person)-[:LOVES]->(song:Song)<-[:LOVES]-(otherPerson:Person)-[:LOVES]->(otherSong:Song)<-[otherLoves:LOVES]-()
WHERE damien.login = 'Damien R.'
WITH otherSong.title AS title, count(otherLoves) AS counter
RETURN title, counter
ORDER BY counter DESC , title
LIMIT 3
UNION
MATCH ()-[loves2:LOVES]->(song2:Song)
WITH song2.title AS title, count(loves2) AS counter
RETURN title, counter
ORDER BY counter DESC , title
LIMIT 3;
----
//table
== Artist Recommendation ==
TODO
Another example is given here : https://github.com/spring-projects/spring-data-neo4j/blob/master/spring-data-neo4j-examples/cineasts/src/main/java/org/neo4j/cineasts/repository/MovieRepository.java
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment