NoSQL Zone is brought to you in partnership with:

Romiko Derbynew is an independent consultant, focusing on integration solutions. Romiko is passionate about future technologies relating to Cloud Computing, Graph Databases, Messaging and Integration. Romiko spends his spare time in the performing arts, surfing, paragliding and finding new adventures. Romiko strives to contribute to the community, check out rangerrom.com if you have kids that love animals and get his latest book" Ranger Rom saves Rhino Reth". Romiko is a DZone MVB and is not an employee of DZone and has posted 18 posts at DZone. You can read more from them at their website. View Full User Profile

Get Reacquainted with the Neo4jClient

04.29.2012
| 5593 views |
  • submit to reddit

Neo4jClient is a .NET client for the Neo4j Rest Server built by a colleague of mine Tatham Oddie. Currently we have a Neo4jClient, which is the latest source code build. This can be found on NuGet with package name Neo4jClient- Neo4jClient is a .NET client for the Neo4j Rest Server.

NuGetPackage: http://nuget.org/List/Packages/Neo4jClient Source Code at: http://hg.readify.net/neo4jclient/

It leverages RestSharp and Newtonsoft.json for Rest and Json serialization/deserialization respectively.

The client also supports executing Gremlin queries in the following ways

  • Send raw gremlin statements that return scalar results
  • Send raw gremlin statements that return enumerable collection of nodes
  • Type safe lambda expressions

The raw version is provided to provide flexibility if you find certain lambda expressions are not supported, over time, more expressions will be added. We will be extending the client over time to certain more expressions as we find ourselves needing new expressions.

Lets have a look at some samples.

First you will need to install the package, so at the package console manager type:

install-package neo4jclient

Once this is installed, you can then of course load the IGraphClient in the entry point of your application, manually or using an IoC. The concept of the GraphClient is to provide basic CRUD operations.

The other powerful aspect is that the IGremlinQuery interface provide cool extensions into Gremlin so that a series of extensions methods will support out and in vertices and edges querying.

Below is a diagram illustrating the core concept.Basically we have a GraphClient, GremlinQuery and NodeReference. Notice that you can also query directly off a NodeReference. A NodeReference will represent a reference to a Vertice in the database. A Node will store the actual data, which is cast to a specific type with generics.

image

Entry Point

Here is sample code in loading the GraphClient using an IoC. It is this easy to get it started.

builder.Register<IGraphClient>(context =>
            {
                var resolver = context.Resolve<IRoleEndpointResolver>();
                var baseUri = resolver.GetNeo4JBaseUri();
                var graphClient = new GraphClient(baseUri);
                graphClient.Connect();
                return graphClient;
            })
            .SingleInstance();

Type Safe Lambda Expressions

Lets look at the cool features we can do. Below is a sample query we can run.

public Node<User> GetUser(User identifier)
        {
            if (identifier.IsSysAdmin)
            {
                return graphClient
                    .RootNode
                    .In<User>(Administers.TypeKey, u=> u.Username == identifier.Username)
                    .SingleOrDefault();
            }

            return graphClient
                .RootNode
                .Out<CustomerSite>(Hosts.TypeKey, a => a.Key == identifier.CustomerSiteKey)
                .In<User>(UserBelongsTo.TypeKey, u => u.Username == identifier.Username)
                .SingleOrDefault();
        }

You can even then run queries off a NodeReference, lets look at an example.

public int CreateUser(User user, NodeReference<Company> companyReference)
{
    return companyReference
                .InE(UserBelongsTo.TypeKey)
                .OutE<User>(u=>u.Username == user.Username)
}

You have the flexibility.

Creating Nodes and Relationships

You notice that in the above, we had a TypeKey representing the relationship, this is important, you can enforce very strict rules on your nodes, we can define a class that represents a relationship and enforce which source and target nodes or data models it is allowed to have e.g.

public class UserBelongsTo :
        Relationship,
        IRelationshipAllowingSourceNode<User>,
        IRelationshipAllowingTargetNode<CustomerSite>
    {
        public UserBelongsTo(NodeReference targetNode)
            : base(targetNode)
        {
        }

        public const string TypeKey = "USER_BELONGS_TO";
        public override string RelationshipTypeKey
        {
            get { return TypeKey; }
        }
    }

This means, that you get compile time safety, if you try and create a node in the database with a relationship. Lets look at a Create statement.

public NodeReference CreateUser(User user, NodeReference<CustomerSite> customerSite)
        {
            user.Username = user.Username.ToLowerInvariant();

            var node = graphClient.Create(
                user,
                new UserBelongsTo(customerSite));

            return node;
        }

Above, if you tried to swap the create, it would not work e.g.

public NodeReference CreateUser(User user, NodeReference<CustomerSite> customerSite)
        {
            user.Username = user.Username.ToLowerInvariant();
            var node = graphClient.Create(
                customerSite,
                new UserBelongsTo(user));

            return node;
        }

Updates

You can also update nodes, this is done by passing in a NodeReference and a delegate e.g.

public void UpdateUser(User user, Action<User> updateCallback)
{
            graphClient.Update(userNode.Reference, u =>
                {
                    updateCallback(u);
                });
}

Notice, you also get type safety here as well. The reference to the delegate/method with then get executed when neo4jClient persists the data.

Here is the sample updateCallback call to the above method. Notice I am in fact using the MVC updateModel facility to do the mappings for me, any how, you can update the object using your logic of course, there is no restrictions here. Here “this” refers to the MVC Controller class. It is just a nice way to get MVC to compare the user with the user from the DB and then merge the changes, no need to write logic to merge changes if using in the context of MVC, since it has a nice UpdateModel method that we can use. Othewise you would be using AutoMapper or something even nicer like ValueInjecter(http://valueinjecter.codeplex.com/).

userService.UpdateUser(user, u => this.UpdateModel(u,
                UpdateModel,
                tu => tu.GivenName,
                tu => tu.FamilyName,
                tu => tu.Username,
                tu => tu.EmailAddress,
                tu => tu.BusinessPhone,
                tu => tu.MobilePhone
            ));

The update above looks a bit tricky at first as we see a method pointer to a method pointer i.e. two action calls, in fact there are 4, one from the graph client, one from the service, one from the extended version of UpdateModel and then the updateModel itself.

The reason why I have a custom extension method for UpdateModel, is so we can Explicitly set which columns to update. Remember UpdateModel takes a callback and a list of properties to explicitly update, you can of course just call the default version if ALL fields need to be updated.

userService.UpdateUser(user, UpdateModel);

Below is the code for the extended UpdateModel.

public static class ControllerExtensions
    {
        public static void UpdateModel<TModel>(this Controller controller, TModel instanceToUpdate, Action<TModel, string[]> updateModelCallback, params Expression<Func<TModel, object>>[] includeProperties)
        {
            var propertyNames = GetPropertyNames(includeProperties);
            updateModelCallback(instanceToUpdate, propertyNames.ToArray());
        }

        public static IEnumerable<string> GetPropertyNames<T>(params Expression<Func<T, object>>[] action)
        {
            return action
                .Select(property =>
                {
                    var body = property.Body;

                    var unaryExpression = body as UnaryExpression;
                    if (unaryExpression != null) body = unaryExpression.Operand;

                    return (MemberExpression) body;
                })
                .Select(expression => expression.Member.Name);
        }
    }

The above extension method will now allow you to call the UpdateModel with type safety on the model fields to explicitly update. As i mentioned, if you need a simpler update to default to all fields then this call will work:

Deletes

You can also delete data. Notice the pattern here, get a node reference then run an operation.

graphClient.Delete(node.Reference,DeleteMode.NodeAndRelationships);

Relationships

You can also create relationships between existing nodes.

graphClient.CreateRelationship(customerNodeReference, new Speaks(languageNode.Reference));

Scalar Query Result – Raw

You might find that a complex lambda is not support, in which case you can execute a raw statement directly an still get type safety e.g.

var count = g.v(0).out('IsCustomer').Count()

Rest under the hood

The Client is smart enough to query the rest endpoint on the server and utilize the correct rest points. So the baseUri would be something like http://locahost:7474/db/data

The graphclient will do a Http get request with application/json to the above endpoint and will retrieve the following response.

{
  "index" : "http://localhost:7474/db/data/index",
  "node" : "http://localhost:7474/db/data/node",
  "reference_node" : "http://localhost:7474/db/data/node/0"
}

The above endpoints is the fundamental way to run rest queries, of course the graphclient does all the work for you, however it is always nice to know how it works.

Summary

So this is my little primer on the project we working with and it is fantastic working with someone like Tatham Oddie who built this brilliant tool for us to use.

We will be extending this tool as we go along and build new functionality as we need it. Not all Lambda expressions are supported yet and it is limited in tis regard but it is easy to extend going forward.

Download NuGetPackage: http://nuget.org/List/Packages/Neo4jClient Source Code at: http://hg.readify.net/neo4jclient/
Published at DZone with permission of Romiko Derbynew, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)