用Neo4jClient和Cypher从嵌套对象创建图形

本文关键字:对象 创建 图形 嵌套 Neo4jClient Cypher | 更新日期: 2023-09-27 18:01:52

我有一些数据建模为一组简单的嵌套c#对象,我试图使用。net Neo4jClient从neo4j数据库创建/检索。

我的类是这样的:

public class Foo {
    public int ID { get; set; }
    public List<Bar> bar { get; set;}
}
public class Bar {
    public int ID { get; set; }
    public List<Baz> baz { get; set;}
}
public class Baz {
    public int ID { get; set; }
}

一旦数据以正确的形式存储在数据库中:

(f:Foo)-[h:HASBAR]->(b:Bar)-[hb:HASBAZ]->(bz:Baz) 

我可以使用以下查询使用collect和可选匹配将数据检索到我的类结构中:

List<Foo> foolist = WebApiConfig.GraphClient.Cypher
    .Match("(f:Foo)-[h:HASBAR]->(b:Bar)")
    .OptionalMatch("(b)-[hb:HASBAZ]->(bz:Baz)")
    .With("f, { ID: b.ID, baz: collect(bz) } as Bar")
    .With("{ ID:f.ID, bar:collect(Bar) } as Foo")
    .Return<Foo>("Foo")
    .Results
    .ToList();

这一切都很完美,数据被正确地序列化到适当的类中。

我的问题是我应该如何执行相反的?

在给定单个Foo类,包含多个嵌套的bar和baz类,我可以在单个查询中创建数据库中的上述数据结构吗?

或者我必须为每层嵌套编写一个查询吗?

我知道我可能必须在创建时列出属性,就像我给客户端一个Foo类一样,它将创建一个带有"bar"作为属性的节点。

我的问题主要来自嵌套的第三层,如果我把第二层(bar)作为一个数组(传入Foo.bar)作为一个变量,我可以创建多个[:HASBAR]关系。但是在同一个查询中,我还没有找到将正确的Baz节点与Bar节点关联起来的方法。

我是否以正确的方式接近这个?

用Neo4jClient和Cypher从嵌套对象创建图形

好吧,它可能在一个查询中做到这一点-不幸的是,我不认为你可以使用美味的UNWINDFOREACH由于二次嵌套,你需要做一些时髦的事情与类,但好吧,这里是:

首先,我们需要定义类,所以我们可以反序列化属性,但不序列化它们,到最后

public class Foo
{
    public int ID { get; set; }
    [JsonIgnore]
    public List<Bar> bar { get; set; }
    [JsonProperty("bar")]
    private List<Bar> barSetter { set { bar = value;} }
}
public class Bar
{
    public int ID { get; set; }
    [JsonIgnore]
    public List<Baz> baz { get; set; }
    [JsonProperty("baz")]
    private List<Baz> bazSetter { set { baz = value; } }
}
public class Baz
{
    public int ID { get; set; }
}

这是什么疯了吗?嗯-通过使用[JsonIgnore],我们告诉Json不要序列化或反序列化给定的属性- 但是我们想要反序列化,所以你的检索查询将工作-所以有JsonProperty private setter允许我们实现这一点。

这种方法的额外好处是,您不需要在Cypher生成位中指定要序列化的属性。这是它所有的荣耀:

var query = gc.Cypher
    .Create("(f:Foo {fooParam})")
    .WithParam("fooParam", foo);
for (int barIndex = 0; barIndex < foo.bar.Count; barIndex++)
{
    var barIdentifier = $"bar{barIndex}";
    var barParam = $"{barIdentifier}Param";
    query = query
        .With("f")
        .Create($"(f)-[:HASBAR]->({barIdentifier}:Bar {{{barParam}}})")
        .WithParam(barParam, foo.bar[barIndex]);
    for (int bazIndex = 0; bazIndex < foo.bar[barIndex].baz.Count; bazIndex++)
    {
        var bazIdentifier = $"baz{barIndex}{bazIndex}";
        var bazParam = $"{bazIdentifier}Param";
        query = query
            .With($"f, {barIdentifier}")
            .Create($"({barIdentifier})-[:HASBAZ]->({bazIdentifier}:Baz {{{bazParam}}})")
            .WithParam(bazParam, foo.bar[barIndex].baz[bazIndex]);
    }
}

f:Foo位是正常的,随后的for循环允许您定义每个标识符并设置参数。

我不认为这是一个理想的解决方案,但它将工作,并将执行在一个查询。显然,如果有很多嵌套的值,这会变得很笨拙。