Sony Arouje

a programmer's log

Archive for February 2012

Persist Static typed Entities to Neo4j using Neo4jD

with 3 comments

At the core Neo4jD uses Node and Relationship to persist data to Neo4j graph database. If you want to see how the core is working then visit here. You might notice that, the Node and Relationship is not static typed objects. Most of us work with Static typed entities and want to persist the same. To deal with static typed entities I added a layer on top of Neo4jD core called NodeMapper.

Node Mapper
Below are the features of NodeMapper

  • Map the entity to Node object and persist in Neo4j.
  • Handles the relation between entities, if an entity has a sub entity then NodeMapper will create a relationship between them.
  • Persist the object graph using the Model created, will cover later in the page.
  • Inject interceptor for Lazy loading of related entities.
  • And more

How to Persist entities using NodeMapper
To persist the entities, first we need to draw the object graph of the Entity we need to persist. Let’s have a look at an eg.

public class Order
{
    public Order()
    {
        _orderItems = new List<OrderItem>();
    }

    [EntityId]
    public int Id { get; set; }
    public virtual string Name { get; set; }

    IList<OrderItem> _orderItems;
    public virtual IList<OrderItem> OrderItems 
    { 
        get { return _orderItems; }
        private set { _orderItems = value; }
    }
    public void AddOrderItem(OrderItem item)
    {
        this._orderItems.Add(item);
    }
}

public class OrderItem
{
    public OrderItem()
    {
    }
    public OrderItem(int id, Product product)
    {
        this.Id = id;
        this.Product = product;
    }
    [EntityId]
    public int Id { get; set; }

    public virtual Product Product { get; set; }
}

public class Product
{
    public Product()
    {

    }
    public Product(int id, string productName)
    {
        this.Id = id;
        this.ProductName = productName;
    }
    [EntityId]
    public int Id { get; set; }
    public string ProductName { get; set; }
}

You will notice some rules in defining entities.

  • The unique Id field is decorated with EntityId attribute. It’s mandatory to identify the Id field of the entity.
  • Properties that needs Lazy loading should be virtual.
  • Mandatory to have a default parameter less constructor.

Now let’s define the object graph. This configuration helps NodeMapper to persist the entire Graph. It’s very similar to how we do in Entity Framwork Code first approach.

public class OrderConfiguration:EntityConfiguration<Order>
{
    public OrderConfiguration()
    {
        this.RelatedTo<OrderItem>(o => o.OrderItems);
    }
}

public class OrderItemConfiguration:EntityConfiguration<OrderItem>
{
    public OrderItemConfiguration()
    {
        this.RelatedTo<Product>(oi => oi.Product);
    }
}

public class ProductConfiguration:EntityConfiguration<Product>
{
    public ProductConfiguration()
    {
        this.Leaf();
    }
}

As you can see in the ProductConfiguration, there is no related entity so we marked it as Leaf.

Let’s see how to persist an order

[SetUp]
public void Initialize()
{
    GraphEnvironment.SetBaseUri("http://localhost:7474/");

    ModelBuilder.Add(new OrderConfiguration());
    ModelBuilder.Add(new OrderItemConfiguration());
    ModelBuilder.Add(new ProductConfiguration());
}
[TestCase]
public void SaveOrder()
{
    Order order = new Order();
    order.Id = 0;
    order.Name = "Sony";
    order.AddOrderItem(new OrderItem(0, new Product(0, "Rice")));
    order.AddOrderItem(new OrderItem(0, new Product(0, "Sugar")));

    NodeMapper nodeMapper = new NodeMapper();
    nodeMapper.Save<Order>(order);
    Console.WriteLine(order.Id.ToString());
    Assert.AreEqual(1, order.Id);
}

In the Initialize method of NUnit Test we added EntityConfigurations to ModelBuilder. NodeMapper uses the ModelBuilder to understand the Object graph and uses reflection to traverse through the object graph and persist it.

Lazy Loading

Neo4jD uses Lazy loading to load the related entities, it uses Castle DynamicProxy to intercept property calls and inject lazy loading functionality. To perform lazy loading the property should be virtual, you can see the OrderItems in Order entity.

Retrieve saved Entity

[TestCase]
public void GetOrder()
{
    NodeMapper nodeMapper = new NodeMapper();
    Order order = nodeMapper.Get<Order>(14);
    Assert.AreEqual(14, order.Id);
    foreach (OrderItem item in order.OrderItems)
    {
        Console.WriteLine(item.Id.ToString());
        Product prod = item.Product;
        if (prod != null)
            Console.WriteLine(prod.ProductName);
    }
    Assert.AreEqual(2, order.OrderItems.Count);
}

Visit Github for source code

Written by Sony Arouje

February 21, 2012 at 6:46 pm

Posted in .NET

Tagged with , , , ,

Neo4jD–.NET client for Neo4j Graph Database Part 3

with 8 comments

This post talks about Index and Graph traversal functionalities added to Neo4jD.

Neo4j supports Cypher, Germlin and REST based api’s for traversal. As of now Neo4jD can creates query for Germlin and REST based traversal. Traversal is not fully implemented in Neo4jD, it’s still in progress.

How to Create Index

Creating index in Neo4j using Neo4jD is very simple as shown below

[TestCase]
public void Create_Index()
{
    Index test = Index.Create("TestIndex");
    Index fav = Index.Create("Favorites");
}

Indexing nodes with key/value pair helps to search it faster. Let’s see how to add nodes to the Favorites index.

[TestCase]
public void AddNodeToFavorites()
{
    Index fav = Index.Get("Favorites");
    Node node = Node.Get(1);
    fav.Add(node, "FirstName", "Sony");

    Node node1 = Node.Get(2);
    fav.Add(node1, "FirstName", "Viji");
}

We added two nodes to the index Favorites, as I said using index we can easily retrieve Nodes using the Key/Value pair. Let’s see how we can query the index using Neo4jD.

[TestCase]
public void Search_Index()
{
    Index fav = Index.Get("Favorites");
    IndexQuery qry = new IndexQuery();
    qry.GetKey("FirstName").StartsWith("Vi").OR().GetKey("FirstName").Equals("Sony");
    IList<Node> nodes= fav.Search(qry);
    Assert.AreEqual(2, nodes.Count);
}

 

You decided to remove a Node from the Favorites index, it’s a very simple call as shown below

[TestCase]
public void Remove_Node_FromIndex()
{
    Index fav = Index.Get("Favorites");
    Node node = Node.Get(1);
    fav.RemoveNode(node);
}

I removed Node Sony from the index.

That’s all about Index, let’s go through Germlin and REST Traversal

Germlin Traversal

Germlin is a groovy based Graph traversal language, Neo4j has a Germlin plugin to send Germlin script to Neo4j server.

Let’s see how to create a Germlin query using Neo4jD.

[TestCase]
public void Get_Out_Nodes()
{
    GermlinPipe germlinQuery = new GermlinPipe();
    germlinQuery.G.V.Out("son");
    Node father = Node.Get(1);
    IList<Node> nodes = father.Filter(germlinQuery);
    Assert.AreEqual(1, nodes.Count);
}

For more Germlin query you can reffer Neo4j API reference.

REST Traversal

For Rest traversal we need to provide Json structured query to the server as shown below. For more details goto Neo4j REST API reference.

{
  “order” : “breadth_first”,
  “return_filter” : {
    “body” : “position.endNode().getProperty(‘name’).toLowerCase().contains(‘t’)”,
    “language” : “javascript”
  },
  “prune_evaluator” : {
    “body” : “position.length() > 10”,
    “language” : “javascript”
  },
  “uniqueness” : “node_global”,
  “relationships” : [ {
    “direction” : “all”,
    “type” : “knows”
  }, {
    “direction” : “all”,
    “type” : “loves”
  } ],
  “max_depth” : 3
}

To create the syntax Neo4jD uses a fluent API as shown below.

[TestCase]
public void REST_Traversal_Test()
{
    Node node = Node.Get(19);
    Assert.IsNotNull(node);
    RestTraversal r = new RestTraversal();
    r.Order(OrderType.breadth_first)
        .Filter(new PropertyFilter().SetPropertyName("FirstName").Contains("Viji"))
        .RelationShips(RelationshipDirection.out_direction, "wife")
        .RelationShips(RelationshipDirection.all_direction, "family")
        .Uniqueness(UniquenessType.node_global)
        .MaxDepth(2);
    IList<Node> nodes = node.Filter(r);
    Assert.AreEqual (1, nodes.Count);
}
 

Neo4jD Source Code

Written by Sony Arouje

February 10, 2012 at 1:12 am

Posted in .NET

Tagged with , , , ,

Neo4jD–.NET client for Neo4j Graph Database Part 2

with one comment

This post talks about some of the new functionalities added to Neo4jD in recent days.

Persist Static typed object

In the previous post I explained how to create Node and Relationship using Neo4jD. As you might noticed that Node’s properties are dynamic, for e.g to set FirstName we say ‘node.SetProperty(“FirstName”,”Sony”)’. Just like me most of us are not comfortable with Dynamic properties, that’s why we create static typed entities. This post deals with how to persist static typed entities to Neo4j.

Note: I am not against Dynamics, it’s just a personal preference not to build around Dynamics.

Let’s talk with some examples.

public class Person
{
    public Person()
    {
        this.Address = new Address();
    }
    [EntityId]
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }

    public Address Address { get; set; }
}

public class Address
{
    [EntityId]
    public int AddressId { get; set; }
    public string Address1 { get; set; }
    public string City { get; set; }
}

 

So I have two classes to hold my data. Let’s see how to persist the object using Neo4jD. The core of Neo4jD is based on Node and Relationship with dynamic properties. On top of the core I added a mapper to do the mapping of Static typed entities to Nodes or relationship. I am not going to much insights of mapper, will just go through some e.g., if you need more insight better go through the Neo4jD in github.

How to Save object 

[TestCase]
public Person SaveTest()
{
    Person person = new Person { FirstName = "Sony", LastName= "Arouje"};
    NodeMapper mapper = new NodeMapper();
    person=mapper.Save<Person>(person);
    Console.WriteLine("Generated Id: " + person.Id.ToString());
    Assert.AreNotEqual(0, person.Id);
    return person;
}

Behind the scenes the NodeMapper uses reflection to get the data from the Person object and create Node object. Let’s see how I am doing it.

public T Save<T>(T entity) where T:class
{
    Node node = this.CreateNode<T>(entity);
    node.Create();
    return MapperHelper.SetIdentity<T>(entity, node.Id);
}

private Node CreateNode<T>(T entity) where T:class
{
    Node node = new Node();
    node.AddProperty("clazz", typeof(T).ToString());
    typeof(T).GetProperties().Where(pr => pr.CanRead && MapperHelper.IsAnId(pr) == false)
    .ToList().ForEach(property =>
    {
        if(MapperHelper.IsPrimitive(property.PropertyType))
            node.AddProperty(property.Name, property.GetValue(entity, null).ToString());
    });

    return node;
}

 

CreateNode function does the mapping. It Iterates through the property and call node.AddProperty(property.Name, property.GetValue()), it’s same as node.AddProperty(“FirstName”,”Sony”).

You might have noticed that the Id field in the class is decorated with ‘EntityId’ attribute. It’s a mandatory attribute to Get or Set the node id to Entity’s id field.

Get Instance from Neo4J

[TestCase]
public void GetPersonById()
{
    NodeMapper mapper = new NodeMapper();
    Person person = mapper.Get<Person>(7);
    Console.WriteLine("Generated Id: " + person.Id.ToString());
    Assert.AreNotEqual(0, person.Id);
    Assert.AreEqual(“Sony”, person.FirstName);
}
 

To get the Person I called mapper.Get with unique id of the person and the mapper will return a valid person if it exist in db. NodeMapper uses below function to generate the object.

public T Get<T>(int id) where T:class
{
    Node node = Node.Get(id);
    T entity = (T)Activator.CreateInstance(typeof(T));
    if (node.GetProperty("clazz")!=typeof(T).ToString())
        throw new InvalidCastException(string.Format("Retrieved object with ID '{0}' is 
          an instance of '{1}' and unable to cast it to '{2}'", id.ToString(), 
          node.GetProperty("clazz"), typeof(T).ToString()));
    typeof(T).GetProperties().Where(pr => pr.CanRead && MapperHelper.IsAnId(pr) == false)
    .ToList().ForEach(property =>
    {
        property.SetValue(entity, MapperHelper.CastPropertyValue(property, 
        node.GetProperty(property.Name)), null);
    });

    entity = MapperHelper.SetIdentity<T>(entity, id);
    return entity;
}

Create Relationships

You can see that Person has an instance of Address, in Graph Person is related to Address. Let’s see how we can create that

[TestCase]
public void CanCreateRelationships()
{
    Person person = new Person { FirsName = "Sony", LastName = "Arouje" };
    person.Address.Address1 = "EcoSpace";
    person.Address.City = "Bangalore";
    NodeMapper mapper = new NodeMapper();
    mapper.CreateRelationshipTo<Person, Address>(person,person.Address);
    Console.WriteLine(person.Id.ToString());
    Assert.AreEqual(1, person.Id); 
}

CreateRelationShipTo function will Save both Person and Address if it’s not saved other wise it’s create a relationship. In Neo4j the relationship will be person->address.

Get Person with Relationship

[TestCase]
public void CanGetRelatedNodes()
{
    NodeMapper mapper = new NodeMapper();
    Person person = mapper.Get<Person>(12);
    IList<Address> address = mapper.GetRelatedEntities<Person, Address>(person, typeof(Address));
    Assert.AreEqual(1, address.Count);
    Assert.AreEqual("EcoSpace", address[0].Address1);
}

 

I know the persisting and retrieving relationships are bit messy, I will work on it to make it more clear and silent.

That’s all about persisting Static typed objects to Neo4j.

In the next post I will talk about Creating Index, Traversal using Germlin and REST.

Neo4jD source code

Written by Sony Arouje

February 10, 2012 at 12:15 am

Posted in .NET

Tagged with , ,

Neo4jD–.NET client for Neo4j Graph DB

with 18 comments

Last couple of days I was working on a small light weight .NET client for Neo4j. The client framework is still in progress. This post gives some existing Api’s in Neo4jD to perform basic graph operations. In Neo4j two main entities are Nodes and Relationships. So my initial focus for the client library is to deal with Node and Relationship. The communication between client and Neo4j server is in REST Api’s and the response from the server is in json format.

Let’s go through some of the Neo4j REST Api’s and the equivalent api’s in Neo4jD, you can see more details of Neo4j RestAPi’s here.

The below table will show how to call Neo4j REST Api directly from an application and the right hand will show how to do the same operation using Neo4jD client.

Neo4j Api Equivalent Neo4jD Api
Create Node

POST http://localhost:7474/db/data/node
Accept: application/json
Content-Type: application/json
{"FirstName" : "Sony"}

Response (click here to see full response)

{
  "outgoing_relationships" : "
http://localhost:7474/db/data/node/……..,
  "data" : {
    "foo" : "bar"
  },…..

}

Node sony=new Node();
sony.AddProperty(“FirstName”,”Sony”);
sony.Create();

Get Node by ID
GET http://localhost:7474/db/data/node/3

Response (click here to see full response)

{
  "outgoing_relationships" :
http://localhost:7474/db/……….,
  "data" : {
    "FirstName" : "Sony"
  },
  "traverse" :
http://localhost:7474/db/data. . .{returnType},
..........
}

Node sony=Node.Get(1);
Assert.AreEqual(“Sony”, sony.GetProperty(“FirstName”);

The Neo4jD will create a Rest request to fetch the Node with Id 1 and parse the json response and set the properties and required field.

Create Relationship between two Nodes

POST http://localhost:7474/db/data/node/1/relationships
Accept: application/json
Content-Type: application/json
{"to" :
http://localhost:7474/db/data/node/2, "type" : "wife"}

Response

(click here to see full response)

Node sony=Node.Get(1); //Get an existing node
//Create a new node
Node viji=new Node();
viji.AddProperty(“FirstName”,”Viji”);
viji.Create();

RelationShip relation=sony.CreateRelationshipTo(viji, “wife”);

 

Client lib is still missing some basic features and I am working on it.

The next important part of the library is Graph traversing. Neo4j supports REST api, Cypher and Germlin for Graph traversing. I am currently writing a query creator for Germlin. So the initial version of Neo4jD will only support Germlin.

If any one wants to join the project feels free to send me a personal mail or add a comment. You all can have a look at the code in Git.

Neo4jD Part 2  Neo4jD Part 3  How to Persist Static typed Entities

 

Neo4jD Git Repository

Written by Sony Arouje

February 3, 2012 at 2:28 pm

Posted in .NET

Tagged with , , ,

%d bloggers like this: