Neo4jClient - Retrieving relationship from Cypher

2019-01-29 00:24发布

问题:

I'm having trouble retrieving matched relationships from a Cypher query.

I have this simple trial code:

var movie = client.Create(new Movie { Title = "The Matrix" });

client.Create(new Actor { Name = "Keanu Reeves" },
    new ActedIn(movie, new ActedInPayload { Role = "Neo" }));

client.Create(new Actor { Name = "Hugo Weaving" },
    new ActedIn(movie, new ActedInPayload { Role = "Agent Smith" }));

var actorsAndRoles = client
    .Cypher
    .Start(new { movie = movie })
    .Match("actor-[r:ACTED_IN]->movie")
    .Return((actor, r) => new
    {
        Actor = actor.As<Node<Actor>>()
        // ActedIn = r.As<?????>()
    })
    .Results;

Problem is that I can't work out how to cast the r (with the matched relationship).

Tried various ".As" type casts, but none work. Casting to Relationship doesn't work because my relationship class doesn't have a parameterless constructor - but then the Relationship base class doesn't have a parameterless constructor itself, so don't think that would work. Casting to RelationshipReference on the other hand causes an exception. Not casting at all (just returning r) causes a "not supported" exception.

There are some related SO entries about this issue, but the suggested code there either no longer works or is deprecated.

How do I retrieve the matched relationship?

回答1:

You can create a parameterless constructor for your relationship type, you just pass it 'duff' data, which I know sounds bad, but as you don't really need it, it won't hurt you:

public class ActedIn : Relationship<ActedInPayload>, IRelationshipAllowingSourceNode<Actor>, IRelationshipAllowingTargetNode<Movie>
{
    public ActedIn() : base(-1, null) {}
    public ActedIn(NodeReference targetNode, ActedInPayload data) : base(targetNode, data) {}

    public override string RelationshipTypeKey
    {
        get { return "ACTED_IN"; }
    }
}

So this is the ActedIn class, with the parameterless constructor chaining a '-1' down to the base constructor.

Your query then becomes:

var actorsAndRoles = client
    .Cypher
    .Start(new {movie})
    .Match("actor-[r:ACTED_IN]->movie")
    .Return((actor, r) => new
        {
            Actor = actor.As<Node<Actor>>(),
            ActedIn = r.As<RelationshipInstance<ActedInPayload>>()
        })
    .Results;

Note, it's being cast to a RelationshipInstance of type ActedInPayload not ActedIn, then, to get the data you might want:

foreach (var actorsAndRole in actorsAndRoles)
{
    Console.WriteLine("{0} acted as {1} - which is in the db as {2}-[ACTED_IN]->{3}", 
        actorsAndRole.Actor.Data.Name, 
        actorsAndRole.ActedIn.Data.Role, 
        actorsAndRole.ActedIn.StartNodeReference.Id, 
        actorsAndRole.ActedIn.EndNodeReference.Id);
}

Which will give you something like:

Keanu Reeves acted as Neo - which is in the db as 482-[ACTED_IN]->481

Hugo Weaving acted as Agent Smith - which is in the db as 483-[ACTED_IN]->481

Though obviously with different numbers on your own DB.