Neo4j使用笔记
1. Neo4j介绍
Neo4j是一种NoSQL数据库,原理是数学里的图论。
常见的SQL数据库有MySQL Oracle等
常见的NoSQL数据库有Redis ES Mongdb Neo4j等
近几年比较流行的的图数据库有Neo4j Titan OrientDB Sparksee Virtuso ArangoDb Airaph GraphDB GraphBase等
Neo4j数据库比较适合处理关系,比如人和人之间的社交关系。
比较成功的应用有 领英 FaceBook Twitter
2. Neo4j下载、安装、配置
Neo4j是开源免费的图数据库,社区版代码开源,企业版代码除集群相关的代码,其余代码全部开源
还有一点,社区版免费,企业版收费。
社区版最多允许 2^35
个节点,2^35
个关系,2^36
个属性
2.1 下载
Neo4j所有版本的下载地址
linux unix 推荐使用 .tar.gz包,windows推荐使用.zip包
这样做的好处就是解压完就可以使用,对目录结构、配置等比较了解
windows上使用.exe安装的用户经常找不见配置,load csv以及其他操作时经常找不见目录
2.2 安装使用
2.2.1 linux下安装使用
- tar -zxvf neo4j-community-3.1.2.tar.gz
- 进入bin目录,使用
neo4j console
启动Neo4j数据库,如果想让Neo4j后台运行,使用neo4j start
启动 - 在浏览器里输入http://localhost:7474来访问数据库,默认用户名、密码: neo4j/neo4j
- 如果想自定义配置,可以在${NEO4J_HOME}/conf/neo4j.conf修改对应配置
linux用户注意:
使用超级用户修改 /etc/security/limits.conf
文件,允许当前用户(neo4j)打开40000个文件
neo4j soft nofile 40000
neo4j hard nofile 40000
修改 /etc/pam.d/su
文件
session required pam_limits.so
设置neo4j开机启动
vim /etc/rc.d/rc.local
在文件最后添加如下命令:/usr/share/neo4j/bin/neo4j start
其中 /usr/share/neo4j/bin/
是Neo4j的安装路径
2.2.2 windows下安装使用
- 下载的压缩包neo4j-community-3.1.2.zip,解压完就可以用。
- 解压完进入bin目录,输入
neo4j console
就可以看到neo4j数据库启动了 - 在浏览器里输入http://localhost:7474来访问数据库,默认用户名、密码: neo4j/neo4j
- 如果想自定义配置,可以在${NEO4J_HOME}/conf/neo4j.conf修改对应配置
2.3 配置
常用配置
#设置可以通过ip当问Neo4j数据库
dbms.connectors.default_listen_address=0.0.0.0
#历史版本请修改
dbms.connector.http.address=0.0.0.0:7474
org.neo4j.server.webserver.address=0.0.0.0
neo4j数据库设置初始密码
bin/neo4j-admin set-initial-password yourpassword
3. Neo4j使用
Neo4j里非常重要的一些思想,一个节点、一条边就是一个对象
节点可以有多个Label、边只能有一个RelationShip
Neo4j是no schema的数据库,导入数据前不需要定义结构
不要用关系型数据库的思维来对待Neo4j
A row is a node
A table name is a label name
Properties
Both nodes and relationships can have properties.
Properties are named values where the name is a string. The supported property values are:
• Numeric values,
• String values,
• Boolean values,
• Collections of any other type of value.
Labels have an id space of an int, meaning the maximum number of labels the database can contain is roughly 2 billion.
Paths
A path is one or more nodes with connecting relationships, typically retrieved as a query or traversal result
Neo4j is a schema-optional graph database
You can use Neo4j without any schema. Optionally you can introduce it in order to gain performance or modeling benefits. This allows a way of working where the schema does not get in your way until you are at a stage where you want to reap the benefits of having one.
Indexs
Performance is gained by creating indexes, which improve the speed of looking up nodes in the database.
Neo4j启动后动态修改配置
//
CALL dbms.setConfigValue('dbms.logs.query.enabled', 'true')
dbms.checkpoint.iops.limit Limit the number of IOs the background checkpoint process will consume per second.
dbms.logs.query.enabled Log executed queries that take longer than the configured threshold, dbms.logs.query.threshold.
dbms.logs.query.rotation.keep_number Maximum number of history files for the query log.
dbms.logs.query.rotation.size The file size in bytes at which the query log will auto-rotate.
dbms.logs.query.threshold If the execution of query takes more time than this threshold, the query is logged - provided query logging is enabled.
dbms.transaction.timeout The maximum time interval of a transaction within which it should be completed.
Cypher
:help 帮助页面
:schema 查看数据库结构
:schema ls -l :Person
:server change-password // 修改密码
CALL dbms.changePassword("newpassword") // (旧版本)修改密码
:server connect 连接
:play sysinfo 查看系统信息
// List node labels 查询所有的label
CALL db.labels()
// List relationship types 查询所有的type
CALL db.relationshipTypes()
// What is related, and how 查询数据里的节点和关系 类似于 SQL的desc
CALL db.schema()
// List functions
CALL dbms.functions()
// List procedures
CALL dbms.procedures()
CALL dbms.listQueries() ;
CALL dbms.killQuery(queryId);
// delete single node
// 删除id=1的节点
match (n:DictEntity) where id(n) = 1 delete n;
// delete a node and connected relationships
// 删除节点和关系
match (n:DictEntity {name: 'zhangsan'})-[r]-() delete r,n
// delete all nodes and relationships
// 删除所有节点和关系
match (n) OPTIONAL match (n)-[r]-() delete n,r
// delete all nodes and relationships
// 删除所有节点和关系
// 容易内存溢出
match (n) detach delete n;
match (n)-[r]-() where n.name = '词典1' delete r 删除关系
match (n:DictEntity) where n.name="词典1" delete n 删除节点
match (n:DictEntity {name:"词典1"}) delete n
// Count all nodes
// 查询一共有多少节点
match (n) RETURN count(n)
// Count all relationships
// 查询一共有多少关系 // 不带方向的话结果是2倍
match ()-->() RETURN count(*);
match ()-[r]->() return count(r);
// What kind of nodes exist
// Sample some nodes, reporting on property and relationship counts per node.
match (n) where rand() <= 0.1
RETURN
DISTINCT labels(n),
count(*) AS SampleSize,
avg(size(keys(n))) as Avg_PropertyCount,
min(size(keys(n))) as Min_PropertyCount,
max(size(keys(n))) as Max_PropertyCount,
avg(size( (n)-[]-() ) ) as Avg_RelationshipCount,
min(size( (n)-[]-() ) ) as Min_RelationshipCount,
max(size( (n)-[]-() ) ) as Max_RelationshipCount
// What is related, and how
// Sample the graph, reporting the patterns of connected labels,
// with min, max, avg degrees and associated node and relationship properties.
match (n) where rand() <= 0.1
match (n)-[r]->(m)
WITH n, type(r) as via, m
RETURN labels(n) as from,
reduce(keys = [], keys_n in collect(keys(n)) | keys + filter(k in keys_n where NOT k IN keys)) as props_from,
via,
labels(m) as to,
reduce(keys = [], keys_m in collect(keys(m)) | keys + filter(k in keys_m where NOT k IN keys)) as props_to,
count(*) as freq
// 在用户结点的用户名属性上创建索引 (除了结点名和字段名,cypther不区分大小写)
create index on :Person(name);
// 删除索引
drop index on :Person(name);
create constraint on (p:Person) assert p.name is unique; //
CREATE CONSTRAINT ON (book:Book) ASSERT book.isbn IS UNIQUE; // 在Book对象isbn属性上创建唯一性约束
// 删除Person类别上的name属性 索引
drop constraint on (p:Person) assert p.name is unique;
// 删除isbn属性上的唯一性约束
DROP CONSTRAINT ON (book:Book) ASSERT book.isbn IS UNIQUE
//
DROP CONSTRAINT ON (book:Book) ASSERT exists(book.isbn)
PROFILE 在查询前加上prifile可以查看数据库查询的详细流程
match (a:Person), (b:Person) where a.name = 'zhangsan' and b.name = 'lisi'
merge (a)-[r:RELTYPE]->(b) return r
// 模糊匹配
match (n:Person) where n.name =~ '张.*' return n
// 包含
match (n:Person) where n.name contains '张' return n;
// 去重
match (n:Person) with n.name as name return distinct name;
// Count all nodes
// 查询一共有多少节点
match (n) RETURN count(n)
// Count all relationships
// 查询一共有多少关系
match ()-->() RETURN count(*);
// Get some data 随便查一些数据
match (n1)-[r]->(n2) RETURN r, n1, n2 LIMIT 25
// 查询一共有多少种节点
call db.labels();
match (n) return distinct label(n);
// 查询一共有多少关系
call db.relationshipTypes()
// 查询数据库里的所有属性
match (n) unwind keys(n) as allkeys return distinct allkeys;
// 查询关系最多的节点
// 实际使用时,最好对n加个范围,要不然 全图扫描
// 使用with 和 别名,能减少一次count(*)的查询
match (n:Movie)--() with n.title as title, count(*) as count return title, count order by count desc limit 1;
match (n:Movie)-[r]-() with n.tile as title, count(r) as count return title, count order by count desc limit 1;
// 查询孤立节点
match (n) where not (n)--() return id(n);
// 查询有3条关系的节点
match (n:Test)-->(m) with count(*) as count, n as result where count =3 return result limit 1;
// 查询有3度关系的节点
match (n:Test)-[r:RelationShip*3]->(m) return n limit 1;
// 查询有1到3度关系的节点
match (n:Test)-[r:RelationShip*1..3]->(m) return n limit 10;
match (p:Person) where id(p) > 184 set p.number=p.序号, p.name=p.姓名, p.class = p.班级, p.sex = p.性别 remove p.序号,p.姓名,p.班级,p.性别 return p;
// Person对象有个sex属性,因为业务需要想改成gender属性
match (p:Person {name:'张三'}) set p.gender = p.sex remove p.sex return p
// 关系r的名字叫IsFriend,因为业务需要改成汉语的名字
match (a)-[r:IsFrend]->(b) create (a)-[r2:朋友]->(b) set r2.id = r.id delete r
match (p1:Person)-[r:isFrend*1..6 {friend:1}]->(p2:Person) return p1,r,p2
match (a:A)-[r1:AB]-(b:B)-[r2:BC]-(c:C) where not (a)-[:AC]-(c) return a,r1,b,r2,c
match (n:Person {name:'lisi'}) with n skip 1 delete n ;
(a:Person {name:'a'})-[r:RelationShip {date:'2017-06-22 12:00:00'}]->(c:Person {name:'c'})<-[r2:RelationShip {date:'2017-06-22 12:30:00'}]-(b:Person {name:'b'})
(d:Person {name:'d'})-[r:RelationShip {date:'2017-06-22 12:00:00'}]->(e:Person {name:'e'})
想删除a, d, e
// 创建节点
merge (a:Person {name:'a'})-[r:RelationShip {date:'2017-06-22 12:00:00'}]->(c:Person {name:'c'})<-[r2:RelationShip {date:'2017-06-22 12:30:00'}]-(b:Person {name:'b'}) return r,r2;
merge (d:Person {name:'d'})-[r:RelationShip {date:'2017-06-22 12:00:00'}]->(e:Person {name:'e'}) return r
// 分三次执行
match (n:Person)-[r]->(m:Person) where (r.date >='2017-06-22 11:00:00' and r.date<='2017-06-22 12:29:59') set n.flag = '1' , m.flag = '1' delete r ;
match (n:Person)-[r]-(m:Person) with count(r) as count , n where count > 0 set n.flag = '0' return count,n;
match (n:Person) where n.flag = '1' delete n;
As the indexes are created after the nodes are inserted, their population happens asynchronously, so we use schema await (a shell command) to block until they are populated.
schema await
[r:Person*3..4]
neo4j-sh (?)$ CALL dbms.procedures();
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| name | signature | description |
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| "db.awaitIndex" | "db.awaitIndex(index :: STRING?, timeOutSeconds = 300 :: INTEGER?) :: VOID" | "Wait for an index to come online (for example: CALL db.awaitIndex(":Person(name)"))." |
| "db.awaitIndexes" | "db.awaitIndexes(timeOutSeconds = 300 :: INTEGER?) :: VOID" | "Wait for all indexes to come online (for example: CALL db.awaitIndexes("500"))." |
| "db.constraints" | "db.constraints() :: (description :: STRING?)" | "List all constraints in the database." |
| "db.createLabel" | "db.createLabel(newLabel :: STRING?) :: VOID" | "Create a label" |
| "db.createProperty" | "db.createProperty(newProperty :: STRING?) :: VOID" | "Create a Property" |
| "db.createRelationshipType" | "db.createRelationshipType(newRelationshipType :: STRING?) :: VOID" | "Create a RelationshipType" |
| "db.index.explicit.addNode" | "db.index.explicit.addNode(indexName :: STRING?, node :: NODE?, key :: STRING?, value :: ANY?) :: (success :: BOOLEAN?)" | "Add a node to an explicit index based on a specified key and value" |
| "db.index.explicit.addRelationship" | "db.index.explicit.addRelationship(indexName :: STRING?, relationship :: RELATIONSHIP?, key :: STRING?, value :: ANY?) :: (success :: BOOLEAN?)" | "Add a relationship to an explicit index based on a specified key and value" |
| "db.index.explicit.auto.searchNodes" | "db.index.explicit.auto.searchNodes(query :: ANY?) :: (node :: NODE?, weight :: FLOAT?)" | "Search nodes in explicit automatic index. Replaces `START n=node:node_auto_index('key:foo*')`" |
| "db.index.explicit.auto.searchRelationships" | "db.index.explicit.auto.searchRelationships(query :: ANY?) :: (relationship :: RELATIONSHIP?, weight :: FLOAT?)" | "Search relationship in explicit automatic index. Replaces `START r=relationship:relationship_auto_index('key:foo*')`" |
| "db.index.explicit.auto.seekNodes" | "db.index.explicit.auto.seekNodes(key :: STRING?, value :: ANY?) :: (node :: NODE?)" | "Get node from explicit automatic index. Replaces `START n=node:node_auto_index(key = 'A')`" |
| "db.index.explicit.auto.seekRelationships" | "db.index.explicit.auto.seekRelationships(key :: STRING?, value :: ANY?) :: (relationship :: RELATIONSHIP?)" | "Get relationship from explicit automatic index. Replaces `START r=relationship:relationship_auto_index(key = 'A')`" |
| "db.index.explicit.drop" | "db.index.explicit.drop(indexName :: STRING?) :: (type :: STRING?, name :: STRING?, config :: MAP?)" | "Remove an explicit index - YIELD type,name,config" |
| "db.index.explicit.existsForNodes" | "db.index.explicit.existsForNodes(indexName :: STRING?) :: (success :: BOOLEAN?)" | "Check if a node explicit index exists" |
| "db.index.explicit.existsForRelationships" | "db.index.explicit.existsForRelationships(indexName :: STRING?) :: (success :: BOOLEAN?)" | "Check if a relationship explicit index exists" |
| "db.index.explicit.forNodes" | "db.index.explicit.forNodes(indexName :: STRING?) :: (type :: STRING?, name :: STRING?, config :: MAP?)" | "Get or create a node explicit index - YIELD type,name,config" |
| "db.index.explicit.forRelationships" | "db.index.explicit.forRelationships(indexName :: STRING?) :: (type :: STRING?, name :: STRING?, config :: MAP?)" | "Get or create a relationship explicit index - YIELD type,name,config" |
| "db.index.explicit.list" | "db.index.explicit.list() :: (type :: STRING?, name :: STRING?, config :: MAP?)" | "List all explicit indexes - YIELD type,name,config" |
| "db.index.explicit.removeNode" | "db.index.explicit.removeNode(indexName :: STRING?, node :: NODE?, key :: STRING?) :: (success :: BOOLEAN?)" | "Remove a node from an explicit index with an optional key" |
| "db.index.explicit.removeRelationship" | "db.index.explicit.removeRelationship(indexName :: STRING?, relationship :: RELATIONSHIP?, key :: STRING?) :: (success :: BOOLEAN?)" | "Remove a relationship from an explicit index with an optional key" |
| "db.index.explicit.searchNodes" | "db.index.explicit.searchNodes(indexName :: STRING?, query :: ANY?) :: (node :: NODE?, weight :: FLOAT?)" | "Search nodes in explicit index. Replaces `START n=node:nodes('key:foo*')`" |
| "db.index.explicit.searchRelationships" | "db.index.explicit.searchRelationships(indexName :: STRING?, query :: ANY?) :: (relationship :: RELATIONSHIP?, weight :: FLOAT?)" | "Search relationship in explicit index. Replaces `START r=relationship:relIndex('key:foo*')`" |
| "db.index.explicit.searchRelationshipsBetween" | "db.index.explicit.searchRelationshipsBetween(indexName :: STRING?, in :: NODE?, out :: NODE?, query :: ANY?) :: (relationship :: RELATIONSHIP?, weight :: FLOAT?)" | "Search relationship in explicit index, starting at the node 'in' and ending at 'out'." |
| "db.index.explicit.searchRelationshipsIn" | "db.index.explicit.searchRelationshipsIn(indexName :: STRING?, in :: NODE?, query :: ANY?) :: (relationship :: RELATIONSHIP?, weight :: FLOAT?)" | "Search relationship in explicit index, starting at the node 'in'." |
| "db.index.explicit.searchRelationshipsOut" | "db.index.explicit.searchRelationshipsOut(indexName :: STRING?, out :: NODE?, query :: ANY?) :: (relationship :: RELATIONSHIP?, weight :: FLOAT?)" | "Search relationship in explicit index, ending at the node 'out'." |
| "db.index.explicit.seekNodes" | "db.index.explicit.seekNodes(indexName :: STRING?, key :: STRING?, value :: ANY?) :: (node :: NODE?)" | "Get node from explicit index. Replaces `START n=node:nodes(key = 'A')`" |
| "db.index.explicit.seekRelationships" | "db.index.explicit.seekRelationships(indexName :: STRING?, key :: STRING?, value :: ANY?) :: (relationship :: RELATIONSHIP?)" | "Get relationship from explicit index. Replaces `START r=relationship:relIndex(key = 'A')`" |
| "db.indexes" | "db.indexes() :: (description :: STRING?, label :: STRING?, properties :: LIST? OF STRING?, state :: STRING?, type :: STRING?, provider :: MAP?)" | "List all indexes in the database." |
| "db.labels" | "db.labels() :: (label :: STRING?)" | "List all labels in the database." |
| "db.propertyKeys" | "db.propertyKeys() :: (propertyKey :: STRING?)" | "List all property keys in the database." |
| "db.relationshipTypes" | "db.relationshipTypes() :: (relationshipType :: STRING?)" | "List all relationship types in the database." |
| "db.resampleIndex" | "db.resampleIndex(index :: STRING?) :: VOID" | "Schedule resampling of an index (for example: CALL db.resampleIndex(":Person(name)"))." |
| "db.resampleOutdatedIndexes" | "db.resampleOutdatedIndexes() :: VOID" | "Schedule resampling of all outdated indexes." |
| "db.schema" | "db.schema() :: (nodes :: LIST? OF NODE?, relationships :: LIST? OF RELATIONSHIP?)" | "Show the schema of the data." |
| "dbms.changePassword" | "dbms.changePassword(password :: STRING?) :: VOID" | "Change the current user's password. Deprecated by dbms.security.changePassword." |
| "dbms.components" | "dbms.components() :: (name :: STRING?, versions :: LIST? OF STRING?, edition :: STRING?)" | "List DBMS components and their versions." |
| "dbms.functions" | "dbms.functions() :: (name :: STRING?, signature :: STRING?, description :: STRING?)" | "List all user functions in the DBMS." |
| "dbms.listConfig" | "dbms.listConfig(searchString = :: STRING?) :: (name :: STRING?, description :: STRING?, value :: STRING?)" | "List the currently active config of Neo4j." |
| "dbms.procedures" | "dbms.procedures() :: (name :: STRING?, signature :: STRING?, description :: STRING?)" | "List all procedures in the DBMS." |
| "dbms.queryJmx" | "dbms.queryJmx(query :: STRING?) :: (name :: STRING?, description :: STRING?, attributes :: MAP?)" | "Query JMX management data by domain and name. For instance, "org.neo4j:*"" |
| "dbms.security.changePassword" | "dbms.security.changePassword(password :: STRING?) :: VOID" | "Change the current user's password." |
| "dbms.security.createUser" | "dbms.security.createUser(username :: STRING?, password :: STRING?, requirePasswordChange = true :: BOOLEAN?) :: VOID" | "Create a new user." |
| "dbms.security.deleteUser" | "dbms.security.deleteUser(username :: STRING?) :: VOID" | "Delete the specified user." |
| "dbms.security.listUsers" | "dbms.security.listUsers() :: (username :: STRING?, flags :: LIST? OF STRING?)" | "List all local users." |
| "dbms.security.showCurrentUser" | "dbms.security.showCurrentUser() :: (username :: STRING?, flags :: LIST? OF STRING?)" | "Show the current user. Deprecated by dbms.showCurrentUser." |
| "dbms.showCurrentUser" | "dbms.showCurrentUser() :: (username :: STRING?, flags :: LIST? OF STRING?)" | "Show the current user." |
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
46 rows
96 ms
neo4j-sh (?)$
github例子
https://github.com/neo4j-examples/movies-java-spring-data-neo4j-4
批量执行cpyther语句
要创建上百个索引,想找一个简单的办法
./neo4j-shell -c < /data/stale/data01/neo4j/create_index.cypther
热启动命令1
MATCH (n)
OPTIONAL MATCH (n)-[r]->()
RETURN count(n.prop) + count(r.prop);
1.1亿节点
neo4j-sh (?)$ match (n) optional match (n)-[r]->() return count(n.prop), count(r.prop);
+-------------------------------+
| count(n.prop) + count(r.prop) |
+-------------------------------+
| 0 |
+-------------------------------+
1 row
556974 ms
neo4j-sh (?)$ match (n) optional match (n)-[r]->() return count(n)+count(r);
+-------------------+
| count(n)+count(r) |
+-------------------+
| 262701611 |
+-------------------+
1 row
172145 ms
neo4j-sh (?)$ match (n) optional match (n)-[r]->() return count(n.uuid), count(r);
+---------------------------+
| count(n.uuid) | count(r) |
+---------------------------+
| 1233398 | 111001002 |
+---------------------------+
1 row
260481 ms
热启动命令2
CALL apoc.warmup.run();
neo4j-sh (?)$ CALL apoc.warmup.run();
+--------------------------------------------------------------------------------------------------------------------------+
| pageSize | nodesPerPage | nodesTotal | nodePages | nodesTime | relsPerPage | relsTotal | relPages | relsTime | totalTime |
+--------------------------------------------------------------------------------------------------------------------------+
| 8192 | 546 | 110000000 | 201466 | 0 | 240 | 110000000 | 458334 | 0 | 1 |
+--------------------------------------------------------------------------------------------------------------------------+
1 row
1416 ms
社区版和企业版有什么区别。其实他们在功能上没有本质区别。主要区别在如下几点:
1、容量:社区版最多支持 320 亿个节点、320 亿个关系和 640 亿个属性,而企业版没有这个限制;
2、并发:社区版只能部署成单实例,不能做集群。而企业版可以部署成高可用集群或因果集群,从而可以解决高并发量的问题;
3、容灾:由于企业版支持集群,部分实例出故障不会影响整个系统正常运行;
4、热备:社区版只支持冷备份,即需要停止服务后才能进行备份,而企业版支持热备,第一次是全量备份,后续是增量备份;
5、性能:社区版最多用到 4 个内核,而企业能用到全部内核,且对性能做了精心的优化;
6、支持:企业版客户能得到 5X10 电话支持(Neo4j 美国电话、邮件,微云数聚电话、微信、邮件);
References
[1] http://neo4j.com/docs/operations-manual/3.1/
[2] https://neo4j.com/docs/developer-manual/3.1/
[3] http://neo4j.com/docs/2.2.9/query-delete.html
[4] https://neo4j.com/docs/developer-manual/3.1/cypher/
[5] https://neo4j.com/blog/neo4j-3-1-ga-release/?ref=home
[6] https://neo4j.com/docs/developer-manual/3.1/cypher/clauses/set/
[7] https://neo4j.com/docs/operations-manual/3.2/installation/linux/debian/#multiple-java-versions
[8] https://neo4j.com/docs/operations-manual/current/installation/windows/
[9] http://neo4j.com/docs/developer-manual/current/extending-neo4j/procedures/
[10] https://neo4j.com/developer/guide-importing-data-and-etl/ 使用ETL方式导入Neo4j
[11] https://neo4j.com/developer/guide-import-csv/ 使用csv文件方式导入Neo4j
[12] https://neo4j.com/docs/
[13] https://neo4j.com/blog/neo4j-3-0-massive-scale-developer-productivity/#capabilities-data-size neo4j支持节点个数
[14] https://neo4j.com/developer/kb/warm-the-cache-to-improve-performance-from-cold-start/
https://stackoverflow.com/questions/41762487/neo4j-bulk-import-and-indexing
https://neo4j.com/blog/batchinsert-auto-indexing-and-friend-recommendation-with-neo4j/
http://grokbase.com/t/gg/neo4j/146xv2wa77/how-i-can-create-index-schema-legacy-after-importing-data-by-batch-inserter-from-neo4j-shell
https://stackoverflow.com/questions/44996896/how-to-execute-cypher-file-using-neo4j-3-1-4-not-through-zip-file
https://github.com/neo4j/cypher-shell/issues/96
https://stackoverflow.com/questions/15161221/neo4j-script-file-format-is-there-any