Mapping Relationships

You have created the application entities for movies and people, but you haven’t mapped the most unique part of the graph - relationships. In this lesson, you will map relationships between movies and people and create an application entity for relationship properties.

Add relationships to the Movie class

If you are familiar with mapping relationships in Spring applications for other types of data stores (such as relational databases), the syntax with Neo4j will not be completely foreign to you.

You will need a new field in the Movie domain class that represents the relationship between a movie and a person. Person entities can act in or direct movies, so you will map the ACTED_IN relationship from the Movie class to the Person class.

  1. Open the Movie.java domain class in your project’s src/main/java/com/example/appspringdata folder.

  2. Import org.springframework.data.neo4j.core.schema.Relationship and java.util.List classes.

    java
    import org.springframework.data.neo4j.core.schema.Relationship;
    import java.util.List;
  3. Add a new field to the Movie class called actors that returns a list of Person entities.

    java
        private List<Person> actors;
  4. Directly above the new field, annotate it with @Relationship and specify the relationship type as ACTED_IN and the direction as Relationship.Direction.INCOMING.

    java
        @Relationship(value = "ACTED_IN", direction = Relationship.Direction.INCOMING)
        private List<Person> actors;
  5. Add a getter and setter for the new field (do not add to the constructor).

    java
        public List<Person> getActors() {
            return actors;
        }
    
        public void setActors(List<Person> actors) {
            this.actors = actors;
        }

The @Relationship annotation has two attributes that are needed - relationship type (value) and direction. In this case, you are mapping the ACTED_IN relationship from the graph to the actors field in the Movie class that returns a list of Person entities. The ACTED_IN relationship goes from Person to Movie, so you specify the direction as INCOMING in the Movie class.

You don’t need to add the field to the constructor because you can create a movie node in the graph that doesn’t have any actors. You do need to add the field to the getters and setters, though, to set and retrieve actors with their related movies.

Click to reveal the completed Movie class code
java
import org.springframework.data.annotation.Id;
import org.springframework.data.neo4j.core.schema.Node;
import org.springframework.data.neo4j.core.schema.Relationship;
import java.util.List;

@Node
public class Movie {
    @Id
    private String movieId;

    private String title;
    private String plot;
    private String poster;
    private String url;
    private String imdbId;
    private String tmdbId;
    private String released;

    private Long year;
    private Long runtime;
    private Long budget;
    private Long revenue;
    private Long imdbVotes;

    private Double imdbRating;

    private String[] languages;
    private String[] countries;

    @Relationship(value = "ACTED_IN", direction = Relationship.Direction.INCOMING)
    private List<Person> actors;

    public Movie(String movieId, String title, String plot, String poster, String url, String imdbId, String tmdbId,
            String released, Long year, Long runtime, Long budget, Long revenue, Long imdbVotes, Double imdbRating,
            String[] languages, String[] countries) {
        this.movieId = movieId;
        this.title = title;
        this.plot = plot;
        this.poster = poster;
        this.url = url;
        this.imdbId = imdbId;
        this.tmdbId = tmdbId;
        this.released = released;
        this.year = year;
        this.runtime = runtime;
        this.budget = budget;
        this.revenue = revenue;
        this.imdbVotes = imdbVotes;
        this.imdbRating = imdbRating;
        this.languages = languages;
        this.countries = countries;
    }

    public String getMovieId() {
        return movieId;
    }

    public void setMovieId(String movieId) {
        this.movieId = movieId;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getPlot() {
        return plot;
    }

    public void setPlot(String plot) {
        this.plot = plot;
    }

    public String getPoster() {
        return poster;
    }

    public void setPoster(String poster) {
        this.poster = poster;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getImdbId() {
        return imdbId;
    }

    public void setImdbId(String imdbId) {
        this.imdbId = imdbId;
    }

    public String getTmdbId() {
        return tmdbId;
    }

    public void setTmdbId(String tmdbId) {
        this.tmdbId = tmdbId;
    }

    public String getReleased() {
        return released;
    }

    public void setReleased(String released) {
        this.released = released;
    }

    public Long getYear() {
        return year;
    }

    public void setYear(Long year) {
        this.year = year;
    }

    public Long getRuntime() {
        return runtime;
    }

    public void setRuntime(Long runtime) {
        this.runtime = runtime;
    }

    public Long getBudget() {
        return budget;
    }

    public void setBudget(Long budget) {
        this.budget = budget;
    }

    public Long getRevenue() {
        return revenue;
    }

    public void setRevenue(Long revenue) {
        this.revenue = revenue;
    }

    public Long getImdbVotes() {
        return imdbVotes;
    }

    public void setImdbVotes(Long imdbVotes) {
        this.imdbVotes = imdbVotes;
    }

    public Double getImdbRating() {
        return imdbRating;
    }

    public void setImdbRating(Double imdbRating) {
        this.imdbRating = imdbRating;
    }

    public String[] getLanguages() {
        return languages;
    }

    public void setLanguages(String[] languages) {
        this.languages = languages;
    }

    public String[] getCountries() {
        return countries;
    }

    public void setCountries(String[] countries) {
        this.countries = countries;
    }

    public List<Person> getActors() {
        return actors;
    }

    public void setActors(List<Person> actors) {
        this.actors = actors;
    }
}

Test the application (again!)

Start the application and execute the curl 'localhost:8080/movies/119155' command in another terminal tab. You should see the same list of movie properties as before, plus an actors one containing a list with fields matching the Person class.

Movie actors results

If you run the Cypher statement in the sandbox tab on the right, it is missing the role property on the relationships. You will map those in the next lesson by creating a separate entity class for the relationship to include relationship properties.

Check Your Understanding

1. Movie ← Person relationships

Which direction should the ACTED_IN relationship be mapped in the Movie class?

  • ❏ OUTGOING

  • ✓ INCOMING

  • ❏ BIDIRECTIONAL

Hint

A Person entity ACTED_IN a Movie entity, so the relationship should be mapped coming into Movie.

Solution

The ACTED_IN relationship should be mapped as INCOMING in the Movie class.

Summary

In this lesson, you mapped the relationship between Movie and Person entities in the application domain by creating a property in the the Movie class.

Next, you will map relationship properties Movie and Person entities with a Role class.