In this challenge, you will implement the remaining methods in the MovieService
for retrieving a list of movies:
-
GetByGenreAsync - should return a paginated list of movies that are listed in a particular Genre
-
getForActorAsync - should return a paginated list of movies that a particular Person has acted in
-
getForDirectorAsync - should return a paginated list of movies that a particular Person has directed
These methods are very similar to the AllAsync()
method which returns a full list, but with a different pattern in the MATCH
clause.
$skip
and $limit
from the request as parameters and string-replace the field to order for sort
and order
direction (as those cannot be parameterized). public async Task<Dictionary<string, object>[]> AllAsync(string sort = "title",
Ordering order = Ordering.Asc, int limit = 6, int skip = 0, string userId = null)
{
// Open a new session.
await using var session = _driver.AsyncSession();
// Execute a query in a new Read Transaction.
return await session.ExecuteReadAsync(async tx =>
{
// Get an array of IDs for the User's favorite movies
var favorites = await GetUserFavoritesAsync(tx, userId);
var cursor = await tx.RunAsync(@$"
MATCH (m:Movie)
WHERE m.{sort} IS NOT NULL
RETURN m {{
.*,
favorite: m.tmdbId IN $favorites
}} AS movie
ORDER BY m.{sort} {order.ToString("G").ToUpper()}
SKIP $skip
LIMIT $limit", new {skip, limit, favorites});
var records = await cursor.ToListAsync();
var movies = records
.Select(x => x["movie"].As<Dictionary<string, object>>())
.ToArray();
return movies;
});
}
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 AllAsync()
method and modify the pattern in the first line of the Cypher statement in the first argument of the tx.RunAsync()
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.
GetByGenreAsync
The GetByGenreAsync()
method returns a paginated list of movies that are listed within a certain genre.
public async Task<Dictionary<string, object>[]> GetByGenreAsync(string name, string sort = "title",
Ordering order = Ordering.Asc, int limit = 6, int skip = 0, string userId = null)
{
// TODO: Get Movies in a Genre
// MATCH (m:Movie)-[:IN_GENRE]->(:Genre {name: $name})
return await Task.FromResult(Fixtures.Popular.Skip(skip).Take(limit).ToArray());
}
Update the GetByGenreAsync()
method to use the following Cypher Pattern:
MATCH (m:Movie)-[:IN_GENRE]->(:Genre {name: $name})
The tx.RunAsync()
call will need to include the name
parameter, which represents the name of the genre.
Click to reveal the completed GetByGenreAsync()
method
Find the GetByGenreAsync()
method in Neoflix/Services/MovieService.cs
and modify the function to find a list of movies by genre.
public async Task<Dictionary<string, object>[]> GetByGenreAsync(string name, string sort = "title",
Ordering order = Ordering.Asc, int limit = 6, int skip = 0, string userId = null)
{
await using var session = _driver.AsyncSession();
var records = await session.ExecuteReadAsync(async tx =>
{
var favorites = await GetUserFavoritesAsync(tx, userId);
var query = $@"
MATCH (m:Movie)-[:IN_GENRE]->(:Genre {{name: $name}})
WHERE m.{sort} IS NOT NULL
RETURN m {{
.*,
favorite: m.tmdbId IN $favorites
}} AS movie
ORDER BY m.{sort} {order.ToString("G").ToUpper()}
SKIP $skip
LIMIT $limit";
var cursor = await tx.RunAsync(query, new {skip, limit, favorites, name});
return await cursor.ToListAsync();
});
return records
.Select(x => x["movie"].As<Dictionary<string, object>>())
.ToArray();
}
getForActorAsync
The GetForActorAsync()
method returns a paginated list of movies that a person has acted in.
public async Task<Dictionary<string, object>[]> GetForActorAsync(string id, string sort = "title",
Ordering order = Ordering.Asc, int limit = 6, int skip = 0, string userId = null)
{
// TODO: Get Movies acted in by a Person
// MATCH (:Person {tmdbId: $id})-[:ACTED_IN]->(m:Movie)
return await Task.FromResult(Fixtures.Roles.Skip(skip).Take(limit).ToArray());
}
Find the GetForActorAsync()
method in Neoflix/Services/MovieService.cs
and modify the function to find a list of movies by actor.
The pattern required in the MATCH
clause will be:
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 GetForActorAsync()
method
public async Task<Dictionary<string, object>[]> GetForActorAsync(string id, string sort = "title",
Ordering order = Ordering.Asc, int limit = 6, int skip = 0, string userId = null)
{
await using var session = _driver.AsyncSession();
var records = await session.ExecuteReadAsync(async tx =>
{
var favorites = await GetUserFavoritesAsync(tx, userId);
var query = $@"
MATCH (:Person {{tmdbId: $id}})-[:ACTED_IN]->(m:Movie)
WHERE m.{sort} IS NOT NULL
RETURN m {{
.*,
favorite: m.tmdbId IN $favorites
}} AS movie
ORDER BY m.{sort} {order.ToString("G").ToUpper()}
SKIP $skip
LIMIT $limit";
var cursor = await tx.RunAsync(query, new { skip, limit, favorites, id });
return await cursor.ToListAsync();
});
return records
.Select(x => x["movie"].As<Dictionary<string, object>>())
.ToArray();
}
getForDirectorAsync
The GetForDirectorAsync()
method returns a paginated list of movies that a person has directed.
public async Task<Dictionary<string, object>[]> GetForDirectorAsync(string id, string sort = "title",
Ordering order = Ordering.Asc, int limit = 6, int skip = 0, string userId = null)
{
// TODO: Get Movies directed by a Person
// MATCH (:Person {tmdbId: $id})-[:DIRECTED]->(m:Movie)
return await Task.FromResult(Fixtures.Popular.Skip(skip).Take(limit).ToArray());
}
Find the GetForDirectorAsync()
method in Neoflix/Services/MovieService.cs
and modify the function to find a list of movies by director.
The pattern required in the MATCH
clause will be:
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 GetForDirectorAsync()
method
public async Task<Dictionary<string, object>[]> GetForDirectorAsync(string id, string sort = "title",
Ordering order = Ordering.Asc, int limit = 6, int skip = 0, string userId = null)
{
await using var session = _driver.AsyncSession();
var records = await session.ExecuteReadAsync(async tx =>
{
var favorites = await GetUserFavoritesAsync(tx, userId);
var query = $@"
MATCH (:Person {{tmdbId: $id}})-[:DIRECTED]->(m:Movie)
WHERE m.{sort} IS NOT NULL
RETURN m {{
.*,
favorite: m.tmdbId IN $favorites
}} AS movie
ORDER BY m.{sort} {order.ToString("G").ToUpper()}
SKIP $skip
LIMIT $limit";
var cursor = await tx.RunAsync(query, new { skip, limit, favorites, id });
return await cursor.ToListAsync();
});
return records
.Select(x => x["movie"].As<Dictionary<string, object>>())
.ToArray();
}
Testing
To test that this functionality has been correctly implemented, run the following code in a new terminal session:
dotnet test --logger "console;verbosity=detailed" --filter "Neoflix.Challenges._11_MovieLists"
The test file is located at Neoflix.Challenges/11-MovieLists.cs
.
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:
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?
As part of the test suite, the test titled should find films directed by Francis Ford Coppola
logs 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:
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 MovieService
to return lists of movies.
In the next Challenge, we will implement the FindByIdAsync()
method.