Ninjecting Neo4jClient into MVC5/4/3

First, create your new MVC project (or load your existing one!)

Then add the Ninject.MVC nuget package:

image

We’ll also want the Neo4jClient package

image

Now we have those two, we can hook it up, first off we need to tell Ninject how to bind to Neo4jClient, I use a Module for this, so add this class to your project (usually I have them in a sub-folder called ‘Modules’ in the App_Start folder – but it can be anywhere):

public class Neo4jModule : NinjectModule
{
    /// <summary>Loads the module into the kernel.</summary>
    public override void Load()
    {
        Bind<IGraphClient>().ToMethod(InitNeo4JClient).InSingletonScope();
    }

    private static IGraphClient InitNeo4JClient(IContext context)
    {
        var neo4JUri = new Uri(ConfigurationManager.ConnectionStrings["Neo4j"].ConnectionString);
        var graphClient = new GraphClient(neo4JUri);
        graphClient.Connect();

        return graphClient;
    }
}

Now we just need to tell Ninject to load the module, so in the NinjectWebCommon.cs file (in the App_Start folder) edit the ‘RegisterServices’ method (at the bottom of the file) so it looks like:
/// <summary>
/// Load your modules or register your services here!
/// </summary>
/// <param name="kernel">The kernel.</param>
private static void RegisterServices(IKernel kernel)
{
    kernel.Load<Neo4jModule>();
} 

Lastly, we just need to inject into our controller, which is a case of adding a constructor (or modifying an existing one) to take an ‘IGraphClient’ instance:

private readonly IGraphClient _graphClient;
public HomeController(IGraphClient graphClient)
{
    _graphClient = graphClient;
}
 

Now the controller has an instance of the graphclient to use as and when it pleases:

public ActionResult Index()
{
    ViewBag.NodeCount =  _graphClient.Cypher.Match("n").Return(n => n.Count()).Results.Single();

    return View();
}
image

Obviously extending this, you could add a base ‘Neo4jController’ which any controllers requiring Neo4j override.

Using BeyondCompare 4 with Tortoise HG 2.11.1 (and possibly all 2.x previous)

So, I started off following the Using Beyond Compare with Version Control Systems knowledge base article on Scooter Software’s pages, and all seemed to be going well, but unfortunately it didn’t stay that way. In effect whenever I did a diff it would open Beyond Compare 4 (BC4) and not show the diff.

Not good.

So I did what we’d all do – raised a question on their support forums, which went well, fast responses, but didn’t fix it. Which is unfortunate, so I started reading up about how HG knows about diff tools etc, and was eventually led to the solution, which is:

  1. Go to: C:\Program Files\TortoiseHg\hgrc.d\
  2. Open up ‘MergeTools.rc’ (you might want to open it with administrator privileges)
  3. Paste the following in below the [merge-tools] header:
    ; Windows version of BeyondCompare 4
    beyondcompare4.priority=-1
    beyondcompare4.args=$local $other $base /mergeoutput=$output /ro /lefttitle=parent1 /centertitle=base /righttitle=parent2 /outputtitle=merged /automerge /reviewconflicts /solo
    beyondcompare4.premerge=False
    beyondcompare4.regkey=Software\Scooter Software\Beyond Compare 4
    beyondcompare4.regkeyalt=Software\Wow6432Node\Scooter Software\Beyond Compare 4
    beyondcompare4.regname=ExePath
    beyondcompare4.gui=True
    beyondcompare4.diffargs=/lro /lefttitle='$plabel1' /righttitle='$clabel' /solo /expandall $parent $child
    beyondcompare4.diff3args=$parent1 $parent2 $child /lefttitle='$plabel1' /centertitle='$clabel' /righttitle='$plabel2' /solo /ro
    beyondcompare4.dirdiff=True
  4. Save the file (I had to save as MergeTools.rc1 then rename in the folder itself)
  5. Restart TortoiseHG (the most successful way I’ve done this is to signout/in or restart the PC)
  6. Try a diff

Microsoft Word, Comments and Sorting by Date

I don’t know if you use Word on a daily basis, and even if you do – whether you use the ‘Revisions’ features which allow you to comment on the document and then comment on those comments and then … well you get the idea.

I do, and it’s becoming increasingly frustrating that I can’t order those comments by the date they were entered (or at least I can’t find how) and I have browsed for quite some time online trying Macro solutions (which create another document), even just reading the ‘comments’ tool window in word to try to see the most recent comment.

Either ways – it’s rubbish.

But.

I’m a developer, specifically a Microsoft .net developer. I casually note that Visual Studio has templates for developing Office 2013 addins – so – how hard can it be?

Turns out - not very.

The Addin:

To get the addin go to: http://sdrv.ms/1hOdoQ3 which gets you to my public SkyDrive folder where you can download it (you want ‘Word_2013_RevisionsByDateAddin.zip’)

Once it’s installed, you can enable / disable it via the ‘Add-ins’ menu on the ribbon:

image

(Sorry for the UI, I only spent about an hour on the code – it’s functional – not pretty :) )

That will show the pane (which will be on the right hand side, you can drag it around wherever you so please):

image

and you can click on the headers to sort by the various columns.

Double clicking on a row will take you to that part of the document, due to the implementation of this, it may not be obvious where it is as it scrolls to the first point – i.e. it might be at the bottom of the page. The addin will select the text on the page that was commented on, so you can use that as a guide.

Known Issues:

1. If a comment is really long, you can’t really see it all in the tool window (you can just double click on it and it’ll take you there though)

2. Not really an issue as such – but it will ask you to install the VSTO (Visual Studio Tools for Office) Runtime on your machine when you install – which does mean downloading it from Microsoft directly. This is handled in the installer, and it’s a whopping 38Mb :( – you can get it directly from MS if you want (http://www.microsoft.com/en-gb/download/details.aspx?id=40790)

Potential Issues:

1. I don’t know if it works on Word 2010 – please try it and let me know!

The Code

You can get the code and tinker with it as you will here (https://bitbucket.org/cskardon/office-addins) I will put it somewhere where it can be installed from better at some point.. probably.. maybe..

Let me know any problems (particularly install ones – please send a picture of an issue to me and I can try to fix it easier!!)

Error: Unable to access jarfile d:\Databases\Neo4j

Cryptic. I’ve spent a large proportion of time on this, and straight off the bat – NO this is not the ‘windows-jarfile’ error of the old skool days. No, this is a pretty simple issue, my folder structure was:

d:\Databases\Neo4j
d:\Databases\Neo4j Trie

Inside each a copy of the the ‘zip’ contents, with different configuration properties for the various things (ports etc) so I could run multiple copies of Neo4j at the same time.

All good.

EXCEPT I get this error from the “Neo4j Trie” folder when I try to run Neo4j.bat:

Error: Unable to access jarfile d:\Databases\Neo4j

I looked through the bat files, to no avail, then – a moment of inspiration! I rename the folder to:

d:\Databases\Neo4j-Trie

And HUZZAH! We’re back in business.

Update: I'm pleased to say that this is fixed in the bat file so from the next release on, it shouldn't be a problem.

Writing Neo4j Console

So, Neo4j Console is now open sourced at: https://github.com/tournr/neo4jvsconsole feel free to branch, fix, edit whatever and issue pull requests, or even just have a gander and code review :)

I thought I’d write some notes on what I’ve done and why.

Let’s split it into 2 main areas, UI and Backend.

The UI

Extremely simple WPF front end – I mean – it’s only got 3 text boxes! If I’m honest, it’s not what I want – I want to create a console window like the F# console, something which really does act like a console. This is step one on that route though. If you look at the code you’ll see it’s MVVM’d with MvvmLight (by Laurent Bugnion) – which has given me at least a semblance of testing of the ‘History’ function. Again – confession time – this isn’t working perfectly.

Backend

So – I’m interacting with Neo4j so Neo4jClient right? Alas no, I’m using RestSharp to do my business, and here is the why –

  1. I don’t want to spend ages parsing your cypher to fit it into the fluent structure (I know I can use the IRawGraphClient – but..)
  2. I need to use signed third party dlls
    No – this isn’t me being picky, it’s Visual Studio. Basically any extension for Visual Studio must be signed itself, and once you’ve gone down that route everything you reference has to be as well. I was very lucky RestSharp has a signed version.

I appreciate I could have build my own Neo4jClient and signed it, but that starts to make things complicated, and I’m nothing if not lazy.

Extension general learnings

  • Signing is a big pain in the ar*e when you haven’t done it in a long time.
  • Remembering to actually install the Visual Studio 2013 SDK is important (alas the Visual Studio 2012 SDK will not install on Windows 8.1)
  • Remember to set the vsix manifest so it will install on Visual Studio 2012 AND 2013 (sorry Kenny)

The Neo4j 1.9 to 2.0 Upgrade

So, with Neo4j 2.0 on the near horizon (RC1 out), it seemed appropriate to upgrade all my current codebase from 1.9 to 2.0. Generally, as a rule of thumb, the conversion has been pretty smooth, there are some pre-2.0 gaffs I’ve made which have made it slightly harder than it needed to be – but the ability to use 1.9 cypher in 2.0 db is incredibly useful.

So – I’m (as you can probably tell) a .NET developer, which means I use the excellent neo4jclient to perform pretty much all of my interaction with the database. The project (though I can’t go into specifics) has been using the client since 1.0.0.395 and initially I was using the Node<T> and NodeReference classes A LOT.

The BANG approach.

I guess some stats, the project isn’t huge, only 10,000 LOC (remember size doesn’t matter) and as a consequence my first thought was the let’s just do it all approach. So, start up a new DB and just see how it runs.

INSTANT FAIL

Of course – my queries are using ‘START’ ooooooooooooops, so I start doing a ‘Find/Replace’ hit some ridiculous number of build errors – swear a bit, back-out those changes and start again.

The Gradual approach.

Or, the “should’ve done this in the first place” approach. Neo4j has the option to switch the cypher parser – by putting ‘CYPHER X.X’ (where x.x is the version you want to use) in front of your queries, so you can run 1.9 queries against the 2.0 database.

Neo4jClient also supports this via the ‘ParserVersion’ method, so my initial queries of:

gc.Cypher
    .Start(new {n = new NodeReference(1)})
    .Return(n => n.As<Blah>())

change to:

gc.Cypher
    .ParserVersion(1,9)
    .Start(new {n = new NodeReference(1)})
    .Return(n => n.As<Blah>())

Compile and run – we’re in a happier place.

Labels, NodeReferences et al.

Now, those of you with eyes or indeed screen readers will have noted the ‘NodeReference’ being used above (even though it’s only an example!!) but still – naughty. NodeReference in Neo4jClient is the actual underlying Neo4j id for the node. There are various reasons not to use it, but one of the biggest is that it’s not something I / we have control over. Id’s can be reused, and there is no guarantee that id 1 is still id 1 after a day of activity.

Tatham Oddie (the primary author of awesomeness Neo4jClient) is moving the client away from NodeReference and Node<T> usage, effectively deprecating them as and when he can. Good news – this makes the code simpler as we deal with POCOs (Plain Old CLR Objects) exclusively, bad news – some chumps (*ahem* me *ahem*) used Node<T> quite a bit in their code and now would be better off removing it.

The other thing which I want, to take advantage of are Labels, so that means a bit of an update to the MATCH clauses. I say a bit. I mean a lot. If (for example) I have a match statement like:

.MATCH("n")

I need to update it to something more like:

.MATCH ("(n:Label)")

AND if I have lots of match statements like that, I have lots of updates to make. Basically, I’m paying for my naiveté at the beginning of the project.

Unit Tests

I’m doing quite a bit of query building based on given inputs (vague eh?) but in practice this means I have need to test my queries are generated correctly via a given input. This is where I’ve been bitten the hardest. See – I care a lot how these queries are produced, and I need to make sure they match up to the expected queries.

So for every MATCH n I’ve changed to MATCH (n:Label) I’ve had a test somewhere which then fails. OK, it’s not quite that bad, but you get the drift.

Conclusions

It’s taken some time (and some pretty impressive regex find/replace scripts if I do say so) but I finally managed to be 100% Cypher 2.0, and on the day Neo4j 2.0 stable was released, so how’s that for timing Smile.

Converting any codebase from a previous version to use the new version is always costly. There are two main changes – the first is the change to Cypher, this was a doddle, being able to put ‘CYPHER 1.9’ at the beginning meant the code worked straight away against Neo4j 2.0 and this is HUGELY helpful. The second change (for me) is removing all ‘Node<T>’ and ‘NodeReference’ usages, this was more complex but as a caveat – only because of the way I had written my code.

Neo4j Console

Hey Hey Super Early adopters, if you’re using Neo4j and you’re a Visual Studio developer (which probably means I’ve just lot 99% of the audience :)) please try Neo4j Console, it’s an extension for Visual Studio 2013 that allows you to make calls to your local (or remote) server instance of Neo4j.

So, once you’ve downloaded it – the question I guess is HOW DO I USE THAT!!!??

Opening It

Once installed, it finds itself in the ‘View->Other Windows’ menu, it’s called ‘Neo4j Console’, just like the picture below shows:

SelectingFromMenu

Cool – so we have it on the screen now.

Querying

Type what you want into the bottom box, press CTRL+ENTER and KAPOW! your cypher is sent over to the server, whereby it will (hopefully) respond to you and display that response in a pleasing way:

SingleColumn

It should accept any valid Cypher that the server will accept, Creates pose no problem:

CreateAndReturn

If there is a problem, it’ll let you know:

ShowsErrors

Tips

  • Remember to press CTRL+ENTER to send your cypher to the server.
  • Pressing the Up & Down arrow keys in the ‘Cypher’ box will scroll up and down through your previous cypher entries (sometimes it’s a bit odd about how it does this – fixes en route)

Open Source?

It will be soon – I just want to tidy it up a bit, but then it’s all guns blazing to an open source repo near you!

Problems

Email me, twitter me, LET ME KNOW!! (Please!!)

Another day, another blog post update

Gist: https://gist.github.com/cskardon/7870411

I know! Another question on StackOverflow leads to yet another blog post being updated to use the newer Neo4jClient. So here goes:

Darko Micics has written a post about using Neo4jClient with C#.Net, and a question has arisen on StackOverflow asking about how to (in effect) do this in the new version of the Neo4JClient – the version where ‘CreateRelationship’ et al have been marked as obsolete.

Luckily, this is a pretty simple (twitter-ish) data set – but in this one, we’re also going to add some data to the relationships, which we eschewed last time. Oh, and we’ll use C# instead of F# :)

The Setup

So. We’ll start with the super simple ‘Person’ class, which is EXACTLY the same as Darko’s:

public class Person { public string Name { get; set; } }

Now, in Darko’s we have a ‘Knows’ and a ‘Hates’ relationship. This is where I will diverge slightly, as I personally prefer ‘Likes’ and ‘Dislikes’ (Hate is a very strong word) – plus, even if you hate someone (perhaps especially) you still ‘know’ them, otherwise how could you hate them?

ANYHEWS

We still want a reason for our dislikes (and maybe even our likes), and so we add another POCO – the ‘Reason’ class:

public class Reason { public string Value { get; set; } }

And because I’m a bit lazy, and prone to typos I’m going to introduce a ‘Relationship’ enum with the relationships we’ll be modeling:

public enum Relationship
{
    LIKES,
    DISLIKES
}

Creation

The original uses the ‘Create’ method on the Neo4jClient, but this uses the old skool REST API, and we don’t want to do this anymore, we want to Cypher this bad boy all the way, so let’s add a ‘Create’ method:

private void Create(Person p)
{
    Client.Cypher
        .Create("(p:Person {params})")
        .WithParam("params", p)
        .ExecuteWithoutResults();
}

Two things of note, firstly, we’re adding a label when creating (the :Person bit), and secondly we’re passing in the person as a parameter (the “params”, p bit). Now we can do the first bit of the code:

Create(new Person{Name = "Person A"});
Create(new Person{Name = "Person B"});
Create(new Person{Name = "Person C"});
Create(new Person{Name = "Person D"});

Now we want to relate them together, the cypher for this is pretty simple, MATCH the two Persons, create a new relationship between them. I’ve put this into a method, which also adds a reason if supplied:

private void CreateRelationship(Person from, Person to, Relationship relationship, Reason reason = null)
{
    var query = Client.Cypher
        .Match("(f:Person), (t:Person)")
        .Where((Person f) => f.Name == from.Name)
        .AndWhere((Person t) => t.Name == to.Name);

    if(reason == null)
        query
            .Create(string.Format("(f)-[:{0}]->(t)", relationship))
.ExecuteWithoutResults(); else query .Create(string.Format("(f)-[:{0} {{relParam}}]->(t)", relationship)) .WithParam("relParam", reason) .ExecuteWithoutResults(); }

Let’s break this down a bit, the first section creates the correct ‘MATCH / WHERE’ clause, which will find our two people. Obviously there is a huge issue if two people have the same name!!! The second part creates the actual create statement, adds the reason (if needed) and executes the query.

So now we can change our startup code to be:

var pA = new Person {Name = "Person A"};
var pB = new Person {Name = "Person B"};
var pC = new Person {Name = "Person C"};
var pD = new Person {Name = "Person D"};

Create(pA);
Create(pB);
Create(pC);
Create(pD);

CreateRelationship(pA, pB, Relationship.LIKES);
CreateRelationship(pB, pC, Relationship.LIKES);
CreateRelationship(pB, pD, Relationship.DISLIKES, new Reason{Value = "Crazy guy"});
CreateRelationship(pC, pD, Relationship.DISLIKES, new Reason{Value = "Don't know why..."});
CreateRelationship(pD, pA, Relationship.LIKES);

Which gives us the following graph:

image

(I’ve clicked on the dislikes relationship to show the value has been set).

Getting

Next up we want to get all the people who dislike Person D. The query is pretty simple in this case:

var query = Client.Cypher
    .Match("(p:Person)<-[r:DISLIKES]-(other:Person)")
    .Where((Person p) => p.Name == disliked.Name)
    .Return((other, r) =>
        new
        {
            Name = other.As<Person>().Name,
            Reason = r.As<Reason>().Value
        });

Here we project the results into an anonymous type – if we wanted to pass these outside of the method we’d need another class, but we all know that!

We can take those results and write them to the screen:

var res = query.Results.ToList();
Console.WriteLine("{0} disliked by:", disliked.Name);
foreach (var result in res)
    Console.WriteLine("\t{0} because {1}", result.Name, result.Reason);

Which gets us:

image

Aceness.

Improving

So, that basically updates the code ‘as-is’. First cut benefits include

  • no need for relationship classes,
  • we get to use labels
  • our code is directly transferable to Cypher, so we can test the queries on the Neo4j management page straight away.

There are some problems with the code (just some I hear you say?)

  • We could create multiple relationships between the same people – if I call ‘CreateRelationship’ n times, I will get n relationships – which in this example doesn’t make sense.
  • The labels are hand typed, which means they can be typo’d which will cause no end of trouble down the line.

Fixing those problems

Multiple Relationships

The first problem of multiples of the same relationship is a simple one to fix, we change the ‘create’ on our ‘CreateRelationship’ to be ‘CreateUnique’. So CreateRelationship becomes:

private void CreateRelationship(Person from, Person to, Relationship relationship, Reason reason = null)
{
    var query = Client.Cypher
        .Match("(f:Person), (t:Person)")
        .Where((Person f) => f.Name == from.Name)
        .AndWhere((Person t) => t.Name == to.Name);

    if(reason == null)
        query
            .CreateUnique(string.Format("(f)-[:{0}]->(t)", relationship)).ExecuteWithoutResults();
    else
        query
            .CreateUnique(string.Format("(f)-[:{0} {{relParam}}]->(t)", relationship))
            .WithParam("relParam", reason)
            .ExecuteWithoutResults();
}

and you can run the following Cypher.

Labels

Typo’s being the biggest issue, we could go down a similar route to using an enum, so:

public enum Labels {
    Person,
}

which we then use:

...Cypher.Match(string.Format("(p:{0})", Labels.Person))...

Or another option is to define a base class for all the objects we’re serializing like so:

public abstract class Neo4jObject
{
    [JsonIgnore]
    public string Labels { get; private set; }

    protected Neo4jObject(string labels)
    {
        Labels = labels;
    }
}

(We JsonIgnore the property, as we don’t want it serialized to the database). This is then implemented by all the data items you want stored:

public class Person : Neo4jObject
{
    public Person() : base("Person") { }
    public string Name { get; set; }
}

Used in very much the same way as the enum would be – the big downside is that you need an instance to get the labels, which tends to make you lean towards doing this sort of implementation instead:
public class Person : Neo4jObject
{
    public const string TypeLabels = "Person";
    public Person() : base(TypeLabels) { }
    public string Name { get; set; }
}

Which allows you to get the Labels without needing an instance. Benefits wise – anyone who subsequently uses your class – provided they use the ‘Labels’ property will get the same labels.

I’m still not 100% sure on which method I prefer at the moment, but I’m leaning towards the latter.

Anyhews, that as they say is it.

A Clear DB solution for Neo4j 2.0 RC1

The old skool way (pre RC1) of wiping the database was:

//Clear DB
START n=node(*)
MATCH (n)-[r?]-() 
DELETE n, r

Which worked fine for M06 and previous, but, RC1 doesn’t allow optional relationships (using the ? operator) you’ll need to update to:

//Clear DB
START n=node(*)
OPTIONAL MATCH (n)-[r]-() 
DELETE n, r

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:

[<CLIMutable>]
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<Person>("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, CreateUnique 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:

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:

[<CLIMutable>]
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<Person>("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<Person>())

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

.Return<Person>("p")

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