Neo4j高效添加多个节点和边
我有下面的例子 我想知道在单个事务中添加节点和边列表的最佳和最快的方法是什么?我使用标准的C#Neo4j.NET软件包,但是我已经读到了Neo4jClient,它更快。老实说,任何支持.NET和4.5的东西 我有一个大约60000个FooA对象的列表,需要添加到Neo4j中,这可能需要几个小时 首先,FooB对象几乎不会改变,所以我不必每天添加它们。性能问题在于每天添加两次新的FooA对象 每个FooA对象都有一个FooB对象列表,其中有两个列表包含我需要添加的关系;RelA和RelB(见下文) 如何使用Neo4jClient和UNWIND或CSV导入之外的任何其他方式添加FooA的列表Neo4j高效添加多个节点和边,neo4j,neo4jclient,Neo4j,Neo4jclient,我有下面的例子 我想知道在单个事务中添加节点和边列表的最佳和最快的方法是什么?我使用标准的C#Neo4j.NET软件包,但是我已经读到了Neo4jClient,它更快。老实说,任何支持.NET和4.5的东西 我有一个大约60000个FooA对象的列表,需要添加到Neo4j中,这可能需要几个小时 首先,FooB对象几乎不会改变,所以我不必每天添加它们。性能问题在于每天添加两次新的FooA对象 每个FooA对象都有一个FooB对象列表,其中有两个列表包含我需要添加的关系;RelA和RelB(见下文
希望这是有意义的,谢谢 最大的问题是嵌套列表,这意味着您必须执行
foreach
循环,因此每个FooA
至少执行4个查询,这对于60000个用户来说——好吧——太多了
快速注释:索引
首先也是最重要的-您需要对FooA
和FooB
节点的Id
属性建立索引,这将大大加快查询速度
我已经玩了一点,让它在我老化的计算机上存储60000个FooA条目,并在大约12-15秒内创建96000个RelB实例
解决方案
我将其分为两个部分-FooA和RelB:
福阿
我必须将FooA
类标准化为我可以在Neo4jClient
中使用的东西,所以让我们介绍一下:
public class CypherableFooA
{
public CypherableFooA(FooA fooA){
Id = fooA.Id;
Name = fooA.Name;
Age = fooA.Age;
}
public long Id { get; set; }
public string Name { get; set; }
public long Age { get; set; }
public string RelA_Val1 {get;set;}
public long RelA_FooBId {get;set;}
}
我已经添加了RelA_Val1
和RelA_FooBId
属性,以便能够在UNWIND
中访问它们。我使用助手方法转换您的FooA
:
public static IList<CypherableFooA> ConvertToCypherable(FooA fooA){
var output = new List<CypherableFooA>();
foreach (var element in fooA.ListA)
{
var cfa = new CypherableFooA(fooA);
cfa.RelA_FooBId = element.Node.Id;
cfa.RelA_Val1 = element.Val1;
output.Add(cfa);
}
return output;
}
展平FooA
实例,因此对于FooA
的ListA
属性中的每个项目,我最终得到1个CypherableFooA
。e、 g.如果您在每个FooA
上的ListA
中有2个项目,并且您有5000个FooA
实例-您将得到包含10000个项目的cyperable
现在,使用cypherable
我调用我的AddFooAs
方法:
public static void AddFooAs(IGraphClient gc, IList<CypherableFooA> fooAs, int batchSize = 10000, int startPoint = 0)
{
var batch = fooAs.Skip(startPoint).Take(batchSize).ToList();
Console.WriteLine($"FOOA--> {startPoint} to {batchSize + startPoint} (of {fooAs.Count}) = {batch.Count}");
if (batch.Count == 0)
return;
gc.Cypher
.Unwind(batch, "faItem")
.Merge("(fa:FooA {Id: faItem.Id})")
.OnCreate().Set("fa = faItem")
.Merge("(fb:FooB {Id: faItem.RelA_FooBId})")
.Create("(fa)-[:RelA {Prop: faItem.RelA_Val1}]->(fb)")
.ExecuteWithoutResults();
AddFooAs(gc, fooAs, batchSize, startPoint + batch.Count);
}
然后我将它们添加到Neo4j中,如下所示:
public static void AddRelBs(IGraphClient gc, IList<RelB> relbs, int batchSize = 10000, int startPoint = 0)
{
var batch = relbs.Select(r => new { StartId = r.Start.Id, EndId = r.End.Id, r.ValExample }).Skip(startPoint).Take(batchSize).ToList();
Console.WriteLine($"RELB--> {startPoint} to {batchSize + startPoint} (of {relbs.Count}) = {batch.Count}");
if(batch.Count == 0)
return;
var query = gc.Cypher
.Unwind(batch, "rbItem")
.Match("(fb1:FooB {Id: rbItem.StartId}),(fb2:FooB {Id: rbItem.EndId})")
.Create("(fb1)-[:RelA {Prop: rbItem.ValExample}]->(fb2)");
query.ExecuteWithoutResults();
AddRelBs(gc, relbs, batchSize, startPoint + batch.Count);
}
publicstaticvoidaddrelbs(IGraphClient-gc,IList-relbs,int-batchSize=10000,int-startPoint=0)
{
var batch=relbs.Select(r=>new{StartId=r.Start.Id,EndId=r.End.Id,r.ValExample}).Skip(startPoint.Take(batchSize.ToList();
WriteLine($“RELB-->{startPoint}到{batchSize+startPoint}(of{relbs.Count})={batch.Count}”);
如果(batch.Count==0)
返回;
var query=gc.Cypher
.展开(批次,“rbItem”)
.Match((fb1:FooB{Id:rbItem.StartId}),(fb2:FooB{Id:rbItem.EndId})
.Create(“(fb1)-[:RelA{Prop:rbItem.ValExample}]->(fb2)”;
query.ExecuteWithoutResults();
AddRelBs(gc、relbs、batchSize、startPoint+batch.Count);
}
同样,批处理默认为10000
显然,时间会根据
ListB
和ListA
中的rel数量而有所不同-我的测试在ListA
中有一个项目,在ListB
中有两个项目。您对此有任何限制吗?i、 e.“Id”是否被约束为唯一的?Id是唯一的约束。
var cypherable = fooAList.SelectMany(a => ConvertToCypherable(a)).ToList();
public static void AddFooAs(IGraphClient gc, IList<CypherableFooA> fooAs, int batchSize = 10000, int startPoint = 0)
{
var batch = fooAs.Skip(startPoint).Take(batchSize).ToList();
Console.WriteLine($"FOOA--> {startPoint} to {batchSize + startPoint} (of {fooAs.Count}) = {batch.Count}");
if (batch.Count == 0)
return;
gc.Cypher
.Unwind(batch, "faItem")
.Merge("(fa:FooA {Id: faItem.Id})")
.OnCreate().Set("fa = faItem")
.Merge("(fb:FooB {Id: faItem.RelA_FooBId})")
.Create("(fa)-[:RelA {Prop: faItem.RelA_Val1}]->(fb)")
.ExecuteWithoutResults();
AddFooAs(gc, fooAs, batchSize, startPoint + batch.Count);
}
var relBs = fooAList.SelectMany(a => a.ListB.Select(lb => lb));
public static void AddRelBs(IGraphClient gc, IList<RelB> relbs, int batchSize = 10000, int startPoint = 0)
{
var batch = relbs.Select(r => new { StartId = r.Start.Id, EndId = r.End.Id, r.ValExample }).Skip(startPoint).Take(batchSize).ToList();
Console.WriteLine($"RELB--> {startPoint} to {batchSize + startPoint} (of {relbs.Count}) = {batch.Count}");
if(batch.Count == 0)
return;
var query = gc.Cypher
.Unwind(batch, "rbItem")
.Match("(fb1:FooB {Id: rbItem.StartId}),(fb2:FooB {Id: rbItem.EndId})")
.Create("(fb1)-[:RelA {Prop: rbItem.ValExample}]->(fb2)");
query.ExecuteWithoutResults();
AddRelBs(gc, relbs, batchSize, startPoint + batch.Count);
}