用 CSV 导入 Neo4j 知识图谱并验证查询结果
知识图谱配置在 Neo4j 里主要做三件事:定义节点和关系的数据结构、把数据导入图数据库、用 Cypher 查询验证结果。
如果数据还停留在 CSV 或表格阶段,先别急着写导入语句,把实体类型和关系方向确定下来,后面会省很多返工。
先确定图谱结构再动手导入
Neo4j 用节点表示实体,用关系表示实体之间的连接。
配置前先回答两个问题:哪些字段是实体,实体之间是什么关系。
比如“用户购买商品”可以拆成 User 节点和 Product 节点,关系类型为 BOUGHT,方向从用户指向商品。
建议在纸上或表格里列出:
- 节点标签:如
User、Product、Category - 每个节点的唯一标识:如
user_id、product_id - 关系类型和方向:如
(:User)-[:BOUGHT]->(:Product) - 关系上是否需要属性:如购买时间、数量
结构确定后,准备两类 CSV 文件:节点文件和关系文件。
节点文件每行一个实体,关系文件每行一条连接,并用 ID 字段关联两端节点。
准备 CSV 数据文件
假设要导入用户和商品数据,可以准备两个文件。
users.csv:
user_id,name,city
u001,张三,北京
u002,李四,上海
products.csv:
product_id,title,price
p001,机械键盘,299
p002,显示器,1299
bought.csv:
user_id,product_id,buy_date
u001,p001,2025-01-10
u002,p002,2025-01-12
把文件放到 Neo4j 的 import 目录下,这是 LOAD CSV 默认能读取的位置。
如果使用 Docker 部署,确认启动时挂载了该目录;
如果使用桌面版,可以在设置里查看 import 路径。
用 Cypher 导入节点和关系
先导入节点。
下面的语句会为每一行创建或更新一个 User 节点,并用 user_id 作为唯一标识:
LOAD CSV WITH HEADERS FROM 'file:///users.csv' AS row
MERGE (u:User {user_id: row.user_id})
SET u.name = row.name, u.city = row.city;
同理导入商品节点:
LOAD CSV WITH HEADERS FROM 'file:///products.csv' AS row
MERGE (p:Product {product_id: row.product_id})
SET p.title = row.title, p.price = toFloat(row.price);
再导入关系。
关系文件里只有两端 ID 和关系属性,需要先匹配已存在的节点,再创建关系:
LOAD CSV WITH HEADERS FROM 'file:///bought.csv' AS row
MATCH (u:User {user_id: row.user_id})
MATCH (p:Product {product_id: row.product_id})
MERGE (u)-[r:BOUGHT {buy_date: row.buy_date}]->(p);
执行后如果返回类似 Added 2 nodes, created 2 relationships 的提示,说明导入有结果。
如果返回 0 行,先检查 CSV 路径和字段名是否和语句一致。
容易踩坑的几个地方
CSV 路径写错:file:/// 后面跟的是 import 目录内的相对路径,不是系统绝对路径。
报错 Couldn't load the external resource 时,优先确认文件名和目录。
字段名大小写不一致:Cypher 里 row.user_id 必须和 CSV 表头完全一致,表头有空格或大小写不同都会取不到值。
关系导入前节点不存在:MATCH 找不到节点时不会创建关系,也不会报错。
可以在关系导入前先跑 MATCH (u:User) RETURN count(u); 确认节点数量。
重复导入产生重复关系:使用 MERGE 而不是 CREATE 可以避免重复创建,但关系属性参与匹配时,属性值不同仍会新建关系。
导入前想清楚哪些属性应该放在关系上。
验证图谱是否配置成功
导入后不要只看语句执行成功,用查询确认数据能按预期返回。
查某个用户买过哪些商品:
MATCH (u:User {user_id: 'u001'})-[:BOUGHT]->(p:Product)
RETURN u.name, p.title, p.price;
查所有关系数量:
MATCH ()-[r]->() RETURN type(r), count(r);
如果数据量会持续增长,建议在唯一标识字段上建约束,避免重复节点:
CREATE CONSTRAINT user_id_unique IF NOT EXISTS
FOR (u:User) REQUIRE u.user_id IS UNIQUE;
建约束后再执行导入语句,重复数据会被拦截或合并,比事后清理更省事。
常见疑问
导入时提示内存不足怎么办?
LOAD CSV 默认会分批处理,但大文件仍可能占用较多内存。
可以先用 LIMIT 试跑前 100 行,确认语句正确后再去掉限制。
也可以在 neo4j.conf 中调整事务内存相关参数,但改配置前先备份原文件。
CSV 里的空值怎么处理?
空字符串会被当成空值写入属性。
如果某个字段允许为空,导入语句里可以加条件判断,例如 SET u.city = row.city 前先确认 row.city 不为空,避免把空字符串覆盖到已有数据上。
导入后查询很慢,先查什么?
先用 PROFILE 查看执行计划,确认查询是否走了索引。
如果按 user_id 查询,却没有在 User.user_id 上建索引或约束,Neo4j 会扫描全部节点。
建约束后重新执行查询,观察扫描节点数是否下降。
Neo4j 知识图谱配置的关键不是把语句跑通一次,而是让节点唯一、关系方向明确、查询能稳定返回结果。
按“定结构、备 CSV、导节点、导关系、查结果”的顺序操作,大部分导入问题都能在验证阶段暴露出来。