介绍
- 节点,关系和属性中的数据
- 节点和关系都包含属性
- 关系连接节点
- 属性是键值对
- 节点用圆圈表示,关系用方向键表示。
- 关系具有方向:单向和双向。
- 每个关系包含“开始节点”或“从节点”和“到节点”或“结束节点”
在属性图数据模型中,关系应该是定向的。如果我们尝试创建没有方向的关系,那么它将抛出一个错误消息。
Neo4j图数据库将其所有数据存储在节点和关系中。
主要构建块是:
- 节点。图表的基本单位。 它包含具有键值对的属性
- 关系。它连接两个节点。每个关系包含一个起始节点和一个结束节点。
- 属性。描述图节点和关系的键值对
- 标签。将一个公共名称与一组节点或关系相关联。 节点或关系可以包含一个或多个标签。
CQL
命令介绍 —
CQL命令 | 介绍 |
---|---|
CREATE | 创建节点,关系和属性 |
MATCH | 检索有关节点,关系和属性数据 |
RETURN | 返回查询结果 |
WHERE | 条件过滤检索数据 |
DELETE | 删除节点和关系 |
REMOVE | 删除节点和关系的属性 |
ORDER BY | 排序检索数据 |
SET | 添加或更新标签 |
CREATE
创建没有属性的节点
CREATE (<node-name>:<label-name>)
CREATE (emp:Employee)
emp是一个节点名。Employee是emp节点的标签名称。
创建带属性的节点
CREATE (
<node-name>:<label-name>
{
<Property1-name>:<Property1-Value>
........
<Propertyn-name>:<Propertyn-Value>
}
)
CREATE (dept:Dept { deptno:10,dname:"Accounting",location:"Hyderabad" })
创建不带属性的关系
CREATE
(<node1-label-name>:<node1-name>)-
[<relationship-label-name>:<relationship-name>]->
(<node1-label-name>:<node1-name>)
RETURN <relationship-label-name>
CREATE (fb1:FaceBookProfile1)-[like:LIKES]->(fb2:FaceBookProfile2)
MATCH (fb1:FaceBookProfile1)-[like:LIKES]->(fb2:FaceBookProfile2)
RETURN like
创建带属性的关系
CREATE
(<node1-label-name>:<node1-name>{<define-properties-list>})-
[<relationship-label-name>:<relationship-name>{<define-properties-list>}]
->(<node1-label-name>:<node1-name>{<define-properties-list>})
RETURN <relationship-label-name>
CREATE (video1:YoutubeVideo1{title:"Action Movie1",updated_by:"Abc",uploaded_date:"10/10/2010"})
-[movie:ACTION_MOVIES{rating:1}]->
(video2:YoutubeVideo2{title:"Action Movie2",updated_by:"Xyz",uploaded_date:"12/12/2012"})
MATCH (video1:YoutubeVideo1)-[movie:ACTION_MOVIES]->(video2:YoutubeVideo2)
RETURN movie
节点之间创建不带属性的关系
MATCH (e:Customer),(cc:CreditCard)
CREATE (e)-[r:DO_SHOPPING_WITH ]->(cc)
这里关系名称为 DO_SHOPPING_WITH
关系标签为 r
e 和 Customer 分别是客户节点的 节点名称 和 节点标签。
cc 和 CreditCard 分别是 CreditCard 节点的 节点名 和 节点标签
还可以用相反的方式创建 cc 到 e 的关系。这样就得到一个双向关系。
节点之间创建带属性的关系
MATCH (cust:Customer),(cc:CreditCard)
CREATE (cust)-[r:DO_SHOPPING_WITH{shopdate:"12/12/2014",price:55000}]->(cc)
RETURN r
关系的方向
节点之间的关系是有方向性的。 它们是单向或双向的。如果我们尝试创建一个没有任何方向的关系, Neo4j DB服务器会抛出一个错误。() - []→() 表明了关系的方向。从一个节点到另一个节点。
为节点创建单个或多个标签
CREATE (<node-name>:<label-name>)
CREATE (google1:GooglePlusProfile)
CREATE (m:Movie:Cinema:Film:Picture)
Movie, Cinema, Film, Picture是m节点的多个标签名称
为关系创建单个或多个标签
CREATE (<node1-name>:<label1-name>)-
[(<relationship-name>:<relationship-label-name>)]
->(<node2-name>:<label2-name>)
CREATE (p1:Profile1)-[r1:LIKES]->(p2:Profile2)
p1 和 profile1 是节点名称和节点标签名称 From Node
p2 和 Profile2 是 To Node 的节点名称和节点标签名称
r1 是关系名称
LIKES 是一个关系标签名称
MATCH
从数据库获取有关节点和属性的数据
MATCH
(
<node-name>:<label-name>
)
MATCH (dept:Dept)
RETURN
RETURN
<node-name>.<property1-name>,
........
<node-name>.<propertyn-name>
RETURN dept.deptno
RETURN + MATCH
MATCH Command
RETURN Command
MATCH (dept: Dept)
RETURN dept.deptno, dept.dname
CREATE + MATCH + RETURN
CREATE (e:Customer{id:"1001",name:"Abc",dob:"01/10/1982"})
CREATE (cc:CreditCard{id:"5001",number:"1234567890",cvv:"888",expiredate:"20/17"})
MATCH (e:Customer)
RETURN e.id, e.name, e.dob
MATCH (cc:CreditCard)
RETURN cc.id, cc.number, cc.cvv, cc.expiredate
查询关系
MATCH (e)-[r:DO_SHOPPING_WITH ]->(cc)
RETURN r
MATCH
(<node1-label-name>)-[<relationship-label-name>:<relationship-name>]->(<node2-label-name>)
RETURN <relationship-label-name>
根据属性匹配节点信息
MATCH (n:Person{name:"李四"}}) RETURN n
字符串匹配
匹配字符串的开头
MATCH (n)
WHERE n.name STARTS WITH '张'
RETURN n
匹配字符串的末尾
MATCH (n)
WHERE n.name ENDS WITH '三'
RETURN n
匹配字符串的包含匹配
MATCH (n)
WHERE n.name CONTAINS '三'
RETURN n
字符串排除匹配
使用NOT关键字来排除匹配到的结果,得到相反的结果
MATCH (n)
WHERE NOT n.name STARTS WITH '张'
RETURN n
字符串正则表达式
模糊匹配,类似sql语句中的like
MATCH (n)
WHERE n.name =~ '.*小.*'
RETURN n
不区分大小写正则匹配
MATCH (n)
WHERE n.name =~ '(?i)ANDR.*'
RETURN n
路径匹配
逻辑运算符号和路径作为过滤条件
匹配名称为”李四”或”王五”且与”张三”有任何关系的节点,并返回符合匹配条件的节点信息
MATCH (n { name: '张三' }),(m)
WHERE m.name IN ['李四', '王五'] AND (n)<--(m)
RETURN m
NOT逻辑运算符号
匹配和张三没有关系的人
MATCH (persons),(zhangsan { name: '张三' })
WHERE NOT (persons)-->(zhangsan)
RETURN persons
关系深度匹配
任意关系,深度1到5的节点
MATCH p=(n)-[*1..5]->(m) RETURN p
任意关系、任意深度的节点
MATCH p=(n)-[*]->(m) RETURN p
WITH
将第一部分匹配的输出作为下一部分匹配的输入
查找有十个以上朋友的张姓男子
MATCH (user)-[:FRIEND]-(friend)
WHERE user.name =~ '张.*'
WITH user, count(friend) AS friends
WHERE friends > 10
RETURN user
使用ORDER BY、SKIP 和 LIMIT 语句
MATCH (user)-[:FRIEND]-(friend)
WITH user, count(friend) AS friends
ORDER BY friends DESC
SKIP 1
LIMIT 3
RETURN user
WHERE
WHERE <condition>
WHERE <condition> <boolean-operator> <condition>
boolean-operator 布尔运算包括:
AND, OR, NOT, XOR
condition 语法
<property-name> <comparison-operator> <value>
comparison-operator 操作运算符包括:
=, <, >, <=, >=
如:
MATCH (emp:Employee)
WHERE emp.name = 'Abc'
RETURN emp
MATCH (emp:Employee)
WHERE emp.name = 'Abc' OR emp.name = 'Xyz'
RETURN emp
为 where 子句的结果创建关系
MATCH (cust:Customer),(cc:CreditCard)
WHERE cust.id = "1001" AND cc.id= "5001"
CREATE (cust)-[r:DO_SHOPPING_WITH{shopdate:"12/12/2014",price:55000}]->(cc)
RETURN r
DELETE
删除节点及相关节点和关系
MATCH (e: Employee)
DELETE e
MATCH (cc: CreditCard)-[rel]-(c:Customer)
DELETE cc, c, rel
REMOVE
删除节点或关系的现有属性。包括标签
CREATE (book:Book {id:122,title:"Neo4j Tutorial",pages:340,price:250})
MATCH (book { id:122 })
REMOVE book.price
RETURN book
删除标签:
MATCH (m:Movie)
REMOVE m:Picture
SET
向现有节点或关系添加新属性
MATCH (dc:DebitCard)
SET dc.atm_pin = 3456
RETURN dc
ORDER BY排序
MATCH (emp:Employee)
RETURN emp.empid,emp.name,emp.salary,emp.deptno
ORDER BY emp.name DESC
Union合并
将两个不同的结果合并成一组结果
<MATCH Command1>
UNION
<MATCH Command2>
MATCH (cc:CreditCard) RETURN cc.id,cc.number
UNION
MATCH (dc:DebitCard) RETURN dc.id,dc.number
MATCH (cc:CreditCard)
RETURN cc.id as id,cc.number as number,cc.name as name,
cc.valid_from as valid_from,cc.valid_to as valid_to
UNION
MATCH (dc:DebitCard)
RETURN dc.id as id,dc.number as number,dc.name as name,
dc.valid_from as valid_from,dc.valid_to as valid_to
UNION ALL
结合并返回两个结果集的所有行成一个单一的结果集。但:两个结果集的名字必须匹配,列名称应该是相同的,列的数据类型也应该是相同
MATCH (cc:CreditCard)
RETURN cc.id as id,cc.number as number,cc.name as name,
cc.valid_from as valid_from,cc.valid_to as valid_to
UNION ALL
MATCH (dc:DebitCard)
RETURN dc.id as id,dc.number as number,dc.name as name,
dc.valid_from as valid_from,dc.valid_to as valid_to
LIMIT 和 SKIP
过滤或限制查询返回的行数。Limit 返回结果集的前 N 行,Skip 返回最后的 N 行。
MATCH (emp:Employee)
RETURN emp
LIMIT 2
MATCH (emp:Employee)
RETURN emp
SKIP 2
Null 值
CQL 将 空值 视为对节点或关系的属性的默认值。当我们创建一个只有标签名称但没有属性的节点时,它将创建一个具有 NULL 属性值的新节点。
CREATE (e:Employee)
MATCH (e:Employee)
RETURN e.id,e.name,e.sal,e.deptno
这里创建了新节点,没有添加任何属性。但后面查询了它的属性,这时候返回的值就是 NULL。可以进行过滤:
CREATE (e:Employee)
MATCH (e:Employee)
WHERE e.id IS NOT NULL
RETURN e.id,e.name,e.sal,e.deptno
IN 查找
MATCH (e:Employee)
WHERE e.id IN [123,124]
RETURN e.id,e.name,e.sal,e.deptno
ID属性
Id 是节点和关系的默认内部属性。 这意味着,当我们创建一个新的节点或关系时,Neo4j数据库服务器将为内部使用分配一个数字。 它会自动递增。最大值约为 35 亿。
CREATE (tweet:Tweet{message:"Hello"})
MATCH (tweet:Tweet{message:"Hello"})
RETURN tweet
如果再添加,可以在图形界面看到节点属性里 ID 的递增。
建索引
CREATE INDEX ON :<label_name> (<property_name>)
注意:
冒号(:)运算符用于引用节点或关系标签名称。
上述语法描述它在节点或关系的的上创建一个新索引。
如:
CREATE INDEX ON :Customer (name)
删除索引
DROP INDEX ON :Customer (name)
函数介绍
CQL函数 | 介绍 |
---|---|
String | 使用String字面量 |
Aggregation | 对CQL查询结果执行一些聚合操作 |
Relationship | 获取关系的细节,如startnode,endnode等 |
字符串函数
CQL提供了一组String函数,用于在CQL查询中获取所需的结果。如:UPPER, LOWER, SUBSTRING, REPLACE。
MATCH (e:Employee)
RETURN e.id, UPPER(e.name), SUBSTRING(e.name, 0, 2) as short_name, e.sal, e.deptno, REPLACE(e.name, 'aaa', 'bbb') as replaced_name,
AGGREGATION 聚合
可以用来查询结果中的诸如:COUNT, MAX, MIN, SUM, AVG 数据。
MATCH (e:Employee)
RETURN MAX(e.sal) as max_sal, MIN(e.sal) as min_sal, AVG(e.sal) as avg_sal, SUM(e.sal) as total_sal, COUNT(*) as cnt
关系函数
获取开始节点,结束节点等细节时知道关系的细节: STARTNODE: 关系的开始节点; ENDNODE: 关系的结束节点; ID; TYPE
STARTNODE (<relationship-label-name>)
MATCH (video1:YoutubeVideo1)-[movie:ACTION_MOVIES]->(video2:YoutubeVideo2)
RETURN STARTNODE (movie) as start_node, ENDNODE (movie) as end_node, ID(movie), TYPE(movie)
数据类型
CQL数据类型 | 介绍 |
---|---|
boolean | 用于表示布尔文字:true,false。 |
byte | 8位整数 |
short | 16位整数 |
int | 32位整数 |
long | 64位整数 |
float | 32位浮点数 |
double | 64位浮点数 |
char | 16位字符 |
String | 字符串 |
管理
索引
节点或关系属性上可以添加索引,以提高应用程序的性能。可以在MATCH或WHERE或IN运算符上使用这些索引列来改进CQL
CREATE INDEX ON :<label_name> (<property_name>)
CREATE INDEX ON :Customer (name)
DROP INDEX ON :Customer (name)
UNIQUE
CQL CREATE 命令始终创建新的节点或关系.
这意味着即使使用相同的值,它也会插入一个新行。 有时,我们想避免这种重复,可以使用一些数据库约束来创建节点或关系的一个或多个属性的规则: UNIQUE
CREATE CONSTRAINT ON (<label_name>)
ASSERT <property_name> IS UNIQUE
CREATE CONSTRAINT ON (cc:CreditCard)
ASSERT cc.number IS UNIQUE
如果当前已经有重复的数据,创建唯一约束时会失败,这时候需要先删除重复的数据再创建。
MATCH (cc:CreditCard)
WHERE cc.number = 222222
DELETE cc
创建了唯一约束后,如果再添加重复的数据,会失败。
删除唯一约束的方式:
DROP CONSTRAINT ON (<label_name>)
ASSERT <property_name> IS UNIQUE
DROP CONSTRAINT ON (cc:CreditCard)
ASSERT cc.number IS UNIQUE
数据导入
通过API,我们可以逐条将节点和关系创建。但如果有上万,甚至百万千万以及更多数据时。如果一条条处理,可以想象性能是非常差的。类似 MySQL ,当有大量数据时, load data 会有数量级的性能提升。同理,neo4j 也提供了类似的功能甚至是更多的方式。
数据导入,可选的方式有:
- create 语句,为每一条数据写一个create
- load csv 语句,将数据转成CSV格式,通过LOAD CSV读取数据
- 官方提供的neo4j-import工具,未来将被neo4j-adminimport代替
- 官方提供的Java API: BatchInserter
- 大牛编写的 batch-import 工具
- neo4j-apocload.csv +apoc.load.relationship
经网友验证,性能大致对比如下:
create 语句 | load csv | neo4j-import | BatchInster | batch-import | apoc | |
---|---|---|---|---|---|---|
适用场景 | 小型数据 | 初始化导入 增量更新 |
初始化导入 | 初始化导入 | 初始化导入 增量更新 |
增量更新 |
导入速度 | 很慢1000/s | 数 k /s | 数 w /s | 数 w /s | 数 w /s | 数 k /s |
实际测试 | 无 | 9.5k/s | 12w/s | 1w/s | 1w/s | 4k/s[1亿数据上增量] 1w/s[百万数据上增量] |
优点 | 使用方便 实时插入 |
官方工具 可加载本地和远程CSV 实时插入 |
官方工具 占用资源少 |
官方 API | 增量更新 基于 BatchInserter |
官方工具 增量更新 可在线导入 |
缺点 | 速度慢 拼CQL复杂 |
速度较慢 不能动态传关系 |
要停数据库 要在JAVA环境中使用 |
要停止数据库 不能增量,只能初始化或覆盖 |
要停数据库 | 速度一般 |
load csv
简单导入
artists.csv
1,ABBA,1992
2,Roxette,1986
3,Europe,1979
4,The Cardigans,1992
导入:
LOAD CSV FROM 'https://neo4j.com/docs/cypher-manual/3.5/csv/artists.csv' AS line
CREATE (:Artist { name: line[1], year: toInteger(line[2])})
+-------------------+
| No data returned. |
+-------------------+
Nodes created: 4
Properties set: 8
Labels added: 4
导入包含头部的CSV
artists-with-headers.csv
Id,Name,Year
1,ABBA,1992
2,Roxette,1986
3,Europe,1979
4,The Cardigans,1992
导入:
LOAD CSV WITH HEADERS FROM 'https://neo4j.com/docs/cypher-manual/3.5/csv/artists-with-headers.csv' AS line
CREATE (:Artist { name: line.Name, year: toInteger(line.Year)})
+-------------------+
| No data returned. |
+-------------------+
Nodes created: 4
Properties set: 8
Labels added: 4
导入自定义分割符的CSV
artists-fieldterminator.csv
1;ABBA;1992
2;Roxette;1986
3;Europe;1979
4;The Cardigans;1992
导入:
LOAD CSV FROM 'https://neo4j.com/docs/cypher-manual/3.5/csv/artists-fieldterminator.csv' AS line FIELDTERMINATOR ';'
CREATE (:Artist { name: line[1], year: toInteger(line[2])})
+-------------------+
| No data returned. |
+-------------------+
Nodes created: 4
Properties set: 8
Labels added: 4
导入大量数据
如果数据量非常大,可以使用 USING PERIODIC COMMIT
,批量提交。默认会一次提交 1000 行。如:
USING PERIODIC COMMIT
LOAD CSV FROM 'https://neo4j.com/docs/cypher-manual/3.5/csv/artists.csv' AS line
CREATE (:Artist { name: line[1], year: toInteger(line[2])})
也可以自定义一次提交的行数:
USING PERIODIC COMMIT 500
LOAD CSV FROM 'https://neo4j.com/docs/cypher-manual/3.5/csv/artists.csv' AS line
CREATE (:Artist { name: line[1], year: toInteger(line[2])})
导入包含转义字符的数据
artists-with-escaped-char.csv
"1","The ""Symbol""","1992"
导入:
LOAD CSV FROM 'https://neo4j.com/docs/cypher-manual/3.5/csv/artists-with-escaped-char.csv' AS line
CREATE (a:Artist { name: line[1], year: toInteger(line[2])})
RETURN a.name AS name, a.year AS year, size(a.name) AS size
+------------------------------+
| name | year | size |
+------------------------------+
| "The "Symbol"" | 1992 | 12 |
+------------------------------+
1 row
Nodes created: 1
Properties set: 2
Labels added: 1
从 name 的 size 可以看出,引号是被算成名称的一部分的。
测试导入
load csv 不能动态传Label、RelationShip,所以测试时Label、RelationShip是写死的
数据如下:
31001986900050040272_1,上*思盾网络技术有限公司,31001986900050040272_1,100002,1,Main
8110201012800355973,上*翘佑投资管理有限公司,8110201012800355973,100002,1,Main
37940188000027492,山*赢任信息技术有限公司,37940188000027492,100002,1,Main
310069053018170148591,上*学清投资有限公司,310069053018170148591,100002,1,Main
310066344018010032559,上*捷申规划建筑设计工程有限公司,310066344018010032559,100002,1,Main
22810401040015380,成*斐讯科技有限公司,,100002,1,Main
导入10w节点
using periodic commit 1000 LOAD CSV FROM 'file:/User.csv' AS line
CREATE (:User { card: line[0], name: line[1], account: line[2], case: line[3], batch: line[4], source: line[5]})
+-------------------+
| No data returned. |
+-------------------+
Nodes created: 100000
Properties set: 500000
Labels added: 100000
3412 ms
10万数据(只有节点,没有关系)导入用时 3.412s
导入1kw节点
load csv from "file:/node_uuid_1kw.csv" as line return count(*);
+----------+
| count(*) |
+----------+
| 10000000 |
+----------+
1 row
7434 ms
using periodic commit 1000 load csv from "file:/node_uuid_1kw.csv" as line with line create (:Test {uuid:line.uuid, name:line.name});
+-------------------+
| No data returned. |
+-------------------+
Nodes created: 10000000
Properties set: 50000000
Labels added: 10000000
151498 ms
1千万数据(只有节点,没有关系)导入用时151.498s
导入100w关系
关系数据如:
35050188000700000572,414358360398,2018-06-14 18:25:42,3216,CNY,164314590,100002,1
00000397461552722,414358360398,2018-05-10 12:41:11,500000,CNY,109361082,100002,1
35050188000700000572,414358360398,2018-05-10 13:11:47,500000,CNY,113810907,100002,1
591157492615,414358360398,2017-12-01 10:36:02,40318.68,CNY,78089892,100002,1
6217886400000221882,414358360398,2017-12-01 11:47:45,38620.2,CNY,88287514,100002,1
591157492615,414358360398,2017-12-04 11:26:21,1,CNY,84487993,100002,1
591157492615,414358360398,2017-12-04 11:26:28,102042.95,CNY,84506860,100002,1
591157492615,414358360398,2017-12-04 11:26:29,67417.14,CNY,84508985,100002,1
591157492615,414358360398,2017-12-04 11:26:31,98653.15,CNY,84512839,100002,1
导入:
using periodic commit 200 load csv from "file:/Transfer.csv" as line
merge (n1:User {card:line[0]})
with n1, line
merge (n2:User {card:line[1]})
create (n1)-[:Transfer{from: line[0], target: line[1], time: line[2], money: toFloat(line[3]), cash: line[4], flow: line[5], case: line[6], batch: line[7]}]->(n2);
+-------------------+
| No data returned. |
+-------------------+
Relationships created: 1000000
75737 ms
创建100w关系用时75.737s。因为节点已经提前导入了,所以merge的时候节点全部存在,只创建了100w关系,没有创建节点。但是这种方式有一个弊端,关系要写死,在只有一种关系时适用,在有多种关系时,不适用。
还有一个不好的地方就是用的merge,uuid是String类型,会随着数据的增长速度变慢。
附: 一些查询命令:
全景图:
MATCH (n1:User)-[r:Transfer]->(n2:User) RETURN n1,r,n2
查主帐号间转帐信息:
MATCH (n1:User)-[r:Transfer]->(n2:User)
where n1.source="Main" and n2.source="Main"
RETURN n1,r,n2
查两个帐户之间的链路:
match p=(n:User {card:'37940188000027492'})-[:Transfer*2]->(m:User {card:'310069053018170148591'})
return distinct(p),m,n
neo4j-import
node.csv
uuid:ID(users),name:String,:Label
c63bc1e7dc594fd49fbe36dd664ff0a6,"维特",Label1
b52fb5f2266b4edbadc82b5ec4c430b8,"廖二松",Label2
d95d430cfeee47dd95f9bf5e0ec1ae93,"徐青偏",Label3
b2d1fffc8173461fa603d4fbb601b3ee,"杨础维",Label2
relationship.csv
uuid:START_ID(users),uuid:END_ID(users),:TYPE
c63bc1e7dc594fd49fbe36dd664ff0a6,b2d1fffc8173461fa603d4fbb601b3ee,RelationShip1
d95d430cfeee47dd95f9bf5e0ec1ae93,c63bc1e7dc594fd49fbe36dd664ff0a6,RelationShip2
b2d1fffc8173461fa603d4fbb601b3ee,b52fb5f2266b4edbadc82b5ec4c430b8,RelationShip3
b52fb5f2266b4edbadc82b5ec4c430b8,d95d430cfeee47dd95f9bf5e0ec1ae93,RelationShip1
导入1000w数据
IMPORT DONE in 27s 932ms.
Imported:
10000000 nodes
10000000 relationships
20000000 properties
Peak memory usage: 209.81 MB
空库初始化导入1千万数据(1kw节点 1kw关系 2kw属性,id使用integer,属性中只有数字和英文)花费27s 932m
BatchInster
可以认为BatchInster和batch-import速度一样
batch-import
node.csv
uuid:string:users,name:String,:label
c63bc1e7dc594fd49fbe36dd664ff0a6,"维特",Label1
b52fb5f2266b4edbadc82b5ec4c430b8,"廖二松",Label2
d95d430cfeee47dd95f9bf5e0ec1ae93,"徐青偏",Label3
b2d1fffc8173461fa603d4fbb601b3ee,"杨础维",Label2
relationship.csv
uuid:string:users,uuid:string:users,type
c63bc1e7dc594fd49fbe36dd664ff0a6,b2d1fffc8173461fa603d4fbb601b3ee,RelationShip1
d95d430cfeee47dd95f9bf5e0ec1ae93,c63bc1e7dc594fd49fbe36dd664ff0a6,RelationShip2
b2d1fffc8173461fa603d4fbb601b3ee,b52fb5f2266b4edbadc82b5ec4c430b8,RelationShip3
b52fb5f2266b4edbadc82b5ec4c430b8,d95d430cfeee47dd95f9bf5e0ec1ae93,RelationShip1
导入:
Using: Importer batch.properties /data/stale/data01/neo4j/neo4j-community-3.1.0/data/databases/test_uuid_1000w_graph.db /data/stale/data01/neo4j/node_uuid_100w.csv /data/stale/data01/neo4j/relationship_uuid_100w.csv
Using Existing Configuration File
..........
Importing 1000000 Nodes took 15 seconds
..........
Importing 1000000 Relationships took 16 seconds
Total import time: 92 seconds
在数据库中已有1kw数据的情况下,导入100w数据(100w节点 100w关系 200w属性,包含中文属性)花费92s。
apoc 导入
直接导入:
CALL apoc.load.csv('test_user_header.csv') yield map
with map
merge (:User{ca_id: map.ca_id, card: map.card, acct_id: map.acct_id, acct_name: map.acct_name, is_main: map.is_main})
批次导入:
CALL apoc.periodic.iterate('CALL apoc.load.csv("4959caf0e8864caca1e88330d7ce1892User.csv") yield map as row return row', 'CREATE (u:User) SET u = row', {batchSize:10000, iterateList:true, parallel:true});
CREATE INDEX ON :User (ca_id,card)
CALL apoc.periodic.iterate('CALL apoc.load.csv("4003dccd9d2b46ab96e7cda939d2f6a3TransferIn.csv" , {sep:"\t", header:TRUE}) yield map as row ',
'match (n:User) where n.ca_id = "1" and n.card = row.from with n, row
match (m:User) where m.ca_id = "1" and m.card = row.target
create (n)-[r:Transfer]->(m) set r = row, r.any_id = "3"',
{batch:10000, iterateList:true, parallel:false})
这里。使用了 apoc 包,要先下载该插件,放到 neo4j 安装目录下的 plugins 目录下。
两个CSV文件都是带表头的。内容如:
acct_id card acct_name is_main ca_id
2052 00000000 招商银行信用卡中心 0 2
2053 000000000000000 中国建设银行 0 2
2054 0000000000000000 代付 0 2
2055 00000000000000000 财付通支付科技有限公司客户备付金 0 2
2056 0000000000000000000 0000000000000000000 0 2
2057 0000000000000000000000 0000000000000000000000 0 2
2058 00000000000000000000000000 00000000000000000000000000 0 2
2059 00000000000000000000000000000000 00000000000000000000000000000000 0 2
2060 0000000000100608134 苏宁消费金融有限公司 0 2
2061 0000000000174892275 苏宁金融服务上海有限公司南京分公司 0 2
from target found time money ca_id
6222021602002353789 6212261602016903603 1 2016-10-12 10:57:06 0.01 2
1001278619005510123 6212261602016903603 1 2017-06-04 05:39:45 200000.00 2
1001278619005510123 6212261602016903603 1 2017-06-04 05:39:45 150000.00 2
1001278619005510123 6212261602016903603 1 2016-10-18 10:59:41 200000.00 2
1001278619005510123 6212261602016903603 1 2016-10-19 03:36:34 200000.00 2
1001278619005510123 6212261602016903603 1 2016-10-19 11:08:38 200000.00 2
1001278619005510123 6212261602016903603 1 2016-10-20 17:53:45 200000.00 2
1001278619005510123 6212261602016903603 1 2017-06-05 00:24:56 200000.00 2
1001278619005510123 6212261602016903603 1 2017-06-05 03:32:00 200000.00 2
load jdbc
除了从 csv 文件导入外,还可以直接从数据库导入。
CALL apoc.periodic.iterate('
call apoc.load.jdbc("jdbc:mysql://localhost:3306/northwind?user=root","company")',
'CREATE (p:Person) SET p += value', {batchSize:10000, parallel:true})
RETURN batches, total
结论
- neo4j-import导入速度快,但是要求是空库,导入时要停止neo4j,也就是脱机导入,而且你要提前处理好数据,数据最好不要有重复,如果有重复,可以导入时跳过,然后根据bad.log来查看或者修正这部分数据
- batch-import可以增量导入,但是要求导入时停止neo4j数据库(脱机导入),而且增量更新的数据不会和库里存在的数据对比,所以要求数据全是新的,否则会出现重复数据
- load csv比较通用,而且可以在neo4j数据库运行时导入,但是导入速度相对较慢,要提前整理好数据,而且不能动态创建 Label RelationShip
- apoc挺好用的,可以动态创建Label、RelationShip,但是速度一般
小型数据时,用 load csv 即可。
性能优化
数据需预热
使用bin/neo4j-shell 进入neo4j命令行界面,执行以下语句预热
MATCH (n)
OPTIONAL MATCH (n)-[r]->()
RETURN count(n.name) + count(r);
索引问题
首先使用explain ,看是否使用了索引,如果没有要添加索引
explain match data=(na)-[r]->(nb:company{name:'ss'}) return data;