如何:使用 Neo4j Desktop 导入 CSV 数据
简介
Neo4j Desktop 提供了一个用户友好的界面,用于创建和启动 Neo4j 实例、添加或删除插件、更改配置和其他功能。它还包含一些快捷方式和简易访问功能,用于将文件(例如 CSV 文件)导入 Neo4j。
在本指南中,您将使用一个包含三个 CSV 文件的压缩文件夹,并将数据导入到 Neo4j Desktop 中的图中。CSV 文件包含产品、订单和订单明细的数据。您将在本指南的后面部分查看文件中的数据。
创建和启动 Neo4j 实例
如果您已经知道如何在 Neo4j Desktop 中创建项目、Neo4j 实例(DBMS)并启动 DBMS,则可以跳到下一步:将 CSV 文件添加到导入文件夹。 |
如果您第一次打开 Neo4j Desktop,您将看到一个 **Neo4j Primer 项目**,其中已启动了 *电影数据库*。如果您想开始学习如何使用 Neo4j 和 Cypher®,可以使用此数据库。但是,您可以创建自己的项目,该项目可以包含一个或多个 DBMS。
让我们看一下如何在 Neo4j Desktop 中创建新项目,以及如何创建和启动 Neo4j 实例。
-
您一次只能运行一个 DBMS。要启动一个新的 DBMS,必须首先单击顶部栏上的“停止”按钮停止活动的 DBMS。结果,您将看到 **没有活动的 DBMS**。
-
要添加新项目,请转到左侧侧边栏上的 **项目** 抽屉,方法是单击图标
,然后单击 **项目** 前面的“新建”按钮。这将创建一个名为“项目”的项目。
-
要更改项目名称,请将鼠标悬停在项目名称上并选择“编辑”按钮。
输入项目名称并选择“确认”按钮以保存它。
-
接下来,您将在项目中创建一个本地 DBMS。单击要添加 DBMS 的项目名称旁边的“添加”按钮,然后选择“本地 DBMS”。这将打开一个对话框,您可以在其中指定 DBMS 的详细信息。
-
现在您可以命名您的 DBMS。您可以使用默认名称 *Graph DBMS*,但建议您将其重命名以帮助您识别它。有关更多详细信息,请参阅 升级和迁移指南 → 数据库命名规则。
此处将 *MyDBMS* 指定为名称
-
您 必须为 DBMS 指定密码。
从 **Neo4j 5.3** 开始,初始密码必须至少为 **八个字符** 长。
-
Neo4j Desktop 自动创建具有默认版本的 DBMS,但您可以为其选择其他版本。但是,您可以选择其他版本。请记住,如果版本旁边显示向下箭头,则表示 Neo4j Desktop 需要下载该特定版本 DBMS 的资源。为此,您 必须连接到互联网。
-
指定 DBMS 的详细信息后,单击“创建”按钮。以下是 DBMS 成功创建后您应该看到的内容
-
由于您一次只能拥有一个活动 DBMS,因此请确保在启动新创建的 DBMS 之前停止任何正在运行的实例,方法是将鼠标悬停在其名称右侧并单击“启动”按钮。
DBMS 将需要几秒钟才能启动。如果成功,您应该会看到类似以下内容
DBMS 启动后,您可以通过系统上运行的 Neo4j 浏览器和 Neo4j Bloom 等客户端访问它。在 Neo4j Desktop 中,DBMS 是一个企业服务器,但只能在本地访问。
将 CSV 文件添加到导入文件夹
首先,下载此 zip 文件。将其解压缩以生成三个用于产品、订单和订单详细信息的 CSV 文件,然后将它们添加到 Neo4j Desktop 中的 **import** 文件夹。
您可以通过将鼠标悬停在已启动 DBMS 右侧的三个点上并选择“打开文件夹”,然后选择“导入”来打开查找器窗口

另一种选择是将三个 CSV 文件复制或移动到系统上的 **导入目录**。有关 Neo4j 文件位置的更多信息,请参阅 操作手册 → 默认文件位置。
现在您的文件已位于 **import** 文件夹中,您可以将数据导入到 DBMS 管理的数据库中。您将使用 CSV 文件中的当前表和列格式,并将其转换为节点和关系。这可以通过几种不同的方式完成,但您将在本指南中使用 Cypher 的 LOAD CSV
命令。
LOAD CSV
LOAD CSV
是 Cypher 中的一个内置命令,允许您读取 CSV 文件并将常规 Cypher 查询附加到创建或更新数据作为图。您也可以在不创建图的情况下使用 LOAD CSV
来输出样本、计数或分布。这有助于在写入和存储数据之前检测不正确的标题列计数、分隔符、引号、转义符或标题名称的拼写。
要输入并在已启动的 DBMS 上运行 Cypher 查询,您可以
-
使用 Neo4j 浏览器
-
单击已启动 DBMS 的“打开”按钮。
-
在顶部的编辑窗格中键入或复制 Cypher 查询 (Cypher 编辑器)。
-
使用右侧的“播放”按钮执行 Cypher 查询。
-
-
使用 Cypher Shell
-
单击“打开”按钮右侧的下拉菜单,然后选择“终端”。
-
输入
bin/cypher-shell
。 -
输入 **neo4j** 作为用户。
-
输入您为 DBMS 指定的密码。
-
使用
:exit
退出。
-
所有 Cypher 查询必须在 Cypher Shell 中以分号 |
之前您下载了.zip文件并将它的CSV文件复制到了DBMS的import文件夹。建议您在将任何内容插入图数据库之前,检查要添加到import文件夹中的文件中的数据。为此,您可以使用LOAD CSV
语句。
如果您之前打开过这些文件,您可能已经注意到其中两个文件有标题,而一个文件没有(products.csv)。要检查每个文件,请检查CSV文件中存在多少行,以确保它们在潜在的导出过程中没有损坏或被截断。对于有标题的文件,您可以在LOAD CSV
之后添加WITH HEADERS
子句,以便它排除标题行并在计数中仅计算数据行。
以下是将要使用的Cypher查询
//count data rows in products.csv (no headers)
LOAD CSV FROM 'file:///products.csv' AS row
RETURN count(row);
//count data rows in orders.csv (headers)
LOAD CSV WITH HEADERS FROM 'file:///orders.csv' AS row
RETURN count(row);
//count data rows in order-details.csv (headers)
LOAD CSV WITH HEADERS FROM 'file:///order-details.csv' AS row
RETURN count(row);
运行这些语句应返回以下计数
-
products.csv有77行
-
orders.csv有830行
-
order-details.csv有2155行
使用LOAD CSV
查看数据
接下来,您可以查看CSV文件中的数据以及LOAD CSV
如何读取它。您需要从上面的Cypher查询中更改的唯一一行是RETURN
子句。由于这些文件有多行,因此使用LIMIT
仅获取样本。
//view data rows in products.csv
LOAD CSV FROM 'file:///products.csv' AS row
RETURN row
LIMIT 3;
您的结果应如下所示
行 |
---|
["1", "Chai", "18"] |
["2", "Chang", "19"] |
["3", "Aniseed Syrup", "10"] |
//count data rows in orders.csv (headers)
LOAD CSV WITH HEADERS FROM 'file:///orders.csv' AS row
RETURN row
LIMIT 5;
您的结果应如下所示
行 |
---|
|
|
|
|
|
//count data rows in order-details.csv (headers)
LOAD CSV WITH HEADERS FROM 'file:///order-details.csv' AS row
RETURN row
LIMIT 8;
您的结果应如下所示
行 |
---|
|
|
|
|
|
|
|
|
请注意,orders.csv和order-details.csv返回的数据格式与products.csv不同。这是因为这些文件有标题,因此这些行的列名与值一起返回。由于products.csv没有列名,因此LOAD CSV
只返回文件中的纯数据行。
使用LOAD CSV
过滤加载的数据
检查完数据后,您可能只想查看或加载CSV文件中的数据子集。您可以按如下方式过滤要查看(或加载)的内容
//count data rows in orders.csv (headers)
LOAD CSV WITH HEADERS FROM 'file:///orders.csv' AS row
WITH row WHERE row.shipCountry = 'Germany'
RETURN row
LIMIT 5;
您的结果应如下所示
行 |
---|
|
|
|
|
|
数据类型
LOAD CSV
命令将所有值读取为字符串。无论值在文件中如何显示,它都将作为字符串使用LOAD CSV
加载。因此,在导入之前,请确保转换任何非字符串值。
Cypher中有多种转换函数。您将在本练习中使用以下函数
-
toInteger()
:将值转换为整数。 -
toFloat()
:将值转换为浮点数(在本例中,用于货币金额)。 -
datetime()
:将值转换为DateTime。
我们查看每个CSV文件中的值以确定需要转换哪些值。
products.csv文件中的值对应于productID
、productName
和unitCost
。productID
看起来像一个随着每行增加的整数值,因此您可以使用Cypher中的toInteger()
函数将其转换为整数。productName
可以保持为字符串,因为它由字符组成。最后一列是产品unitCost
。尽管您检查的示例值都是整数,但货币金额通常有小数位值。出于这个原因,建议使用toFloat()
函数将这些值转换为浮点数。
以下是您应该如何运行Cypher查询。请记住,您此时尚未将值加载到Neo4j中。您将只是查看具有转换值的CSV文件。
LOAD CSV FROM 'file:///products.csv' AS row
WITH toInteger(row[0]) AS productId, row[1] AS productName, toFloat(row[2]) AS unitCost
RETURN productId, productName, unitCost
LIMIT 3;
您的结果应如下所示
productId | productName | unitCost |
---|---|---|
1 |
"Chai" |
18.0 |
2 |
"Chang" |
19.0 |
3 |
"Aniseed Syrup" |
10.0 |
请注意,我们使用集合位置(row[0]、row[1]、row[2])来引用行中的列,并通过使用别名来引用它们以提高可读性。在没有标题的文件中,这就是引用每个位置中的值的方式。
orders.csv中的值(根据列名)对应于orderID
、orderDate
和shipCountry
。同样,您可以评估这些值并确定要应用的任何转换。
OrderID
看起来像一个整数,因此您可以使用toInteger()
函数进行转换。orderDate
列当然采用日期格式,我们将需要使用datetime()
函数对其进行格式化。最后,shipCountry值是字符,因此您可以将其保留为字符串。
就像您对上一个CSV文件所做的那样,让我们在不导入数据的情况下查看这些转换的结果。
LOAD CSV WITH HEADERS FROM 'file:///orders.csv' AS row
WITH toInteger(row.orderID) AS orderId, datetime(replace(row.orderDate,' ','T')) AS orderDate, row.shipCountry AS country
RETURN orderId, orderDate, country
LIMIT 5;
您的结果应如下所示
orderId | orderDate | country |
---|---|---|
10248 |
"1996-07-04T00:00:00Z" |
"France" |
10249 |
"1996-07-05T00:00:00Z" |
"Germany" |
10250 |
"1996-07-08T00:00:00Z" |
"Brazil" |
10251 |
"1996-07-08T00:00:00Z" |
"France" |
10252 |
"1996-07-09T00:00:00Z" |
"Belgium" |
这个CSV在orderDate
列中有一个棘手的问题。Neo4j的datetime使用ISO 8601格式,该格式在日期和时间值之间使用分隔符T
。CSV文件没有连接日期和时间值的'T',而是用空格分隔。您使用了replace()
函数将空格更改为字符'T',并将字符串转换为预期格式。然后,您在它周围包装了datetime()
函数以将更改后的字符串转换为DateTime值。
order-details.csv中的值(根据列名)对应于productID
、orderID
和quantity
。让我们看看哪些需要转换。
productID
也来自products.csv文件,您已将该值转换为整数。您将在这里执行相同的操作以确保格式匹配。orderID
字段包含orders.csv文件中的值,因此您将匹配之前的转换并将此字段也转换为整数。此文件中的quantity
字段是数值。您可以使用一直使用的toInteger()
函数将其转换为整数。
这些转换的结果在下面的代码中。请记住,您仍然没有加载任何数据。
LOAD CSV WITH HEADERS FROM 'file:///order-details.csv' AS row
WITH toInteger(row.productID) AS productId, toInteger(row.orderID) AS orderId, toInteger(row.quantity) AS quantityOrdered
RETURN productId, orderId, quantityOrdered
LIMIT 8;
您的结果应如下所示
productId | orderId | quantityOrdered |
---|---|---|
11 |
10248 |
12 |
42 |
10248 |
10 |
72 |
10248 |
5 |
14 |
10249 |
9 |
51 |
10249 |
40 |
41 |
10250 |
10 |
51 |
10250 |
35 |
65 |
10250 |
15 |
加载数据
既然您已经确定CSV文件数据看起来不错,并且您已经验证了LOAD CSV
如何读取数据并转换了任何非字符串值,那么您几乎准备好创建图数据库中的数据了。
为此,您将在上面使用的LOAD CSV
命令旁边使用Cypher语句。LOAD CSV
将读取文件,而Cypher语句将在您的数据库中创建数据。
图数据模型
不过,在编写Cypher语句之前,您需要执行的重要步骤是确定导入文件数据后图结构应是什么样子。毕竟,从现有的表和列数据中导入数据不会提供您希望从图中获得的值。为了充分利用图数据库,您需要一个图数据模型。
虽然有多种方法可以组织文件中的产品和订单,但这将在另一本指南中进行介绍。在本练习中,请使用模型的以下版本
您有两个节点——一个用于产品,一个用于订单。每个节点都具有来自CSV文件属性。对于Product
,您有ID、名称和单位成本。对于Order
,您有ID、日期/时间和订单送达的国家/地区。
order-details.csv文件定义了这两个节点之间的关系。它包含产品ID、它所属的订单ID以及订单中产品的数量。在数据模型中,这些成为Product
和Order
节点之间的CONTAINS
关系。属性quantityOrdered
也包含在关系中,因为只有当产品与订单相关联时,产品数量值才存在。
现在您知道了将要拥有的节点和关系的类型以及涉及的属性,您可以构建Cypher语句以创建此模型的数据。
避免重复并提高性能
在您在图中创建数据之前,您需要考虑的最后一件事是确保值是唯一的并且性能是高效的。要处理此问题,您可以使用约束。就像其他数据库一样,约束确保不会违反数据完整性标准,同时还对具有约束的属性进行索引以提高查询性能。
在导入任何数据之前以及在已存在数据时,都有在数据库中应用索引的情况。在本练习中,您将在创建任何数据之前添加两个约束——一个用于productId
,一个用于orderId
。这将确保当您创建每个这些类型的节点或连接它们的关联时,您知道实体是唯一的且已编入索引。
以下是添加约束的Cypher代码
CREATE CONSTRAINT UniqueProduct FOR (p:Product) REQUIRE p.id IS UNIQUE;
CREATE CONSTRAINT UniqueOrder FOR (o:Order) REQUIRE o.id IS UNIQUE;
Cypher查询
现在您已准备好编写用于在图中创建数据的Cypher代码了。
您可以使用CREATE
子句,在您确定CSV文件中不会有重复行,并使用MATCH
查找现有数据以进行更新。但是,由于很难完全清理所有数据并从任何来源导入完全干净的数据,因此您将使用MERGE
子句来检查数据是否已存在。如果节点或关系存在,则Cypher将匹配并返回它们(无需任何写入),但如果它们不存在,则Cypher将插入它们。
使用MERGE
可能会导致一些性能开销,但通常它是维护高数据完整性的更好方法。
为什么同时使用约束和 如果您同时使用两者,则可以避免因约束冲突而终止加载语句,并且还可以确保不会在临时查询中意外创建重复项。 |
要开始将产品加载到图中,请使用上面提到的LOAD CSV
语句,然后运行Cypher查询,将CSV文件中的数据创建到您的模型中。请记住使用MERGE
检查Product
是否已存在。属性将设置为本指南前面处理的转换后的值。
LOAD CSV FROM 'file:///products.csv' AS row
WITH toInteger(row[0]) AS productId, row[1] AS productName, toFloat(row[2]) AS unitCost
MERGE (p:Product {productId: productId})
SET p.productName = productName, p.unitCost = unitCost
RETURN count(p);
如果运行该语句,它将返回数据库中创建的产品节点数(count(p)
)。您可以将此数字与前面CSV文件中的行数进行交叉检查(products.csv中为77行)。您还可以运行验证查询以返回节点样本,并查看属性是否准确。
//validate products loaded correctly
MATCH (p:Product)
RETURN p LIMIT 20;
以下是Neo4j浏览器中的结果

接下来,您将加载订单。同样,由于您希望验证不会创建重复的Order
节点,因此可以使用MERGE
子句。与产品一样,您从LOAD CSV
命令开始,然后添加Cypher查询,并包含您的数据转换。
LOAD CSV WITH HEADERS FROM 'file:///orders.csv' AS row
WITH toInteger(row.orderID) AS orderId, datetime(replace(row.orderDate,' ','T')) AS orderDate, row.shipCountry AS country
MERGE (o:Order {orderId: orderId})
SET o.orderDateTime = orderDate, o.shipCountry = country
RETURN count(o);
您还可以像以前一样运行验证查询,以验证图数据是否正确。
//validate orders loaded correctly
MATCH (o:Order)
RETURN o LIMIT 20;
以下是Neo4j浏览器中的结果

最后但并非最不重要的是,您将在产品和订单之间创建关系。由于您预计图中已存在所有产品和所有订单(这些数据应已使用最后两个文件加载),因此您从MATCH
开始查找现有的Product
和Order
节点。然后,MERGE
语句将添加新的关系或匹配现有关系。
如您在上面运行order-details文件的计数时发现的那样,CSV中有2,155行。虽然对于文件导入来说,这不是一个很大的数字,但您将让Cypher分批将数据提交到数据库,以减少事务状态的内存开销。为此,您可以在LOAD CSV
子句之后使用子查询CALL {…} IN TRANSACTIONS
。
输入行数由修饰符OF n ROWS
(或ROW
)设置。如果省略,则默认批处理大小为1000行。在本练习中,您将要求Cypher每500行提交一次。如果已经有大量内存分配给其他任务,或者内存有限,则可以减小此数字。
LOAD CSV WITH HEADERS FROM 'file:///order-details.csv' AS row
CALL {
WITH row
MATCH (p:Product {productId: toInteger(row.productID)})
MATCH (o:Order {orderId: toInteger(row.orderID)})
MERGE (o)-[rel:CONTAINS {quantityOrdered: toInteger(row.quantity)}]->(p)
} IN TRANSACTIONS OF 500 ROWS
在Neo4j浏览器中,不要忘记在前面的Cypher查询前加上 |
就像您上面所做的那样,您可以使用下面的查询验证数据。
MATCH (o:Order)-[rel:CONTAINS]->(p:Product)
RETURN p, rel, o LIMIT 50;
以下是Neo4j浏览器中的结果

总结
您已成功使用Neo4j Desktop将三个CSV文件加载到Neo4j图数据库中。
LOAD CSV
功能与Cypher相结合,对于将文件中的数据获取到图结构中非常有用。提高这方面技能的最佳方法是为各种数据集和模型加载各种文件。
如果您以后再次完成此练习,可以随意增加挑战,为这些文件设计自己的数据模型,或者尝试将其他一些CSV文件加载到图中。
如果您有任何疑问或需要有关使用LOAD CSV
的帮助,请在社区网站上联系我们。