Neo4j:使用Cypher批量关联节点的最佳方法?

时间:2014-02-28 17:43:36

标签: neo4j

当我运行试图批量合并某些类型的所有节点的脚本时,我得到了一些奇怪的性能结果。

当合并2个节点集合(~42k)和(~26k)时,性能良好且快速。 但是当我合并(~42)和(5)时,性能DRAMATICALLY会降低。我正在批处理ParentNodes(所以(~42k)分批分批500个。为什么性能下降当我基本上合并较少的节点时(当批量设置相同时,但是源的批量设置为高,目标设置为低)?

关系查询:

MATCH (s:ContactPlayer)   
WHERE  has(s.ContactPrefixTypeId)    
WITH  collect(s) AS allP   
WITH  allP[7000..7500] as rangedP   
FOREACH  (parent in rangedP  |  
    MERGE (child:ContactPrefixType 
            {ContactPrefixTypeId:parent.ContactPrefixTypeId}
          )  
    MERGE (child)-[r:CONTACTPLAYER]->(parent)  
    SET r.ContactPlayerId = parent.ContactPlayerId ,      
        r.ContactPrefixTypeId = child.ContactPrefixTypeId  )

绩效结果:

流程启动

开始插入联系人项目 [+++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++ ++++++]


  • 42149联系方式的总时间:19176.87ms
  • 每批平均时间(500):213.4毫秒
  • 最长批次时间:663毫秒

开始插入ContactPlayer项目 [+++++++++++++++++++++++++++++++++++++++++++++++++ +++++++]


  • 27970 ContactPlayer项目的总时间:9419.2106ms
  • 每批平均时间(500):167.75ms
  • 最长批次时间:689毫秒

开始将Contact与ContactPlayer联系起来 [+++++++++++++++++++++++++++++++++++++++++++++++++ +++++++]


  • 将联系人与ContactPlayer关联的总时间:7907.4877ms
  • 每批平均时间(500):141.151517857143ms
  • 最长批次时间:批号为883.0918ms:0

开始插入ContactPrefixType项目
[+]


  • 5个ContactPrefixType项目的总时间:22.0737ms
  • 每批平均时间(500):22毫秒
  • 最长批次时间:22毫秒

已插入联系人数据。

开始将ContactPrefixType与Contact联系起来 [+++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++ ++++++]


  • 将ContactPrefixType与联系人联系起来所需的总时间:376540.8309ms
  • 每批平均时间(500):4429.78643647059ms
  • 最长批次时间:批号为14263.1843ms:63

2 个答案:

答案 0 :(得分:0)

您可以将ID作为参数传递而不是从图中获取它们吗?查询可能看起来像

MATCH (s:ContactPlayer {ContactPrefixTypeId:{cptid})
MERGE (c:ContactPrefixType {ContactPrefixTypeId:{cptid})
MERGE c-[:CONTACT_PLAYER]->s

如果您使用REST API Cypher资源,我认为该实体应该类似于

{
    "query":...,
    "params": {
        "cptid":id1
    }
}

如果您使用事务性端点,它应该看起来像这样。您可以通过每次调用中的语句数量以及提交前的调用次数来控制事务大小。更多here

{
    "statements":[
        "statement":...,
        "parameters": {
            "cptid":id1
        },
        "statement":...,
        "parameters": {
            "cptid":id2
        }
    ]
}

答案 1 :(得分:0)

到目前为止,我能想到的最好的是以下(这是一个特定于我的环境的黑客攻击):

如果/ Else条件:

If childrenNodes.count() < 200 - &gt;假设它们是父类的类型标识符...即ContactPrefixType

否则假设它是用于将多个项目类型关联在一起的矩阵(即ContactAddress)

If childNodes < 200

MATCH (parent:{parentLabel}), 
(child:{childLabel} {{childLabelIdProperty}:parent.{parentRelationProperty}})
CREATE child-[r:{relationshipLabel}]->parent

每种关系类型需要3-5秒才能完成

Else

MATCH (child:{childLabel}), 
(parent:{parentLabel} {{parentPropertyField : child.{childLabelIdProperty}})
WITH collect(parent) as parentCollection, child
WITH parentCollection[{batchStart}..{batchEnd}] as coll, child
FOREACH (parent in coll | 
CREATE child-[r:{relationshipLabel}]-parent )

我不确定这是否是最有效的方法,但在尝试了许多不同的选项之后,这似乎是最快的。

统计:

  1. 插入225,018个节点,其中包含2,070,977个属性
  2. 创建464,606个关系
  3. 总计:331秒。

    因为这是一个直接的导入,我还没有处理更新,我假设所有关系都是正确的,不需要担心无效数据...但是,我会尝试设置属性为关系类型以便以后能够执行清理功能(即将关系类型中的父和子ID存储为属性以供以后参考)

    如果有人可以改进,我会喜欢它。