使用 Neo4jClient 和 Cypher 从嵌套对象创建图形
Creating Graph from nested objects with Neo4jClient and Cypher
我有一些数据建模为一组简单的嵌套 c# 对象,我正尝试使用 .Net Neo4jClient 从 neo4j 数据库中 create/retrieve。
我的 class 的形式是:
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)
我能够使用以下查询使用收集和可选匹配将数据检索到我的 class 结构中:
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();
这一切都完美无缺,数据被正确序列化为正确的 classes。
我的问题是我应该如何执行反向操作?
如给定单个 Foo class,包含多个嵌套的 bar 和 baz classes,我可以在单个查询中在数据库中创建上述数据结构吗?
或者我是否必须为每个嵌套级别编写一个查询?
我知道在创建时我可能必须列出属性,就好像我给客户端一个 Foo class 它会创建一个 "bar" 作为 属性 的节点。
我的问题主要来自第三层嵌套,如果我把第二层(bar)当作一个数组(传入Foo.bar)作为一个变量,我可以创建多个[:HASBAR]关系.但是在同一个查询中,我还没有找到将正确的 Baz 节点与 Bar 节点相关联的方法。
我的处理方式是否正确?
非常感谢任何回复,提前致谢...
好吧, 可以在一次查询中完成 - 不幸的是,我不认为你可以使用美味的 UNWIND
或 FOREACH
由于到二级嵌套,你需要用 类 做一些时髦的事情,但是,这里是:
首先,我们需要定义 类,因此我们可以 反序列化 属性,但不序列化它们,为此
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 不要序列化或反序列化给定的 属性 - 但是 我们想要反序列化所以你的检索查询将起作用- 因此 private
setter 和 JsonProperty
允许我们实现这一点。
此方法的额外好处是您无需指定要在 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
循环允许您定义每个标识符并设置参数。
我认为这不是一个理想的解决方案,但它会起作用,并且会在 1 个查询中执行。 Obvs,如果有很多嵌套值,这可能会变得笨拙。
我有一些数据建模为一组简单的嵌套 c# 对象,我正尝试使用 .Net Neo4jClient 从 neo4j 数据库中 create/retrieve。
我的 class 的形式是:
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)
我能够使用以下查询使用收集和可选匹配将数据检索到我的 class 结构中:
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();
这一切都完美无缺,数据被正确序列化为正确的 classes。
我的问题是我应该如何执行反向操作?
如给定单个 Foo class,包含多个嵌套的 bar 和 baz classes,我可以在单个查询中在数据库中创建上述数据结构吗?
或者我是否必须为每个嵌套级别编写一个查询?
我知道在创建时我可能必须列出属性,就好像我给客户端一个 Foo class 它会创建一个 "bar" 作为 属性 的节点。
我的问题主要来自第三层嵌套,如果我把第二层(bar)当作一个数组(传入Foo.bar)作为一个变量,我可以创建多个[:HASBAR]关系.但是在同一个查询中,我还没有找到将正确的 Baz 节点与 Bar 节点相关联的方法。
我的处理方式是否正确?
非常感谢任何回复,提前致谢...
好吧, 可以在一次查询中完成 - 不幸的是,我不认为你可以使用美味的 UNWIND
或 FOREACH
由于到二级嵌套,你需要用 类 做一些时髦的事情,但是,这里是:
首先,我们需要定义 类,因此我们可以 反序列化 属性,但不序列化它们,为此
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 不要序列化或反序列化给定的 属性 - 但是 我们想要反序列化所以你的检索查询将起作用- 因此 private
setter 和 JsonProperty
允许我们实现这一点。
此方法的额外好处是您无需指定要在 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
循环允许您定义每个标识符并设置参数。
我认为这不是一个理想的解决方案,但它会起作用,并且会在 1 个查询中执行。 Obvs,如果有很多嵌套值,这可能会变得笨拙。