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.