Chapter 16. Recommendations

Movies! Friends! Bargains!

In the last part of this exercise we wanted to add recommendations to the app. One obvious recommendation was movies that our fiends liked.

And there was this query language called Cypher that looked a bit like SQL but expressed graph query conditions. So we gave it a try, using the neo4j-shell, to incrementally expand the query, just by declaring what relationships we wanted to be taken into account and which properties of nodes and relationships to filter and sort on.

Example 16.1. Cypher based movie recommendation on Repository

interface Movie extends GraphRepository<Movie> {
	@Query("
	start user=node({0}) 
	match user-[:FRIEND]-friend-[r:RATED]->movie
	return movie
	order by avg(r.stars) desc, count(*) desc
	limit 10
	")
    Iterabe<Movie> recommendMovies(User me);
}


But we didn't have enough friends, so it was time to get some suggested. That would be likeminded cineasts that rated movies similiarly to us. Again cypher for the rescue, this time only a bit more complex. Something that became obvious with both queries is that graph queries are always local, so they start a node or set of nodes or relationships and expand outwards.

Example 16.2. Cypher - Friend Recommendation on Repository

interface UserRepository extends GraphRepository<User> {
	@Query("
	start user=node({0}) 
	match user-[r:RATED]->movie<-[r2:RATED]-likeminded,
	  user-[:FRIEND]-friend
	where r.stars > 3 and r2.stars => r.stars
	return likeminded
	order by count(*) desc
	limit 10
	")
    Iterabe<User> findFriends(User me);
}


The controllers simply called these methods, added their results to the model, and the view rendered the recommendations alongside the user's own ratings.