如何在Neo4J中使用Cypher为现有节点添加多个值

3
我正在尝试在Neo4J中加载一些数据。我已经设置好了一个名为Person的节点。现在,这个节点需要具有一个email属性,它应该是一个数组(或集合)。基本上,email属性需要具有多个值,如 -
email: ["abc@xyz.com", "abc@foo.com"]

我在这里看到过类似的问题,但所有答案都表明要在创建节点本身的时候设置多个属性值。就像这篇答案中的查询一样 -

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

这是结果 - enter image description here 如您所见,email属性现在具有多个值。
但问题在于,由于我的CSV文件有数千行,因此我需要一个通用查询来完成这项任务。
我考虑使用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错误被修复,我也不确定这是否有效。

我真的被卡住了,希望能得到任何帮助。谢谢。

3个回答

5
您可以使用COALESCE()函数,在您要获取的值为空时使用默认值。您可能会像这样使用它:
... SET n.email = COALESCE(n.email, []) + "Mahinda933@yahoo.com" ...
每当您将一组值设置为节点属性时,最好考虑是否可以将其建模为与原始节点具有关系的单独节点。
在这种情况下,每个电子邮件都有一个:Email节点,并且从:TESTPERSON到多个:Emails有多个关系。
这里的优点是,如果您想要确保系统中只有一个: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 - Manish Giri

0

一个快速的解决方案是分两步加载数据

1/ 用LOAD CSV创建一个带空数组属性的节点

2/ 再次使用LOAD CSV,设置电子邮件+=

3/ 根据每个节点的数据,可选地从数组中删除重复项(使用自定义过程进行处理)。

应该可以解决问题。我也不是很喜欢CASE语法。


谢谢!我之前一直考虑用另一种方式实现,但后来发现可以使用“COALESCE”在一次遍历中解决问题。 - Manish Giri
我经常忘记使用COALESCE函数,它是更好的解决方案。 - Jerome_B

0

尝试使用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中得到的结果: enter image description here


你好,感谢您的回答!我的代码已经设置了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,并且将任何内容添加到null将返回null。 此外,在您的屏幕截图中的代码中,email是一个字符串属性,其中使用“-”连接字符串,而我需要email成为字符串数组。有什么想法吗? - Manish Giri
我现在看到问题了,谢谢!我认为你可以在我的代码中实现@InverseFalcon的答案,应该可以解决问题! - Fabio Lamanna

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接