When the user clicks a genre in the list, they are taken to a list of movies for that genre. This list is populated by an API request to /api/genres/[name]
, for example http://localhost:3000/api/genres/Comedy.
The FindGenreAsync()
method the the GenreService
accepts one argument, the name of the genre, and should return the information about the genre, along with a count of movies and a poster image.
public Task<Dictionary<string, object>> FindGenreAsync(string name)
{
// TODO: Open a new session
// TODO: Get Genre information from the database
// TODO: return null if the genre is not found
// TODO: Close the session
return Task.FromResult(
Fixtures
.Genres
.OfType<Dictionary<string, object>>()
.First(x => x["name"] == name));
}
In this challenge, you will modify the method to run the following Cypher statement in a read transaction:
MATCH (g:Genre {name: $name})<-[:IN_GENRE]-(m:Movie)
WHERE m.imdbRating IS NOT NULL AND m.poster IS NOT NULL AND g.name <> '(no genres listed)'
WITH g, m
ORDER BY m.imdbRating DESC
WITH g, head(collect(m)) AS movie
RETURN g {
.name,
movies: count { (g)<-[:IN_GENRE]-() },
poster: movie.poster
} AS genre
What does this query do?
This query uses the MATCH
clause for a Genre
node with the name passed through with the function call as a parameter.
The query then finds the highest rated movie with a poster
property and uses that image as the background to the card in the UI.
The size()
function returns the number of incoming IN_GENRE
relationships for each the Genre
node.
Your Task
-
Modify the
FindGenreAsync()
method on theGenreService
to call the Neo4j database and return details for a genre. -
The
name
variable should be passed to therun()
call as a parameter. -
If no records or more than one record are found, a
NotFoundError
should be thrown by using thesingle()
method onResult
. -
Remember to close the session and use the
ToListAsync()
function to convert the genre values to native C# types.
Working Solution
Click here to view the completed FindGenreAsync()
method.
public async Task<Dictionary<string, object>> FindGenreAsync(string name)
{
await using var session = _driver.AsyncSession();
return await session.ExecuteReadAsync(async tx =>
{
var query = @"
MATCH (g:Genre {name: $name})<-[:IN_GENRE]-(m:Movie)
WHERE m.imdbRating IS NOT NULL
AND m.poster IS NOT NULL
AND g.name <> '(no genres listed)'
WITH g, m
ORDER BY m.imdbRating DESC
WITH g, head(collect(m)) AS movie
RETURN g {
link: '/genres/' + g.name,
.name,
movies: count { (g)<-[:IN_GENRE]-() },
poster: movie.poster
} AS genre";
var cursor = await tx.RunAsync(query, new { name });
if (!await cursor.FetchAsync())
{
throw new NotFoundException($"Could not find a genre with the name '{name}'.");
}
return cursor.Current["genre"].As<Dictionary<string, object>>();
});
}
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._10_GenreDetails"
The test file is located at Neoflix.Challenges/10-GenreDetails.cs
.
Are you stuck? Click here for help
If you get stuck, you can see a working solution by checking out the 10-genre-details
branch by running:
git checkout 10-genre-details
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
How many movies are in the Action genre?
After the test has succeeded, the test suite will log the count of movies in the Action genre to the console.
Enter the number of movies below and click Check Answer.
-
✓ 1545
Hint
You can also find the answer by running the following Cypher statement:
MATCH (g:Genre {name: "Action"})
RETURN count { (g)<-[:IN_GENRE]-() }
Lesson Summary
In this Challenge, you modified the FindGenreAsync()
method in the GenreService
to retrieve genre information from Neo4j.
In the next Challenge, you will update multiple methods in the MovieService
that return a paginated list of movies.