github链接:
本文演示了将CSV数据导入Neo4j的不同方法以及该过程中可能出现的潜在问题的解决方案。
在导入数据之前,您应该熟悉什么是图形数据库,如何构建属性图数据模型以及Cypher查询语言的一些基础知识。所有这些技能都是数据导入过程的一部分。中间
CSV是一个用逗号分隔的值的文件,通常在Excel或其他电子表格工具中查看。可以使用其他类型的值作为定界符,但最标准的是逗号。如今,许多系统和流程已经将其数据转换为CSV格式,以便将文件输出到其他系统,人性化的报告以及其他需求。这是人类和系统已经熟悉的使用和处理的标准文件格式。
为Neo4j提供读取和加载CSV文件的功能有助于减少将数据从各种格式和系统导入Neo4j的麻烦。
有几种方法可以将CSV数据导入Neo4j,每种方法都有不同的标准和功能。您选择的选项将取决于数据集的大小,以及您对各种工具的舒适程度。
让我们看看Neo4j可以读取和导入CSV文件的一些方式。
LOAD CSV
Cypher命令:此命令是一个很好的起点,可以处理中小型数据集(最多1000万条记录)。neo4j-admin
批量导入工具:命令行工具,用于直接加载大型数据集。我们将简要介绍这些工具中的每一个,它们的操作方式以及如何开始使用一般用例。每个文档的更多文档和信息也将包括在内,以在更复杂的情况下提供帮助。数据质量也可能是任何类型的数据导入任何系统的问题,因此我们将介绍其中一些潜在的困难以及如何解决它们。
该LOAD CSV
子句是Cypher查询语言的一部分。我们的Cypher手册包含一个专门介绍其用法的页面,Neo4j的各种博客,视频,解决方案和其他材料都使用此命令。它使用简单并且广泛适用。 LOAD CSV
不只是您的基本数据提取机制,还因为它将多个方面组合到一个操作中。
为了更好地控制,您可以LOAD CSV 使用cypher-shell 而不是在浏览器中运行命令。有关更多信息,请参见Cypher shell的手册页。 |
LOAD CSV
可以处理本地和远程文件,并且每个文件都有一些语法。这很容易错过,最终会导致访问错误,因此我们将在此处尝试阐明规则。
本地文件file:///
在文件名之前带有前缀。Neo4j安全性有一个默认设置,即只能从Neo4j导入目录中读取本地文件,这取决于您的操作系统。Neo4j操作手册中列出了每个操作系统的文件位置。我们建议将文件放在Neo4j的import目录中,因为这样可以保护环境安全。但是,如果您需要访问其他位置的文件,则可以在我们的手册中找到要更改的设置。
//Example 1 - file directly placed in import directory (import/data.csv)
LOAD CSV FROM "file:///data.csv"
//Example 2 - file placed in subdirectory within import directory (import/northwind/customers.csv)
LOAD CSV FROM "file:///northwind/customers.csv"
网络托管文件可以直接使用其URL进行引用,例如https://host/path/data.csv
。但是,必须设置权限,以便外部源可以读取文件。有关与在线文件导入相关的访问的更多信息,请参见此知识库文章。
//Example 1 - website
LOAD CSV FROM 'https://neo4j.com/docs/cypher-manual/3.5/csv/artists.csv'
//Example 2 - Google
LOAD CSV WITH HEADERS FROM 'https://docs.google.com/spreadsheets/d/'
需要牢记一些事情,LOAD CSV
以及一些有用的技巧来处理您可能会遇到的各种数据方案。
toInteger()
,toFloat()
,split()
或类似功能的转换数值。Cypher具有一些清理和转换功能,可帮助清除数据。这些对于处理丢失的数据或将字段拆分为图形的多个值非常有用。
首先,请记住Neo4j不存储空值。可以跳过CSV文件中的空字段或将其替换为中的默认值LOAD CSV
。
//skip null values
LOAD CSV WITH HEADERS FROM 'file:///data.csv' AS row
WITH row WHERE row.Company IS NOT NULL
MERGE (c:Company {companyId: row.Id})
//set default for null values
LOAD CSV WITH HEADERS FROM 'file:///data.csv' AS row
MERGE (c:Company {companyId: row.Id, hqLocation: coalesce(row.Location, "Unknown")})
//change empty strings to null values (not stored)
LOAD CSV WITH HEADERS FROM 'file:///data.csv' AS row
MERGE (c:Company {companyId: row.Id})
SET c.emailAddress = CASE trim(row.Email) WHEN "" THEN null ELSE row.Email END
接下来,如果您在CSV中有一个要拆分的项目列表的字段,则可以使用Cypher split()
函数来分隔单元格中的数组。
//split string of employee skills into separate nodes
LOAD CSV FROM 'file:///data.csv' AS row
MERGE (e:Employee {employeeId: row.Id})
UNWIND split(row.skills, ',') AS skill
MERGE (s:Skill {name: skill})
MERGE (e)-[r:HAS_EXPERIENCE]->(s);
可以使用条件转换CASE
。当我们检查空值或空字符串时,您看到了一个示例,但让我们看另一个示例。
//set businessType property based on shortened value in CSV
LOAD CSV WITH HEADERS FROM 'file:///data.csv' AS row
WITH row,
(CASE row.BusinessType
WHEN 'P' THEN 'Public'
WHEN 'R' THEN 'Private'
WHEN 'G' THEN 'Government'
ELSE 'Other' END) AS type
MERGE (c:Company {companyId: row.CompanyId})
SET c.businessType = type
RETURN *
通常,有多种方法可以提高数据加载期间的性能,这在处理大量数据或复杂加载时特别有用。
为了改善在图形中插入或更新唯一实体(使用更新MERGE
或MATCH
使用更新),可以为计划合并或匹配的每个标签和属性创建声明的索引和约束。
为了获得最佳性能,总是MATCH 和MERGE 与编索引的主键属性的单个标签上。 |
您还应该将节点和关系创建分为单独的语句。例如,代替以下内容:
MERGE (e:Employee {employeeId: row.employeeId})
MERGE (c:Company {companyId: row.companyId})
MERGE (e)-[r:WORKS_FOR]->(c)
您可以这样写:
LOAD CSV WITH HEADERS FROM 'file:///data.csv' AS row
MERGE (e:Employee {employeeId: row.employeeId})
RETURN count(e);
LOAD CSV WITH HEADERS FROM 'file:///data.csv' AS row
MERGE (c:Company {companyId: row.companyId})
RETURN count(c);
LOAD CSV WITH HEADERS FROM 'file:///data.csv' AS row
MATCH (e:Employee {employeeId: row.employeeId})
MATCH (c:Company {companyId: row.companyId})
MERGE (e)-[r:WORKS_FOR]->(c)
RETURN count(*);
这样,负载一次只执行一次导入,并且可以快速而有效地遍历大量数据,从而减少了繁重的处理。
当要加载的数据量太大而无法容纳到内存中时,可以使用两种不同的方法来防止在数据加载期间内存不足。
PERIODIC COMMIT
。可以在该LOAD CSV
子句之前添加此子句,以告诉Cypher在清除内存和事务状态之前仅处理文件的这么多行。欲了解更多信息,请参见手册页上PERIODIC COMMIT
。USING PERIODIC COMMIT 500
LOAD CSV WITH HEADERS FROM 'file:///data.csv' AS row
MERGE (pet:Pet {petId: row.PetId})
MERGE (owner:Owner {ownerId: row.OwnerId})
ON CREATE SET owner.name = row.OwnerName
MERGE (pet)-[r:OWNED_BY]->(owner)
PROFILE
对查询运行,以查看它们是否使用EAGER加载并修改查询或对同一文件运行多次传递,因此它不会这样做。可在Mark的博客文章中找到有关EAGER加载以及如何避免的更多信息。neo4j.conf
:
dbms.memory.heap.initial_size
和dbms.memory.heap.max_size
:设置为至少4G。dbms.memory.pagecache.size
:理想情况下,其值应足以将整个数据库保留在内存中。加载CSV资源
LOAD CSV
非常适合导入中小型数据(最多1000万条记录)。对于大于此的数据集,我们可以访问命令行批量导入器。该neo4j-admin import
工具允许您通过指定节点文件和关系文件将CSV数据导入到空数据库中。
我们要使用它将订单数据导入Neo4j:客户,订单和订购的产品。
该工具位于中
,并按以下方式使用:
bin/neo4j-admin import --id-type=STRING \
--nodes:Customer=customers.csv --nodes=products.csv \
--nodes="orders_header.csv,orders1.csv,orders2.csv" \
--relationships:CONTAINS=order_details.csv \
--relationships:ORDERED="customer_orders_header.csv,orders1.csv,orders2.csv"
用于此导入的前几行数据如下所示:
customerId:ID(客户) | 名称 |
---|---|
23 |
熟食公司 |
42 |
美味面包店 |
productId:ID(产品) | 名称 | 价钱 | :标签 |
---|---|---|---|
11 |
巧克力 |
10 |
产品;食品 |
orderId:ID(订单) | 日期 | 总 | customerId:IGNORE |
---|---|---|---|
1041 |
2015-05-10 |
130 |
23 |
1042 |
2015-05-12 |
20 |
42 |
:START_ID(订购) | 量 | 价钱 | :END_ID(产品) |
---|---|---|---|
1041 |
13 |
130 |
11 |
1042 |
2 |
20 |
11 |
:END_ID(订购) | 日期:IGNORE | 总计:IGNORE | :START_ID(客户) |
---|---|---|---|
1041 |
2015-05-10 |
130 |
23 |
1042 |
2015-05-12 |
20 |
42 |
如果调用bin/neo4j-admin import 不带参数的话,它将列出一个全面的帮助页面。 |
重复的--nodes
和--relationships
参数是同一实体(即具有相同列结构)的多个(可能是拆分的)CSV文件的组。
每组中的所有文件都被视为可以串联为一个大文件。甲标题行中的组的所述第一文件或在一个单独的,单行文件是必需的。将标头放在单独的文件中,比将其包含在数GB的文本文件中更容易处理和编辑。还支持压缩文件。
--id-type=STRING
指示所有:ID
列包含字母数字值(有用于纯数字ID的优化)。customers.csv
直接将其作为带有:Customer
标签的节点导入,并将属性直接从文件中获取。Product
节点遵循从:LABEL
列中获取节点标签的相同模式。Order
一个头和两个内容文件-节点由3个文件拍摄。:CONTAINS
的order_details.csv
订单项关系是通过创建的,通过ID将订单与包含的产品相关联。列名称用于节点和关系的属性名称。在特定的列上有特定的标记,我们将对其进行解释。
name:ID
–全局ID列,用于在以后重新连接时查找节点。
--id-type
所指的。:ID(Order)
。:LABEL
–节点的标签列。多个标签可以用定界符分隔。:START_ID
,:END_ID
–涉及节点ID的关系文件列。对于id组,请使用:END_ID(Order)
。:TYPE
–指定关系类型的列。:IGNORE
。:INT
,:BOOLEAN
等等。有关此标头格式和工具的更多详细信息,请参见Neo4j手册中的文档以及随附的教程。
现实世界的数据是混乱的。每次使用数据时,您都会看到一些需要清除或转换的值,然后才能将其移至另一个系统。较小的语法错误,格式说明,一致性或正确的引用,甚至对数据要求或标准的不同假设,都很容易造成大量的清理工作。
我们将重点介绍将数据从其他系统加载到Neo4j时容易遗漏的一些数据质量问题,并尝试帮助避免数据导入和清理问题。
标头与数据不一致(缺少,列太多,标 头中的分隔符不同)验证标头与文件中的数据匹配。在此阶段调整格式,定界符,列等将在以后节省大量时间。
整个文件中多余或缺少的引号如果 未加引号的文本中间有独立的双引号或单引号,或者带引号的文本中未转义的引号会导致读取文件以加载的问题。最好是转义或删除流浪引号。Cypher样式指南和知识库文章中提供了正确转义的文档。
文件中的 特殊字符或换行符处理文件中的任何特殊字符时,请确保将其引号或将其删除。对于带引号或不带引号的字段中的换行符,请为其添加引号或将其删除。
换行符不一致 计算机处理不好的一件事是数据不一致。确保换行符始终一致。我们建议选择Unix风格以与Linux系统兼容(导入工具的通用格式)。
二进制零,文件开头的BOM字节顺序标记(2个UTF-8字节)或其他非文本字符 任何不寻常的字符或特定于工具的格式(Excel或Word)有时都隐藏在应用程序工具中,但在基本编辑器。如果您在文件中遇到这些类型的字符,最好将其完全删除。
如上所述,某些应用程序具有特殊的格式以使文档看起来不错,但是这种隐藏的额外代码无法由常规文件阅读器和脚本处理。其他时候,很难找到小的语法更改或对包含大量数据的文件进行广泛的调整。
为了处理这些类型的情况或常规数据清除,有许多工具可帮助您检查和验证CSV数据文件。
基本工具(例如hexdump,vi,emacs,UltraEdit和Notepad ++)可以很好地处理基于快捷方式的命令,以编辑和处理文件。但是,还有其他更有效或用户友好的选项可用于数据清除和格式化。
LOAD CSV
而不创建图形结构只会输出样本,计数或分布,从而有可能检测不正确的标题列计数,定界符,引号,转义符或标题名称拼写。// assert correct line count
LOAD CSV FROM "file-url" AS line
RETURN count(*);
// check first 5 line-sample with header-mapping
LOAD CSV WITH HEADERS FROM "file-url" AS line
RETURN line
LIMIT 5;