Neo4jClient new Cypher Start notation & F# Extra Top Level Operators

Some weeks ago a new version of Neo4jClient was released. There were introduced some changes in the Cypher Start notation. The new Start notation for sure has become nicer for C#, but it is still a bit ugly for F#. Recommended way of using new Start notation are anonymous classes that do not supported in F#.

The one reasonable option is to use dictionary-like interface, which in C# looks in the following way:

graphClient
    .Cypher
    .Start(new Dictionary<string, object>
    {
        { "foo", nodeRef },
        { "bar", otherNodeRef }
    });

We need to create IDictionary<string,obj> object somehow. Luckily, F# has a set of Extra Top Level Operators one of which is dict that does exactly what we need. The last thing what we need to do is to box dictionary values to convert them to obj.

dict : seq<'Key * 'Value> -> IDictionary<'Key,'Value> (requires equality)
box : 'T -> obj

Some examples:

let getById (queryObject:'T when 'T :> NeoEntity) =
    client.Cypher
        .Start(dict ["n", box(sprintf "node(%d)" (queryObject.Id))])
        .Return<Node<'T>>("n")
        .Results

let simpleConnection connectionType (target:Node<_>) (source:Node<_>) =
    client.Cypher
        .Start(dict [("n", box(source.Reference)); ("m", box(target.Reference))])
        .CreateUnique(sprintf "n-[:%s]->m" connectionType)
        .ExecuteWithoutResults()

Update: Start notation supports wide range of possible object references:

graphClient
  .Cypher
  .Start(dict [
    ("n1", box "custom");
    ("n2", box nodeRef);
    ("n3", box Node.ByIndexLookup("indexName", "property", "value"));
    ("n4", box Node.ByIndexQuery("indexName", "query"));
    ("r1", box relRef);
    ("moreRels", box [|relRef; relRef2|]);
    ("r2", box Relationship.ByIndexLookup("indexName", "property", "value"));
    ("r3", box Relationship.ByIndexQuery("indexName", "query"));
    ("all", box All.Nodes)
  ])

My first disappointment on F# type system.

Today I found that there are examples of code that correct for C# and could not be compiled in F#. I was very surprised and upset.

I continued playing with Neo4jClient and tried to implement more complex model. I have found that it  is not possible to define F# type for cross entity relationship. To make such relationship I need to define type that implements two interfaces, like this:

type FollowRelationship(target) =
    inherit Relationship(target)
    interface IRelationshipAllowingSourceNode<Person>
    interface IRelationshipAllowingTargetNode<Company>

    override this.RelationshipTypeKey
        with get() = "follow"

But F# compiler does not allow the creation of such type. I’ve got the following compilation error:

This type implements or inherits the same interface at different generic instantiations ‘IRelationshipAllowingParticipantNode’ and ‘IRelationshipAllowingParticipantNode’. This is not permitted in this version of F#.

It happens because IRelationshipAllowingSourceNode and IRelationshipAllowingTargetNode inherited from a single generic interface IRelationshipAllowingParticipantNode and F# does not allow to implement the same interface in different generic instantiations.

Here is an implementation of these interfaces from Neo4jClient source code.

public interface IRelationshipAllowingParticipantNode<out TNode>
{
}
public interface IRelationshipAllowingSourceNode<out TNode>
    : IRelationshipAllowingParticipantNode<TNode>
{
}
public interface IRelationshipAllowingTargetNode<out TNode>
    : IRelationshipAllowingParticipantNode<TNode>
{
}

As I found, there is actually no way to do it in F#. An only option is to write such types in C#. We have a similar question about this on StackOverflow: “Implementing the same interface at different generic instantiations“.

May be it is not a real constrain of F#, but it adds a noise to C#/F# integration. It is means that not all C# design patterns are integrable with F#.

It can be one more answer to

Using Neo4j Graph DB With F#

Neo4j_logo

Today, I have found Neo4j and could do nothing but play with it. It looks extremely attractive(for example, for enterprise social data).

Neo4j is an open-source, high-performance, enterprise-grade NOSQL graph database.

Neo4j stores data in nodes connected by directed, typed relationships with properties on both, also known as a Property Graph.

First good news, Neo4j has REST API and .NET client library that is available on NuGet. Let’s download it and reference from our script.

#r "System.Net.Http.dll"
#r "System.Net.Http.WebRequest.dll"
#r "Neo4jClient.dll"
#r "Newtonsoft.Json.dll"

open System
open Neo4jClient
open System.Linq

Than, we model a twitter data with following and friendship relationships. Let’s define a Person entity, we need to do nothing more than define a new .NET type.

[<CLIMutable>]
type Person = { Name:string; Twitter:string }

We also need to define relationships. Let’s start from following relationship that is simple and does not store any extra data. To define such relationship we need to write new type that inherits Relationship then implement two interfaces which constrain types of source and target entities that can be connected by this relationship. The last step is to define RelationshipTypeKey property that helps us to identify this relationship in graph.

type FollowRelationship(target) =
    inherit Relationship(target)
    interface IRelationshipAllowingSourceNode<Person>
    interface IRelationshipAllowingTargetNode<Person>

    override this.RelationshipTypeKey
        with get() = "follows"

We also can define relationship that store any extra data. To do so, we need to create a new class for metadata and pass it into Relationship constructor.

[<CLIMutable>]
type KnowsData = { Details:string }

type KnowsRelationship(target, data) =
    inherit Relationship(target, data)
    interface IRelationshipAllowingSourceNode<Person>
    interface IRelationshipAllowingTargetNode<Person>

    override this.RelationshipTypeKey
        with get() = "knows"

So, we are ready to build a graph. Let’s setup a connection to DB and create data.

let client = new GraphClient(new Uri("http://localhost:7474/db/data"));
client.Connect();

let createPerson person =
    client.Create person

let pA = createPerson { Name = "Person A"; Twitter="tA"}
let pB = createPerson { Name = "Person B"; Twitter="tB"}
let pC = createPerson { Name = "Person C"; Twitter="tC"}
let pD = createPerson { Name = "Person D"; Twitter="tD"}

let follows target source =
    client.CreateRelationship(source, FollowRelationship target)

pB |> follows pA
pC |> follows pA
pD |> follows pB
pD |> follows pC

let knows target details source =
  client.CreateRelationship(source,KnowsRelationship(target,{Details=details}))

pB |> knows pC "colleagues"

We have built the graph. It’s time to make a query to search something. Neo4j supports a special graph oriented query language – Cypher that looks readable even for analysts, not only for programmers. Neo4jClient has a LINQ-like query builder for Cypher.

let pAfollowers =
    client.Cypher
        .Start("n", pA)
        .Match("n<-[r:follows]-e")
        .Return<Person>("e")
        .Results
        .Select(fun x -> x.Name)

If you execute this query, you will see a list of followers of “Person A“. It is [“Person B”; “Person C”].

Neo4j has a cool web UI, where you can not only maintain server, but browse, edit and query your data.

Let’s open Data Browser tab, click on the Layout button and add a new layout. Here we can define rules for how we want to show entities based on their internal properties values. Please fill the form according to the picture below.

Neo4j_Layout

After that save the layout, open Data browse tab one more time and search entity by id 2. You should see the following beautiful graph.

Neo4j_graph

Have a good time playing with graphs! 🙂

Using Neo4j with F# – Cypher 2.0” is a new version of this demo that is updated to VS2013 and Cypher 2.0. Thanks to Chris Skardon.