Movie Lists and Pagination

Optional Lesson

This lesson is optional and will not count towards your achievement. To view the completed code, check out the 11-movie-lists branch.

In this challenge, you will implement the remaining methods in the MovieDAO for retrieving a list of movies:

  • get_by_genre() - should return a paginated list of movies that are listed in a particular Genre

  • get_for_actor() - should return a paginated list of movies that a particular Person has acted in

  • get_for_director() - should return a paginated list of movies that a particular Person has directed

These methods are very similar to the all() method which returns a full list, but with a different pattern in the MATCH clause.

For each subtask, we will provide you with the pattern required to run in the Cypher statement, plus any additional parameters that are required for the query to run.

All you need to do is take the code from the all() method of the MovieDAO and modify the pattern in the first line of the Cypher statement in the first argument of the tx.run() call, then if necessary add the additional parameters to the second argument.

If you get stuck at any point, you can click to reveal the completed solution or skip to the bottom for instructions on how to checkout the branch with the working solution.

get_by_genre()

The get_by_genre() method returns a paginated list of movies that are listed within a certain genre.

python
api/dao/movies.py
def get_by_genre(self, name, sort='title', order='ASC', limit=6, skip=0, user_id=None):
    # TODO: Get Movies in a Genre
    # TODO: The Cypher string will be formated so remember to escape the braces: {{name: $name}}
    # MATCH (m:Movie)-[:IN_GENRE]->(:Genre {name: $name})

    return popular[skip:limit]

Update the get_by_genre() method to use the following Cypher Pattern:

cypher
MATCH (m:Movie)-[:IN_GENRE]->(:Genre {name: $name})

The tx.run() call will need to include the name property, which represents the name of the genre.

Click to reveal the completed get_by_genre() method

Find the get_by_genre() method in api/dao/movies.py and modify the function to find a list of movies by genre.

python
api/dao/movies.py
def get_by_genre(self, name, sort='title', order='ASC', limit=6, skip=0, user_id=None):
    # Get Movies in a Genre
    def get_movies_in_genre(tx, sort, order, limit, skip, user_id):
        favorites = self.get_user_favorites(tx, user_id)

        cypher = """
            MATCH (m:Movie)-[:IN_GENRE]->(:Genre {{name: $name}})
            WHERE m.`{0}` IS NOT NULL
            RETURN m {{
                .*,
                favorite: m.tmdbId in $favorites
            }} AS movie
            ORDER BY m.`{0}` {1}
            SKIP $skip
            LIMIT $limit
        """.format(sort, order)

        result = tx.run(cypher, name=name, limit=limit, skip=skip, user_id=user_id, favorites=favorites)

        return [ row.get("movie") for row in result ]

    with self.driver.session() as session:
        return session.execute_read(get_movies_in_genre, sort, order, limit=limit, skip=skip, user_id=user_id)

get_for_actor()

The get_for_actor() method returns a paginated list of movies that a person has acted in.

python
api/dao/movies.py
def get_for_actor(self, id, sort='title', order='ASC', limit=6, skip=0, user_id=None):
    # TODO: Get Movies for an Actor
    # TODO: The Cypher string will be formated so remember to escape the braces: {{tmdbId: $id}}
    # MATCH (:Person {tmdbId: $id})-[:ACTED_IN]->(m:Movie)

    return popular[skip:limit]

Find the get_for_actor() method in api/dao/movies.py and modify the function to find a list of movies by actor.

The pattern required in the MATCH clause will be:

cypher
MATCH (:Person {tmdbId: $id})-[:ACTED_IN]->(m:Movie)

You will have to include the additional parameter id - The tmdbId property relating to the actor.

Click to reveal the completed getForActor() method
python
api/dao/movies.py
def get_for_actor(self, id, sort='title', order='ASC', limit=6, skip=0, user_id=None):
    # Get Movies for an Actor
    def get_movies_for_actor(tx, id, sort, order, limit, skip, user_id):
        favorites = self.get_user_favorites(tx, user_id)

        cypher = """
            MATCH (:Person {{tmdbId: $id}})-[:ACTED_IN]->(m:Movie)
            WHERE m.`{0}` IS NOT NULL
            RETURN m {{
                .*,
                favorite: m.tmdbId in $favorites
            }} AS movie
            ORDER BY m.`{0}` {1}
            SKIP $skip
            LIMIT $limit
        """.format(sort, order)

        result = tx.run(cypher, id=id, limit=limit, skip=skip, user_id=user_id, favorites=favorites)

        return [ row.get("movie") for row in result ]

    with self.driver.session() as session:
        return session.execute_read(get_movies_for_actor, id, sort, order, limit=limit, skip=skip, user_id=user_id)

get_for_director()

The get_for_director() method returns a paginated list of movies that a person has directed.

python
api/dao/movies.py
def get_for_director(self, id, sort='title', order='ASC', limit=6, skip=0, user_id=None):
    # TODO: Get Movies directed by a Person
    # TODO: The Cypher string will be formated so remember to escape the braces: {{name: $name}}
    # MATCH (:Person {tmdbId: $id})-[:DIRECTED]->(m:Movie)

    return popular[skip:limit]

Find the getForDirector() method in api/dao/movies.py and modify the function to find a list of movies by director.

The pattern required in the MATCH clause will be:

cypher
MATCH (:Person {tmdbId: $id})-[:DIRECTED]->(m:Movie)

You will have to include the additional parameter id - The tmdbId property relating to the director.

Click to reveal the completed get_for_director() method
python
api/dao/movies.py
def get_for_director(self, id, sort='title', order='ASC', limit=6, skip=0, user_id=None):
    # Get Movies directed by a Person
    def get_movies_for_director(tx, id, sort, order, limit, skip, user_id):
        favorites = self.get_user_favorites(tx, user_id)

        cypher = """
            MATCH (:Person {{tmdbId: $id}})-[:DIRECTED]->(m:Movie)
            WHERE m.`{0}` IS NOT NULL
            RETURN m {{
                .*,
                favorite: m.tmdbId in $favorites
            }} AS movie
            ORDER BY m.`{0}` {1}
            SKIP $skip
            LIMIT $limit
        """.format(sort, order)

        result = tx.run(cypher, id=id, limit=limit, skip=skip, user_id=user_id, favorites=favorites)

        return [ row.get("movie") for row in result ]

    with self.driver.session() as session:
        return session.execute_read(get_movies_for_director, id, sort, order, limit=limit, skip=skip, user_id=user_id)

Testing

To test that this functionality has been correctly implemented, run the following code in a new terminal session:

sh
Running the test
pytest -s tests/11_movie_lists__test.py

The test file is located at tests/11_movie_lists__test.py.

Are you stuck? Click here for help

If you get stuck, you can see a working solution by checking out the 11-movie-lists branch by running:

sh
Check out the 11-movie-lists branch
git checkout 11-movie-lists

You may have to commit or stash your changes before checking out this branch. You can also click here to expand the Support pane.

Verifying the Test

1. How many films has Francis Ford Copolla directed?

The last test within the test suite will log out the number of results returned from the API for films directed by Francis Ford Copolla.

Enter the number in the box below and click Check Answer.

  • ✓ 16

Hint

You can also find the answer by running the following Cypher statement:

cypher
MATCH (p:Person {tmdbId: "1776"})
RETURN count { (p)-[:DIRECTED]->() }

Copy the answer without any quotes or whitespace.

Lesson Summary

In this Challenge, you modified the functions in the MovieDAO to return lists of movies.

In the next Challenge, you will implement the find_by_id() method.