Neo4j 逗号分隔匹配和多重匹配之间的区别

Neo4j 逗号分隔匹配和多重匹配之间的区别,neo4j,cypher,Neo4j,Cypher,上下文:我们正在Neo4J数据库中导入csv文件。 我们遇到了批处理的性能问题 CALL apoc.periodic.iterate(' CALL apoc.load.csv(\'file:///myfile.csv\') yield map as row return row ',' WITH row.Source AS sourceName, row.Quote AS quoteName, row.Type AS type, row.CurveIndex AS

上下文:我们正在Neo4J数据库中导入csv文件。
我们遇到了批处理的性能问题

CALL apoc.periodic.iterate('
CALL apoc.load.csv(\'file:///myfile.csv\') yield map as row return row
','
WITH
    row.Source AS sourceName,
    row.Quote AS quoteName,
    row.Type AS type,
    row.CurveIndex AS curveIndexName,
    row.Entity AS entityName,
    row.FreeParam AS freeParamName,
    row.Location AS locationName,
    row.Maturity AS maturityName,
    row.DepMaturity AS depMaturityName,
    row.Seniority AS seniorityName,
    row.Tray AS trayName,
    row.VolatilityModel AS volatilityModelName
MATCH (marketData:MarketData {source:sourceName, quoteName:quoteName, type:type})
OPTIONAL MATCH  (curveIndex:CurveIndex {name:curveIndexName})
OPTIONAL MATCH  (entity:Entity {name:entityName})
OPTIONAL MATCH  (maturity:Maturity {name:maturityName})
OPTIONAL MATCH  (depMaturity:Maturity {name:depMaturityName})
OPTIONAL MATCH  (seniority:Seniority {name:seniorityName})
OPTIONAL MATCH  (tray:Tray {name:trayName})
OPTIONAL MATCH  (volatilityModel:VolatilityModel {name:volatilityModelName})

FOREACH (f1 IN CASE WHEN curveIndex IS NULL THEN [] ELSE [curveIndex] END |
    MERGE (marketData)-[:CURVE_INDEX]->(curveIndex))
FOREACH (f1 IN CASE WHEN entity IS NULL THEN [] ELSE [entity] END |
    MERGE (marketData)-[:ENTITY]->(entity))
FOREACH (f1 IN CASE WHEN maturity IS NULL THEN [] ELSE [maturity] END |
    MERGE (marketData)-[:MATURITY]->(maturity))
FOREACH (f1 IN CASE WHEN depMaturity IS NULL THEN [] ELSE [depMaturity] END |
    MERGE (marketData)-[:DEP_MATURITY]->(depMaturity))
FOREACH (f1 IN CASE WHEN seniority IS NULL THEN [] ELSE [seniority] END |
    MERGE (marketData)-[:SENIORITY]->(seniority))
FOREACH (f1 IN CASE WHEN tray IS NULL THEN [] ELSE [tray] END |
    MERGE (marketData)-[:TRAY]->(tray))
FOREACH (f1 IN CASE WHEN volatilityModel IS NULL THEN [] ELSE [volatilityModel] END |
    MERGE (marketData)-[:VOLATILITY_MODEL]->(volatilityModel))
', {batchSize:1000, iterateList:true, parallel:true, concurrency:4});
此命令使用大量内存(许多停止world gc,有些大于10s),并且在900k行文件上执行需要数小时

经过几次尝试后,我们发现此命令的效率更高:

CALL apoc.periodic.iterate('
CALL apoc.load.csv(\'file:///myfile.csv\') yield map as row return row
','
WITH
    row.Source AS sourceName,
    row.Quote AS quoteName,
    row.Type AS type,
    row.CurveIndex AS curveIndexName,
    row.Entity AS entityName,
    row.FreeParam AS freeParamName,
    row.Location AS locationName,
    row.Maturity AS maturityName,
    row.DepMaturity AS depMaturityName,
    row.Seniority AS seniorityName,
    row.Tray AS trayName,
    row.VolatilityModel AS volatilityModelName
MATCH (marketData:MarketData {source:sourceName, quoteName:quoteName, type:type})
OPTIONAL MATCH  (curveIndex:CurveIndex {name:curveIndexName}),
                (entity:Entity {name:entityName}),
                (maturity:Maturity {name:maturityName}),
                (depMaturity:Maturity {name:depMaturityName}),
                (seniority:Seniority {name:seniorityName}),
                (tray:Tray {name:trayName}),
                (volatilityModel:VolatilityModel {name:volatilityModelName})

FOREACH (f1 IN CASE WHEN curveIndex IS NULL THEN [] ELSE [curveIndex] END |
    MERGE (marketData)-[:CURVE_INDEX]->(curveIndex))
FOREACH (f1 IN CASE WHEN entity IS NULL THEN [] ELSE [entity] END |
    MERGE (marketData)-[:ENTITY]->(entity))
FOREACH (f1 IN CASE WHEN maturity IS NULL THEN [] ELSE [maturity] END |
    MERGE (marketData)-[:MATURITY]->(maturity))
FOREACH (f1 IN CASE WHEN depMaturity IS NULL THEN [] ELSE [depMaturity] END |
    MERGE (marketData)-[:DEP_MATURITY]->(depMaturity))
FOREACH (f1 IN CASE WHEN seniority IS NULL THEN [] ELSE [seniority] END |
    MERGE (marketData)-[:SENIORITY]->(seniority))
FOREACH (f1 IN CASE WHEN tray IS NULL THEN [] ELSE [tray] END |
    MERGE (marketData)-[:TRAY]->(tray))
FOREACH (f1 IN CASE WHEN volatilityModel IS NULL THEN [] ELSE [volatilityModel] END |
    MERGE (marketData)-[:VOLATILITY_MODEL]->(volatilityModel))
', {batchSize:1000, iterateList:true, parallel:true, concurrency:4});
它使用更少的内存,大约在50秒内执行

我不确定是否真正理解这两个命令之间的区别。
有人能解释吗?

这两个查询做的不是同一件事,尽管对于您的特定数据,您可能会得到相同的结果(只要您在
可选匹配中匹配的所有内容总是返回一些内容)

OPTIONAL MATCH
基本上是说“如果模式I
OPTIONAL MATCH
ing不存在,只需将
nulls
替换从
OPTIONAL MATCH
中的节点和关系派生的任何内容。”

MATCH
OPTIONAL MATCH
之后的逗号分隔块都只是要找到的一个模式-我们没有隐式地为找到的每个逗号执行另一个
MATCH
,而是找到符合整个模式的内容(并返回它),或者不符合(并为所提到的任何内容返回null)

就你而言:

  • 第一个查询将找到与CSV行匹配的
    MarketData
    节点,然后可以选择依次匹配其他节点类型,如果找到,则在该节点和
    MarketData
    节点之间创建关系。
    • 因为您是一次对每个节点标签进行
      可选匹配
      ,如果缺少任何标签,您只需为该节点变量获取一个
      null
      ,并且由于您的
      FOREACH
      技巧,您将跳过创建关系
  • 第二个查询将找到一个与第一个相同的CSV行相匹配的
    MarketData
    节点,但它要么满足整个模式(找到一个与该行匹配的
    CurveIndex
    ,一个与之匹配的
    Maturity
    ,以及一个与之匹配的
    Tray
    )返回这些值,否则,它将无法找到所有这些值,并为所有值返回空值
如果您熟悉SQL,就好像您在使用逗号时将CSV行
OUTER JOIN
添加到内部联接数据的子选择中,而不是查询1,查询1只
OUTER JOIN
按顺序排列每个“表”。在第二个示例中,如果有任何内容不匹配,您的
内部联接将不会返回任何行,因此您将只剩下CSV行,而没有其他内容

我的感觉是,您可能在第二个查询中创建了更少的节点和关系,因为如果从
实体
挥发模型
的任何内容不匹配,则
可选匹配
将不会返回任何内容

让我们用一些虚拟数据做一个简化的示例,只包含MarketData、CurveIndex和实体节点:

MERGE (m: MarketData { quoteName: 'MSFT' })
MERGE (curveIndex: CurveIndex { name: 'SomeCurveIndex' })
MERGE (entity: Entity { name: 'MSFT Entity' })
我们将在内存中伪造CSV,暂时避免所有的批处理位和BOB。首先,当CSV中的所有内容都匹配时,让我们只返回匹配的MarketData、CurveIndex和Entity节点,使用您的第一个查询来证明它是有效的:

WITH { Quote: 'MSFT', CurveIndex: 'SomeCurveIndex', Entity: 'MSFT Entity' } as row
WITH
    row.Quote AS quoteName,
    row.CurveIndex AS curveIndexName,
    row.Entity AS entityName
MATCH (marketData: MarketData { quoteName: quoteName })
OPTIONAL MATCH (curveIndex: CurveIndex { name: curveIndexName })
OPTIONAL MATCH (entity: Entity { name: entityName })
RETURN marketData, curveIndex, entity

因为所有数据都存在于CSV和数据库中已有的参考数据中,所以我们将返回三个节点。现在,让我们尝试更改实体名称,看看会发生什么:

WITH { Quote: 'MSFT', CurveIndex: 'SomeCurveIndex', Entity: 'Some Other Entity' } as row
WITH
    row.Quote AS quoteName,
    row.CurveIndex AS curveIndexName,
    row.Entity AS entityName
MATCH (marketData: MarketData { quoteName: quoteName })
OPTIONAL MATCH (curveIndex: CurveIndex { name: curveIndexName })
OPTIONAL MATCH (entity: Entity { name: entityName })
RETURN marketData, curveIndex, entity

完美-我们得到可以匹配的位,而不能匹配的位得到空值

现在,让我们尝试将这两个
可选MATCH
子句连接起来,看看效果如何:

WITH { Quote: 'MSFT', CurveIndex: 'SomeCurveIndex', Entity: 'Some Other Entity' } as row
WITH
    row.Quote AS quoteName,
    row.CurveIndex AS curveIndexName,
    row.Entity AS entityName
MATCH (marketData: MarketData { quoteName: quoteName })
OPTIONAL MATCH (curveIndex: CurveIndex { name: curveIndexName }), (entity: Entity { name: entityName })
RETURN marketData, curveIndex, entity


Ruh roh-尽管我们找到了一个匹配的
曲线索引
,但我们没有返回它,因为我们无法同时找到一个匹配的
实体

这两个查询做的事情并不相同,尽管对于您的特定数据,您可能会得到相同的结果(只要您在
可选匹配中匹配的所有内容始终返回某些内容)

OPTIONAL MATCH
基本上是说“如果模式I
OPTIONAL MATCH
ing不存在,只需将
nulls
替换从
OPTIONAL MATCH
中的节点和关系派生的任何内容。”

MATCH
OPTIONAL MATCH
之后的逗号分隔块都只是要找到的一个模式-我们没有隐式地为找到的每个逗号执行另一个
MATCH
,而是找到符合整个模式的内容(并返回它),或者不符合(并为所提到的任何内容返回null)

就你而言:

  • 第一个查询将找到与CSV行匹配的
    MarketData
    节点,然后可以选择依次匹配其他节点类型,如果找到,则在该节点和
    MarketData
    节点之间创建关系。
    • 因为您是一次对每个节点标签进行
      可选匹配
      ,如果缺少任何标签,您只需为该节点变量获取一个
      null
      ,并且由于您的
      FOREACH
      技巧,您将跳过创建关系
  • 第二个查询将找到一个与第一个相同的CSV行相匹配的
    MarketData
    节点,但它要么满足整个模式(找到一个与该行相匹配的
    CurveIndex
    ,一个与之匹配的
    Maturity
    ,以及一个与之匹配的
    Tray
    )返回这些值,否则它将无法找到所有这些值并返回空值
如果您熟悉SQL,就好像您在使用逗号时将CSV行
OUTER JOIN
连接到内部连接数据的子集,而不是查询1,查询1只是
OUTER JOIN
按顺序连接每个“表”。在第二个示例中,如果有任何不匹配的内容,则
internal JOIN
将不返回任何行,因此您将e只剩下CSV行,其他什么都没有

我的感觉是
OPTIONAL MATCH (curveIndex:CurveIndex {name:curveIndexName})
FOREACH (f1 IN CASE WHEN curveIndex IS NULL THEN [] ELSE [curveIndex] END |
  MERGE (marketData)-[:CURVE_INDEX]->(curveIndex))
FOREACH (ci IN [(c:CurveIndex {name:curveIndexName})-[*0..0]-()|c] |
  MERGE (marketData)-[:CURVE_INDEX]->(ci))
:MarketData(sourceName, quoteName, type)
:CurveIndex(name)