Sony Arouje

a programmer's log

Archive for February 10th, 2012

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 , ,

%d bloggers like this: