Using Neo4j with F# – Cypher 2.0

There’s an excellent post by Sergey Tihon about using Neo4j with F#, written back in March, before Neo4j began the progression into Cypher 2.0. (Gist for this here: https://gist.github.com/cskardon/7673426)

Some of the benefits of the newer Neo4jClient versions are the general move away from using Node / NodeReferences and  going for a more consistent Cypher only approach, so I’ve updated the code by Sergey to use the 2.0 stuff (as part of the process of answering this StackOverflow question).

BTW: It should be noted, I am not an F# developer, so any corrections are warmly received!

I’m using VS2013 and I’ve created a new FSharp Console Application for this, which I believe is a different approach to Sergey, but I think you can do the same ‘script’ approach he has, but with this code (below) and we’ll be cooking.

Sergey is creating a basic twitter pattern, so Person A follows Person B etc, we need a POCO for our person:

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

Now we can define our new ‘createPerson’ function:

let createPerson person = client.Cypher .Create("(p:Person {param})") .WithParam("param", person) .Return("p") .Results .Single

Here we’ve attached a ‘Person’ label to our person being created.

So, let’s create some people:

let pA = createPerson { Name = "PersonA"; Twitter = "tA" } let pB = createPerson { Name = "PersonB"; Twitter = "tB" } let pC = createPerson { Name = "PersonC"; Twitter = "tC" } let pD = createPerson { Name = "PersonD"; Twitter = "tD" }

Now, we need to add the ‘follows’ function:

let follows target source = client.Cypher .Match("(s:Person)", "(t:Person)") .Where(fun s -> s.Twitter = source.Twitter) .AndWhere(fun t -> t.Twitter = target.Twitter) .CreateUnique("s-[:follows]->t") .ExecuteWithoutResults

Again, we’re using the Labels in the Match clause, will only create the link if it doesn’t already exist, which is what we’re after. So once again, following Sergey’s example, let’s do some following:

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

We now have a graph like so:

!(/images/cskardon/using-neo4j-with-f-ndash-cypher-2.0/e5e194e2-fsharp_graph_1266835520.png "fsharp_graph_1266835520.png")

In the next step Sergey adds a ‘knows’ relationship, with some data defining ‘how’ the people know each other. So step one, let’s create our Knows data:

[] type Knows = { How:string }

Note, there is no use of the ‘Relationship’ classes here, we’re shifting away from them. Now we need to define our knows function, I’ve modified the function signature here to allow us to use Parameters which is better for Neo4j on the whole, plus we don’t really want a load of ‘Knows’ relationships with different data structures.

let knows target (details: Knows) source = client.Cypher .Match("(s:Person)", "(t:Person)") .Where(fun s -> s.Twitter = source.Twitter) .AndWhere(fun t -> t.Twitter = target.Twitter) .CreateUnique("s-[:knows {knowsData}]->t") .WithParam("knowsData", details) .ExecuteWithoutResults

And let’s make B know C…

pB |> knows pC {How = "colleagues"}

Now we can do the same query as Sergey and get all the people who follow ‘A’:

let pAfollowers = client.Cypher .Match("n<-[:follows]-e") .Where(fun n -> n.Twitter = "tA") .Return("e") .Results .Select(fun x -> x.Name)

As I said before, I’m not au fait with F# really, there are a couple of things I’d have liked to have done, but I’m not sure how at the moment, the biggest (and most annoying) is the Return statements. They should be:

.Return(fun p -> p.As)

But the F# compiler complains about that, so I’ve had to drop back to the

.Return("p")

route. Maybe someone can figure out how to get that to work, which would be awesome!

This article is part of the GWB Archives. Original Author: Chris Skardon

New on Geeks with Blogs

  • We Won The One Award I Actually Care About

    Full Scale made the Inc. 5000 for the fifth year straight, the 12th listing across my three companies. Here is why the one award you cannot buy is worth stopping for.

  • Your Customers Build the Features Now

    I let a tool I liked sit dead for a year rather than build the features I wanted. An MCP server meant I never had to, and your customers can do the same to your product.

  • Get the Size of a Directory in Linux the Easy Way

    du -sh for the quick answer, ncdu for the cleanup, df for the disk itself: every command for checking directory size in Linux, plus why du and df never agree.

  • Vim Search and Replace: The Ultimate Guide

    One :%s command replaces every match in a file before a find dialog would even open. The Vim substitute patterns worth the muscle memory: flags, ranges, capture groups, and multi-file edits.