Search code examples
c#neo4jcypherneo4jclient

Writing Neo4J Cypher query in C#


I have an MVC Asp.Net application that is connected to a Neo4j database. In my database there is a relationship (m:Movie)<-[r:HAS_WATCHED_MOVIE]-(u:User) between movie and user entities.

What I want to do is return a IEnumerable<Movie> that contains top 3 movies (top movies are the movies with most HAS_WATCHED_MOVIE relationsips) in descending order.

I have already figured out a Cypher query to do this, and it goes like this:

MATCH (m:Movie)<-[r:HAS_WATCHED_MOVIE]-(b) 
RETURN m, COUNT(r) 
ORDER BY COUNT(r) DESC 
LIMIT 3

Since I'm new with Neo4j C# client, I'm not sure how to go about writing this query in c#?


Solution

  • Using Neo4jClient

    var client = new BoltGraphClient("bolt://localhost:7687", "neo4j", "neo");
    client.Connect();
    
    var query = client.Cypher
                      .Match("(m:Movie)<-[r:HAS_WATCHED_MOVIE]-(b)")
                      .Return((m,r) => new { 
                          Movie = m.As<Movie>(), 
                          Count = r.Count()
                       })
                      .OrderByDescending("Count")
                      .Limit(3);;
    
    foreach(var result in query.Results)
       Console.WriteLine($"'{result.Movie.Title}' had {result.Count} watchers");
    

    For this one, I have my Movie class defined as:

    public class Movie{
        [JsonProperty("title")]
        public string Title {get;set;}
    }
    

    Using Neo4j-Driver

    using (var driver = GraphDatabase.Driver("bolt://localhost:7687", AuthTokens.Basic("neo4j", "neo")))
    {
        using (var session = driver.Session())
        {
            using (var tx = session.BeginTransaction())
            {
                IStatementResult results = tx.Run(
                @"MATCH (m:Movie)<-[r:HAS_WATCHED_MOVIE]-(b) 
                  RETURN m, COUNT(r)
                  ORDER BY COUNT(r) DESC
                  LIMIT 3");
                foreach (IRecord result in results)
                {
                    var node = result["m"].As<INode>();
                    var title = node.Properties["title"]?.As<string>();
                    var count = result["COUNT(r)"].As<long>();
    
                    var movie = new Movie {
                          Title = title,
                    };
    
                    Console.WriteLine($"'{movie.Title}' had {count} watchers");
                }
            }
        }
    }
    

    NB. I've only done the new Movie bit in this one, as you say you wanted an IEnumerable<Movie> response.

    Thoughts

    The Neo4jClient one doesn't return an IEnumerable<Movie> as you're using the COUNT call in the RETURN you'd need to do something like:

    var query = gc.Cypher
            .Match("(m:Movie)<-[r:HAS_WATCHED_MOVIE]-(b)")
            .With("m, COUNT(r) AS count")
            .Return((m, r) => m.As<Movie>())
            .OrderByDescending("count")
            .Limit(3);
    

    Where you use a WITH to do the COUNT before returning. You don't have to do this with the Neo4j-Driver version as you can fake it after the fact, but I would still change the query if all you want is the Movie to:

    IStatementResult results = tx.Run(
                @"MATCH (m:Movie)<-[r:HAS_WATCHED_MOVIE]-(b) 
                  WITH m, COUNT(r) AS count
                  RETURN m
                  ORDER BY count DESC
                  LIMIT 3");