Moviegraph is a demo and training application for ASP.NET Core and Neo4j.
wget http://dist.neo4j.org/neo4j-community-3.4.7-unix.tar.gz
tar xf neo4j-community-3.4.7-unix.tar.gz
cd neo4j-community-3.4.7
bin/neo4j-admin set-initial-password password
bin/neo4j start|console
Browse to
http://localhost:7474/
:play movies
call db.schema
(explore a bit)
git clone ...
export ASPNETCORE_ENVIRONMENT=Development
cd MovieGraph.Web && dotnet run
If you want to use a different language, feel free to convert the code.
http://127.0.0.1:5000
Answers/- all the answers!!MovieGraph.sln- the solution file that includes the projectMovieGraph.WebMovieGraph.Web/- the project itselfMovieGraph.Web/Controllers/- the MVC controllersMovieGraph.Web/Views/- the MVC views (razor)MovieGraph.Web/Model/- the MVC modelsMovieGraph.Web/Helpers/- couple of extension methods for Neo4jMovieGraph.Web/wwwroot/- the static files (css)
The driver is added as a service - Startup.cs
// Add Neo4j Driver As A Singleton Service
services.AddSingleton<IDriver>(provider => GraphDatabase.Driver("bolt://localhost:7687", AuthTokens.Basic("neo4j", "password")));MVC controllers accept IDriver in their constructors, ASP.NET MVC passes our already registered singleton instance.
private readonly IDriver driver;
public HomeController(IDriver driver)
{
this.driver = driver;
}You can have a look at the home page view templates at Views/Home.
[Route("")]
public async Task<IActionResult> Index(string q)
{
return View("Index", await MatchMovies(q));
}private async Task<IEnumerable<MovieModel>> MatchMovies(string term)
{
if (string.IsNullOrEmpty(term))
{
return Enumerable.Empty<MovieModel>();
}
var session = driver.Session();
try
{
return await session.ReadTransactionAsync(async tx =>
{
var cursor =
await tx.RunAsync(
"MATCH (movie:Movie) WHERE toLower(movie.title) " +
"CONTAINS toLower($term) RETURN movie",
new {term});
return await cursor.ToListAsync(record => new MovieModel(record));
});
}
finally
{
if (session != null)
{
await session.CloseAsync();
}
}
}You can have a look at the home page view templates at Views/Movie.
[Route("{id}")]
public async Task<IActionResult> Index(string id)
{
var movie = await MatchMovie(id);
if (movie == null)
{
return StatusCode(404);
}
return View("Index", movie);
}private async Task<MovieModel> MatchMovie(string title)
{
var session = driver.Session();
try
{
return await session.ReadTransactionAsync(async tx =>
{
var cursor =
await tx.RunAsync(
"MATCH (movie:Movie) WHERE movie.title = $title " +
"OPTIONAL MATCH(person) -[:ACTED_IN]->(movie) " +
"RETURN movie, collect(person) AS actors",
new {title});
return new MovieModel(await cursor.SingleAsync());
});
}
finally
{
if (session != null)
{
await session.CloseAsync();
}
}
}GetOrDefault functions used are defined as extension methods by the static classes in Helpers folder.
public class MovieModel
{
public const string MovieKey = "movie";
public const string TitleKey = "title";
public const string TaglineKey = "tagline";
public const string ReleasedKey = "released";
public const string ActorsKey = "actors";
public MovieModel(IRecord record)
{
var movie = record.GetOrDefault(MovieKey, (INode) null);
if (movie != null)
{
Title = movie.GetOrDefault<string>(TitleKey, null);
Tagline = movie.GetOrDefault<string>(TaglineKey, null);
Released = movie.GetOrDefault<int?>(ReleasedKey, null);
}
var actors = record.GetOrDefault(ActorsKey, (List<object>) null);
if (actors != null)
{
Actors = actors.Select(actor => new PersonModel((INode) actor)).OrderBy(p => p.Name);
}
}
public string Title { get; }
public string Tagline { get; }
public int? Released { get; }
public IEnumerable<PersonModel> Actors { get; }
}GetOrDefault functions used are defined as extension methods by the static classes in Helpers folder.
public class PersonModel
{
public const string NameKey = "name";
public PersonModel(INode node)
{
if (node == null)
{
throw new ArgumentNullException(nameof(node));
}
Name = node.GetOrDefault<string>(NameKey, null);
}
public string Name { get; }
}- Add links behind the movie cast list (movies are rendered by templates in
Views/Movie) - Add a new
Personcontroller (inControllers) - Modify
PersonModelto include year of birth and all films played in (inModel) - Add a new view template for
Person(save new view file(s) inViews/Person)
cd Answers/1/MovieGraph.Web && dotnet run- Add clickable star rating to each movie (movies are rendered by templates in
Views/Movie) - Add handler for POST requests to
/movie/<title>(inControllers/MovieController) - Modify
MovieModelto include stored star rating (inModel)
cd Answers/2/MovieGraph.Web && dotnet run- Add stars to search results (search results are rendered by templates in
Views/Home) - Add order by box (search box is rendered in
Views/Shared/_Layout.cshtml)
cd Answers/3/MovieGraph.Web && dotnet run