12. Adding social

Movies 2.0

So far, the website had only been a plain old movie database. We now wanted to add a touch of social to it.

12.1 Users

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);
}


12.2 Ratings for movies

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.