In the last part of this exercise we wanted to add recommendations to the app. One obvious recommendation was movies that our fiends liked.
There was this query language called Cypher that looked a bit like SQL but
expressed graph matching queries. 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 MovieRepository 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 like-minded cineasts that rated movies similarly to us. Again Cypher to 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 from a node, or set of nodes or relationships, and then expand outwards from there.
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 >= 3 return likeminded order by count(*) desc limit 10 ") Iterabe<User> suggestFriends(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.