【问题标题】:How to add multiple values to existing nodes with Cypher in Neo4J如何在 Neo4J 中使用 Cypher 向现有节点添加多个值
【发布时间】:2017-12-02 18:56:42
【问题描述】:

我正在尝试在 Neo4J 中加载一些数据。我有一个已经设置好的Person 节点。现在,这个节点需要有一个email 属性,它应该是一个数组(或集合)。基本上,email 属性需要有多个值,比如 -

email: ["abc@xyz.com", "abc@foo.com"]

我在这里遇到过类似的问题,但所有答案都表明要设置多个属性值在创建节点本身时。喜欢来自this 答案的这个查询 -

CREATE (e:Employee { name:"Sam",languages: ["C", "C#"]})
RETURN e

但我的问题是Person 节点已经创建,我现在需要在其上设置email 属性。

这是我必须加载的数据的一小部分 -

 Personid|email 
933|Mahinda933@hotmail.com 
933|Mahinda933@yahoo.com
933|Mahinda933@zoho.com 
1129|Carmen1129@gmail.com
1129|Carmen1129@gmx.com 
1129|Carmen1129@yahoo.com
4194|Ho.Chi4194@gmail.com 
4194|Ho.Chi4194@gmx.com

此外,数据来自包含数千行的 CSV 文件,因此我的查询需要是通用的,我无法为每个 Person 节点设置属性。

当我用这个子集测试电子邮件属性的创建时,我的第一次尝试是这样 -

 MATCH (n:TESTPERSON{id:933})
 SET n.email = "Mahinda933@hotmail.com"
 RETURN n

 MATCH (n:TESTPERSON{id:933})
 SET n.email = "Mahinda933@yahoo.com"
 RETURN n

正如我所想,这只是将email 属性覆盖为最近查询中的值。

查看此处和 Cypher 文档的答案后,我发现 Neo4J 允许您将数组/集合(相同类型的多个值)设置为属性值,然后我尝试了这个 -

 // CREATE test node
 CREATE (n:TESTPERSON{id:933})
 RETURN n

 // at this time, this node does not have any `email` property, so setup 
 // email as an array with one string value
 MATCH (n:TESTPERSON{id:933})
 SET n.email = ["Mahinda933@hotmail.com"]
 RETURN n


 // Now, using +=, I can append to the array of strings
 MATCH (n:TESTPERSON{id:933})
 SET n.email = n.email + "Mahinda933@yahoo.com"
 RETURN n

 // add a third value to array
 MATCH (n:TESTPERSON{id:933})
 SET n.email = n.email + "Mahinda933@zoho.com"
 RETURN n

这是结果 -

如您所见,email 属性现在有多个值。

但问题是,由于我的 CSV 文件有数千行,我需要一个通用查询来执行此操作。

我想根据文档here 使用CASE 语句,并尝试了这个 -

MATCH (n:TESTPERSON {id:933}) 
CASE 
WHEN n.email IS NULL THEN SET n.email = [ "Mahinda933@hotmail.com"] 
ELSE SET n.email = n.email + "Mahinda933@yahoo.com" 
RETURN n

但这只会引发错误 - mismatched input CASE expecting ;

我希望我可以将此查询用作我的 CSV 文件的通用方式 -

LOAD CSV WITH HEADERS FROM 'FILEURL' AS line FIELDTERMINATOR `|`
MATCH (n:TESTPERSON {id:toInt(line.Personid)}) 
CASE 
WHEN n.email IS NULL THEN SET n.email = [line.email] 
ELSE SET n.email = n.email + line.email 

但我什至不知道这是否可行,即使 CASE 错误已修复。

我真的很难过,如果有任何帮助,我将不胜感激。谢谢。

【问题讨论】:

    标签: csv neo4j cypher graph-databases


    【解决方案1】:

    您可以使用 COALESCE() 来使用默认值,以防您尝试获取的值为 null。你可以这样使用它:

    ... SET n.email = COALESCE(n.email, []) + "Mahinda933@yahoo.com" ...

    每当您将值数组设置为节点属性时,最好考虑是否可以将这些值建模为与原始节点有关系的单独节点。

    在这种情况下,:Email 节点与您的 :TESTPERSON 节点有某种关系,每封电子邮件有一个 :Email 节点,以及从 :TESTPERSON 到多个 :Email 的多个关系。

    这里的一个优势是您可以支持唯一性约束,如果您想确保系统中只有一个 :Email,并且如果您有索引,您将能够通过他们的电子邮件快速查找一个人或唯一约束,因为查询将使用索引来查找 :Email 并且从那里它只有一个关系遍历到电子邮件的所有者。

    当您在节点上的集合中有值时,您不能对集合中的值使用索引查找,因此您当前的模型将无法通过电子邮件快速查找人员。

    【讨论】:

    • 谢谢!这非常有效。这是我执行的查询 - LOAD CSV WITH HEADERS FROM "fileURL" AS line FIELDTERMINATOR '|' MATCH (n:TESTPERSON {id: toInt(line.Personid)}) SET n.email = COALESCE(n.email, []) + line.email
    【解决方案2】:

    快速解决方法是分两步加载数据

    1/ LOAD CSV,创建具有空数组属性的节点

    2/再次加载 CSV,设置电子邮件 +=

    3/ 可选,根据每个节点的数据,删除数组中的双精度数(使用自定义过程)。

    应该这样做。我对 CASE 语法也不是很满意

    【讨论】:

    • 谢谢!我正在考虑这样做,但事实证明你可以通过COALESCE 一次性完成。
    • 我倾向于忘记合并。这是一个更好的答案。
    【解决方案3】:

    使用MERGE试试这个解决方案:

    LOAD CSV WITH HEADERS FROM 'file:///p.csv' AS line FIELDTERMINATOR '|'
    MERGE (p:Person {id:toInteger(line.Personid)})
    ON CREATE SET p.mail = line.email
    ON MATCH SET p.mail = p.mail + '-' + line.email
    

    MERGE 命令处理重复节点,然后我们仅在使用ON CREATE SET 创建节点并且节点已经在数据库中(即ON MATCH SET)时设置属性,我们要将电子邮件地址添加到属性中。

    希望对您有所帮助。

    这是我在 Neo4j 中的结果:

    【讨论】:

    • 您好,谢谢您的回答!!我的代码已经有了Person 节点设置,所以我认为这行根本不会被执行-ON CREATE SET p.mail = line.email。这意味着p.mail 将是null 并且当它到达ON MATCH SET 子句时,代码ON MATCH SET p.mail = p.mail + '-' + line.email 将导致null,因为此时p.mail 仍然是null,并向@987654337 添加任何内容@ 将返回 null。此外,在屏幕截图中的代码中,email 是一个字符串属性,其中使用- 连接字符串,而我需要email 是一个字符串数组。想法?
    • 我现在看到了问题,谢谢!我认为您可以在我的代码中实现@InverseFalcon 答案,应该可以解决问题!
    猜你喜欢
    • 2014-03-04
    • 2014-04-15
    • 2020-03-04
    • 2017-03-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多