DotNet4Neo4j / Neo4jClient

.NET client binding for Neo4j
https://www.nuget.org/packages/Neo4jClient
Microsoft Public License
431 stars 146 forks source link

How to match multiple relationships? #450

Closed pandabytes closed 1 year ago

pandabytes commented 1 year ago

In Cypher we can match multiple relationships like this

match (p:Person) where p.first_name = 'Jack'
match (p)-[r:LIVES_IN|BORN_IN]->(l:Location)
return p, r, l;

How can we achieve this using Neo4jClient? This is what I have so far but stuck on retrieving the relationship

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

class Location 
{
  public string City { get; set }
}

class LivesInRelationship
{
  public DateTime? Since { get; set; }
}

class BornInRelationship
{
  public string HospitalName { get; set; }
}

var results = await _cypherGraphClient.Cypher
                        .Match($"(p:Person)-[r:LIVES_IN|BORN_IN]->(l:Location)")
                        .Where((Person p) => p.FirstName == "Jack")
                        .Return((p, r, l) => new { 
                          Person = p.As<Person>(),
                          Location = l.As<Location>(),
                          // How would I get the relationship here, when the relationship can
                          // be either LivesInRelationship or BornInRelationship?
                          // Relationship = ???
                        })
                        .ResultsAsync;

Versions:

cskardon commented 1 year ago

The easiest way for you would be to have a LivesInBornInRelationship that looks like this:

class LivesInBornInRelationship
{
  public DateTime? Since { get; set; }
  public string HospitalName { get; set; }
}

But that's not sustainable if you increase the numbers of relationship types. At that point you are looking at returning it as a string and parsing it yourself, which is again, not an ideal solution - but to do so, you'd want 3 things, the

  1. Is a class to hold the data:
public class RelationshipType<TRel>{
    public TRel Data {get; set;}
}
  1. Update the query to return the relationships as a <string>:
var results = await _cypherGraphClient.Cypher
                        .Match($"(p:Person)-[r:LIVES_IN|BORN_IN]->(l:Location)")
                        .Where((Person p) => p.FirstName == "Jack")
                        .Return((p, r, l) => new { 
                          Person = p.As<Person>(),
                          Location = l.As<Location>(),
                          Relationship = r.As<string>()
                      })
                     .ResultsAsync

And then a method to do the conversion:

foreach (var result in results){
    switch(result.Type){
        case "ACTED_IN":
            return JsonConvert.DeserializeObject<RelationshipType<ActedIn>>(result. Data).Data;
        case "DIRECTED":
            return JsonConvert.DeserializeObject<RelationshipType<Directed>>(result. Data).Data;
        default:
            throw new ArgumentOutOfRangeException("Type", $"Unknown relationship type {result. Type}");
    }
}

Depending on what you're doing with it obviously.

pandabytes commented 1 year ago

Got it, thank you @cskardon!