Dalaigh88
Dalaigh88

Reputation: 427

Add temporary property to node in Neo4j Cypher for return

I am using the Neo4j .Net client to interface with a Neo4j database.

I often come by this issue when trying to map related nodes to nested classes :

For example mapping

(:Foo)-[:FOOBAR]->(:Bar)

to the form

public class Foo {
    public string FooPropOne { get; set; }
    public string FooPropTwo { get; set; }
    public List<Bar> Bars { get; set; }  
}

public class Bar {
    public string BarPropOne { get; set; }
    public string BarPropTwo { get; set; }
}

To get the correct output for deserialzation I have to write the query like so:

Foo foo = WebApiConfig.GraphClient.Cypher
    .Match("(f:Foo)-[:FOOBAR]->(b:Bar)")
    .With("@{
                FooPropOne: f.FooPropOne,
                FooPropTwo: f.FooPropTwo,
                Bars: collect(b)
             } AS Result")
    .Return<Foo>("Result")
    .SingleOrDefault();

To a certain extent this is fine, and works perfectly, but becomes quite unwieldy the more properties the Foo class has.

I was wondering if there was something I can do in the "With" statement to add a temporary property (in the above case "Bars") when returning the nodes, that does not require me to write every single property on the parent node?

Something like

.With("f.Bars = collect(b)")
.Return<Foo>("f")

that wont actually affect the data stored in the DB?

Thanks in advance for any relpies!!

Upvotes: 1

Views: 688

Answers (1)

Charlotte Skardon
Charlotte Skardon

Reputation: 6280

I don't know of a way to add a temporary property like you want, personally - I would take one of two approaches, and the decision really comes down to what you like to do.

Option 1

The choosing properties version

var foo = client.Cypher
    .Match("(f:Foo)-[:FOOBAR]->(b:Bar)")
    .With("f, collect(b) as bars")
    .Return((f, bars) => new Foo
    {
        FooPropOne = f.As<Foo>().FooPropOne,
        FooPropTwo = f.As<Foo>().FooPropTwo,
        Bars = Return.As<List<Bar>>("bars")
    }).Results.SingleOrDefault();

This version allows you to be Type Safe with your objects you're bringing out, which on a class with lots of properties does limit the chance of a typo creeping in. This will give you back a Foo instance, so no need to do any other processing.

Option 2

The little bit of post-processing required version

var notQuiteFoo = client.Cypher
    .Match("(f:Foo)-[:FOOBAR]->(b:Bar)")
    .With("f, collect(b) as bars")
    .Return((f, bars) => new
    {
        Foo = f.As<Foo>(),
        Bars = bars.As<List<Bar>>()
    }).Results;

This saves on the typing out of the properties, so in a big class, a time saver, and also means if you add extra properties you don't need to remember to update the query. However you do need to do some post-query processing, something like:

IEnumerable<Foo> foos = notQuiteFoo.Results.Select(r => {r.Foo.Bars = r.Bars; return r.Foo;});

Personally - I like Option 2 as it has less potential for Typos, though I appreciate neither is exactly what you're after.

Upvotes: 2

Related Questions