So far, the website had only been a plain old movie database. We now wanted to add a touch of social to it.
So we started out by taking the User class that we'd already coded and made it a full-fledged Spring Data Neo4j entity. We added the ability to create friends and to rate movies. With that we also added a simple UserRepository that was able to look up users by ID.
The relationships of the user are his friends and the movie-ratings which is implemented
with a Rating Relationship-Entity. This time we used a different approach
(for educational and curiosity purposes) to create the Rating relationships.
The createRelationshipBetween operation of the Neo4jTemplate was our matchmaker of
choice.
Example 12.1. Social entities
@NodeEntity
class User {
@Indexed(unique=true) String login;
String name;
String password;
@RelatedToVia(type = RATED)
@Fetch Set<Rating> ratings;
@RelatedTo(type = "FRIEND", direction=Direction.BOTH)
@Fetch Set<User> friends;
public Rating rate(Neo4jOperations template, Movie movie, int stars, String comment) {
final Rating rating = template.createRelationshipBetween(this, movie, Rating.class, RATED, false);
rating.rate(stars, comment);
return template.save(rating);
}
public void addFriend(User user) {
this.friends.add(user);
}
}
@RelationshipEntity
class Rating {
@StartNode User user;
@EndNode Movie movie;
int stars;
String comment;
public Rating rate(int stars, String comment) {
this.stars = stars; this.comment = comment;
return this;
}
}
We extended the DatabasePopulator to add some users and ratings to the initial setup.
Example 12.2. Populate users and ratings
@Transactional
public List<Movie> populateDatabase() {
Actor tomHanks = new Actor("1", "Tom Hanks");
Movie forestGump = new Movie("1", "Forrest Gump");
tomHanks.playedIn(forestGump, "Forrest");
template.save(tomHanks);
User me = template.save(new User("micha", "Micha", "password"));
Rating awesome = me.rate(template, forestGump, 5, "Awesome");
User ollie = template.save(new User("ollie", "Oliver", "password"));
ollie.rate(template,forestGump, 2, "ok");
me.addFriend(ollie);
template.save(me);
return asList(forestGump);
}
We also put a ratings field into the Movie class to be able to get a movie's ratings, and also a method to average its star rating.
Example 12.3. Getting the rating of a movie
class Movie {
...
@RelatedToVia(type="RATED", direction = Direction.INCOMING)
@Fetch Iterable<Rating> ratings;
public int getStars() {
int stars = 0, count = 0;
for (Rating rating : ratings) {
stars += rating.getStars(); count++;
}
return count == 0 ? 0 : stars / count;
}
}
Fortunately our tests highlighted the division by zero error when calculating the stars for a movie without ratings. The next steps were to add this information to the movie presentation in the UI, and creating a user profile page. But for that to happen, users must first be able to log in.