使用 CSV 文件
本页介绍了一些在将 CSV 文件导入 Neo4j 之前需要考虑的优化点。
CSV 文件的结构
CSV 文件中包含多个元素,可以帮助您理解即将导入并最终建模的数据。
数据格式
Neo4j 将 CSV 文件中的所有数据读取为string
类型。对于其他数据类型,您需要使用toInteger()
、toFloat()
、toBoolean()
或类似函数将数据转换为适当的类型。另请记住,标签、属性名、关系类型和变量是区分大小写的。
有关如何操作数据格式的更多信息,请参阅转换数据值。
字段分隔符
字段分隔符,也称为分隔符,是用于分隔 CSV 文件中每个字段的字符。在此示例中,使用了逗号 (,
),但其他字符,如制表符 (\t
) 或竖线 (|
) 也可以使用,并且可以混合使用。
personId,name,birthYear
23945,"Gerard Pires"|1942
553509,"Helen Reddy"|1941
113934,"Susan Flannery"|1939
您可以将示例复制并粘贴到任何文本编辑器(例如记事本或 TextEdit)或电子表格应用程序(例如 Excel 或 Google 表格)中,然后将其保存为 CSV 文件。 |
表头
表头通常是 CSV 文件的第一行。它们不是强制性的,但添加它们是一个好习惯。在上一个示例中,表头是
personId,name,birthYear
如果您的 CSV 文件没有表头行,您需要知道列的顺序并使用索引引用它们。这会使数据处理比实际需要更复杂。
引号
引号 ("
) 定义应作为单个值存储的文本。例如
personId,name,birthYear
23945,"Pires, Gerard",1942
553509,"Reddy, Helen",1941
113934,"Flannery, Susan",1939
此 CSV 文件使用逗号作为字段分隔符,在第二行names
中,像Pires, Gerard
这样的条目也包含逗号。如果条目Pires, Gerard
没有用引号括起来,它将被视为两个不同的条目(即一个用于Pires
,另一个用于Gerard
)。
范式化数据
如果源数据是范式化的(例如,从关系数据模型导出时),通常会有多个 CSV 文件。每个 CSV 文件代表关系数据模型中的一个表,并且文件之间通过唯一 ID 相互关联。
在此范式化数据示例中,有三个人员、电影和角色文件
person.csv
personId,name,birthYear
23945,Gerard Pires,1942
553509,Helen Reddy,1941
113934,Susan Flannery,1939
movies.csv
movieId,title,avgVote,releaseYear,genres
189,Sin City,8.000000,2005,Crime|Thriller
2300,The Fifth Element,7.700000,1997,Action|Adventure|Sci-Fi
11969,Tombstone,7.800000,1993,Action|Romance|Western
roles.csv
personId,movieId,character
2295,189,Marv
56731,189,Nancy
16851,189,Dwight
请注意,person.csv
文件为每个人都有一个唯一 ID,movies.csv
文件为每部电影都有一个唯一 ID。roles.csv
文件将人员与电影关联起来并提供角色。它对应于图中的关系,因为它包含将节点绑定在一起所需的链接。
非范式化数据
非范式化数据通常表示来自多个表的数据。如果源数据是非范式化的,通常只有一个 CSV 文件包含所有数据,其中实体之间存在关系时数据常常重复。例如
movies-n.csv
movieId,title,avgVote,releaseYear,genres,personType,name,birthYear,character
2300,The Fifth Element,7.700000,1997,Action|Adventure|Sci-Fi,ACTOR,Bruce Willis,1955,Korben Dallas
2300,The Fifth Element,7.700000,1997,Action|Adventure|Sci-Fi,ACTOR,Gary Oldman,1958,Jean-Baptiste Emanuel Zorg
2300,The Fifth Element,7.700000,1997,Action|Adventure|Sci-Fi,ACTOR,Ian Holm,1931,Father Vito Cornelius
11969,Tombstone,7.800000,1993,Action|Romance|Western,ACTOR,Kurt Russell,1951,Wyatt Earp
11969,Tombstone,7.800000,1993,Action|Romance|Western,ACTOR,Val Kilmer,1959,Doc Holliday
11969,Tombstone,7.800000,1993,Action|Romance|Western,ACTOR,Sam Elliott,1944,Virgil Earp
在这里,电影和人员数据(包括 ID)在每次出现关于某个演员角色的新信息时都会在不同行中重复。这种重复会损害图数据结构。在这种情况下,建议在导入前准备好文件。
文件位置
使用LOAD CSV
命令加载 CSV 数据时,CSV 文件通过 URL 访问,无论是通过互联网
LOAD CSV WITH HEADERS
FROM 'https://data.neo4j.com/importing-cypher/people.csv' AS row
RETURN row
或者,如果您使用本地部署,则从本地文件夹访问。在这种情况下,您需要在文件名之前添加file:///
前缀
LOAD CSV WITH HEADERS
FROM 'file:///people.csv' AS row
RETURN row
出于安全原因,默认情况下,本地文件只能从 Neo4j 导入目录读取,该目录根据您的操作系统位于不同位置。有关更多信息,请参阅操作 → 文件位置。
如果您想从其他位置打开 CSV 文件,您需要更改server.directories.import
设置。
安全
强烈建议只允许通过 HTTPS 等安全协议加载资源,而不是 HTTP 等不安全协议。这可以通过将加载权限限制为仅使用安全协议的受信任来源来实现。
如果无法避免允许不安全协议,您需要在配置设置server.jvm.additional
中添加 JVM 参数-Dsun.net.http.allowRestrictedHeaders=true
,以避免 Neo4j 内置安全检查的自动阻止。
文件准备
在导入 CSV 数据之前,您应该考虑数据的来源。它可以来自
-
关系型数据库
-
Web API
-
公共数据目录
-
BI 工具
-
电子表格(例如 Excel 或 Google 表格)
大多数数据系统都提供将数据导出为 CSV 文件的选项,因为这是一种常见的数据交换格式。然而,实际数据通常很混乱,这意味着某些值在导入到另一个系统之前需要清理或转换。
以下是一些您可能会遇到的常见问题:
-
源文件包含的数据超出您的需求
例如,如果您只对某个导演及其参与的电影感兴趣,电影数据集中包含大量与您无关的数据。为了提高导入效率,您需要在导入 CSV 文件之前删除不必要的数据。
-
表头与数据不一致
表头可能与数据不一致。它们可能缺失或在太多列中丢失。
为避免此问题
-
检查表头是否与文件中的数据匹配。
-
在导入之前调整格式、列等,以确保流程顺利。
-
-
多余或缺失的引号
非引用文本中间的独立双引号 (
"
) 或单引号 ('
),或引用文本中未转义的引号,在读取文件进行加载时可能会导致问题。最好是转义或删除多余的引号。在Cypher® 样式指南中查找正确的转义文档。 -
特殊字符或换行符
处理文件中的任何特殊字符时,请确保它们被引用或删除。对于引用或非引用字段中的换行符,请为它们添加引号或将其删除。
-
换行符不一致
确保文件中所有换行符一致。对于 Linux 用户,建议使用 Unix 样式以实现兼容性。
-
二进制零、BOM 字节顺序标记(2 个 UTF-8 字节)或其他非文本字符
不寻常的字符或工具特定的格式有时隐藏在应用程序工具中。您可以使用基本编辑器检测并从文件中删除此类字符,或使用反引号对其进行转义。
数据类型
由于 Neo4j 将所有导入的值读取为string
类型,因此您需要转换任何非string
类型的值。您可以使用 Cypher 中的函数来完成此操作:
-
toInteger()
:将值转换为integer
。 -
toFloat()
:将值转换为float
(例如,用于金额)。 -
datetime()
:将值转换为DateTime
。
根据 CSV 文件中数据的类型,您需要根据其类型转换值。有关 Cypher 中可用值和类型的更多信息,请参阅Cypher → 值和类型。
清理
CSV 文件中的一些问题需要您在加载它们之前解决,而另一些则可以在加载它们时通过向LOAD CSV
命令添加附加子句来解决。
空值
Neo4j 不存储空值,但您可以通过向LOAD CSV
命令添加子句或函数来跳过它们或用默认值替换它们。假设您有以下 CSV 文件:
Id,Name,Location,Email,BusinessType
1,Neo4j,San Mateo,contact@neo4j.com,P
2,AAA,,info@aaa.com,
3,BBB,Chicago,,G
第三行和第四行缺少某些标题的条目,这意味着它们包含需要跳过的空值。您可以使用WHERE
子句来指定它
LOAD CSV WITH HEADERS FROM 'file:///companies.csv' AS row
WITH row WHERE row.Id IS NOT NULL
MERGE (c:Company {companyId: row.Id});
或者为它们设置一个默认值(例如“未知”),并使用coalesce
函数
LOAD CSV WITH HEADERS FROM 'file:///companies.csv' AS row
MERGE (c:Company {companyId: row.Id, hqLocation: coalesce(row.Location, "Unknown")})
您还可以将空的strings
更改为不会存储的空值,方法是使用SET
子句
LOAD CSV WITH HEADERS FROM 'file:///companies.csv' AS row
MERGE (c:Company {companyId: row.Id})
SET c.emailAddress = CASE trim(row.Email) WHEN "" THEN null ELSE row.Email END
条件转换
条件转换可以通过CASE
实现。上一个示例检查了空值或空strings
,但您也可以在此清理阶段根据 CSV 文件中的值设置属性。
例如,您可以根据 CSV 文件中的缩写值设置businessType
属性
LOAD CSV WITH HEADERS FROM 'file:///companies.csv' AS row
WITH row WHERE row.Id IS NOT NULL
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.Id, hqLocation: coalesce(row.Location, "Unknown")})
SET c.emailAddress = CASE trim(row.Email) WHEN "" THEN null ELSE row.Email END
SET c.businessType = type
RETURN *
列表作为条目
如果您的 CSV 文件中有一个字段是您想要拆分为单独行的项目列表,您可以使用 Cypher 的split()
函数来分隔单元格中的数组。例如
Id,Name,Skills,Email
1,Joe Smith,Cypher:Java:JavaScript,joe@neo4j.com
2,Mary Jones,Java,mary@neo4j.com
3,Trevor Scott,Java:JavaScript,trevor@neo4j.com
Joe 和 Trevor 在此文件中都列出了多项技能。您可以使用split()
函数和UNWIND
子句将它们拆分,如下所示
LOAD CSV WITH HEADERS FROM 'file:///employees.csv' AS row
MERGE (e:Employee {employeeId: row.Id, email: row.Email})
WITH e, row
UNWIND split(row.Skills, ':') AS skill
MERGE (s:Skill {name: skill})
MERGE (e)-[r:HAS_EXPERIENCE]->(s)
清理工具
您可以使用以下第三方工具来确保您的 CSV 文件状况良好,以便高效导入数据:
-
CSVKit:一组 Python 工具,提供统计(csvstat)、搜索(csvgrep)等功能。
-
CSVLint:一个在线服务,用于验证 CSV 文件。您可以上传文件或提供 URL 进行加载。
-
Papa Parse:一个全面的 Javascript CSV 解析库,允许您流式传输 CSV 数据,并提供良好、易读的错误报告。
文件大小
您可以使用大多数 Neo4j 的导入方法导入小型或中型数据集(最多 1000 万条记录)。如果您想导入更大的数据集,建议使用neo4j-admin database import
。有关更多信息,请参阅Neo4j-admin import教程。
优化
处理大量数据或复杂加载时,性能可能成为问题。但是,一些策略可以提高大量信息的处理效率。
例如,如果您想使用前述的companies.csv文件和以下文件来创建图
employeeId,Name,companyId
1,Bob Smith,1
2,Joe Jones,3
3,Susan Scott,2
4,Karen White,1
在这种情况下,您应该将节点和关系的创建分离到处理的不同部分。例如,不是以下方式
LOAD CSV WITH HEADERS FROM 'file:///people.csv' AS row
MERGE (e:Employee {employeeId: row.employeeId})
MERGE (c:Company {companyId: row.companyId})
MERGE (e)-[r:WORKS_FOR]->(c)
您可以这样写
Employee
节点LOAD CSV WITH HEADERS FROM 'file:///people.csv' AS row
MERGE (e:Employee {employeeId: row.employeeId, name: row.Name})
RETURN count(e);
Company
节点LOAD CSV WITH HEADERS FROM 'file:///people.csv' AS row
MERGE (c:Company {companyId: row.companyId})
RETURN count(c);
LOAD CSV WITH HEADERS FROM 'file:///people.csv' AS row
MATCH (e:Employee {employeeId: row.employeeId, name: row.Name})
MATCH (c:Company {companyId: row.companyId})
MERGE (e)-[:WORKS_FOR]->(c)
RETURN *
通过这种方式,加载每次只执行导入的一部分,可以快速高效地处理大量数据,减少繁重的处理。
结果是这个图