Neo4j-notes

Neo4j使用总结
不断更新中…

1. Neo4j介绍

Neo4j是一种NoSQL数据库,而且属于NoSQL数据库里的图数据库。
常见的SQL数据库有MySQL Oracle等
常见的NoSQL数据库有Redis ES Mongdb Neo4j等
近几年比较流行的的图数据库有Neo4j Titan OrientDB Sparksee Virtuso ArangoDb Airaph GraphDB GraphBase等

Neo4j数据库比较适合处理关系,比如人和人之间的关系。
比较成功的应用有 领英 FaceBooke Twitter

2. Neo4j下载、安装、配置

neo4j分为企业版和社区版,社区版免费,企业版收费。
社区版企业版对比

2.1 下载

linux unix 推荐下载.tar.gz包
windows推荐下载.zip包,第一,和linux上目录及文件一样;第二,使用.exe安装的用户经常找不见配置,load csv以及其他操作时经常找不见目录

下载地址:
Neo4j所有版本的下载地址

 https://neo4j.com/artifact.php?name=neo4j-community-3.1.2-unix.tar.gz Linux Mac版
 https://neo4j.com/artifact.php?name=neo4j-community-3.1.2-windows.zip Windows版

2.2 安装

  1. 下载的压缩包neo4j-community-3.1.2.zip,解压完就可以用。
  2. 解压完进入bin目录,输入neo4j console就可以看到neo4j数据库启动了,可以在浏览器里输入http://localhost:7474来访问数据库,默认用户名、密码: neo4j/neo4j
  3. 如果想自定义配置,可以在${NEO4J_HOME}/conf/neo4j.conf修改对应配置

官网的安装说明

1
2
3
4
5
6
7
8
9
10
11
12
13
  打开终端
1 Open up your terminal/shell.
解压安装包
2 Extract the contents of the archive, using: tar -xf <filecode>.
For example,tar -xf neo4j-community-3.1.2-unix.tar.gz
the top level directory is referred to as NEO4J_HOME
$NEO4J_HOME指安装目录,在neo4j安装目录的bin目录使用neo4j console启动neo4j数据库
3 Run Neo4j using, $NEO4J_HOME/bin/neo4j console
Instead of 'neo4j console', you can use neo4j start to start the server process in the background.
在浏览器里输入http://localhost:7474即可访问数据库
4 Visit http://localhost:7474 in your web browser.
第一次登录数据库默认用户名密码是neo4j/neo4j,第一次登录需要修改密码
5 Change the password for the 'neo4j' account.

linux用户注意:
使用超级用户修改/etc/security/limits.conf文件,允许当前用户(neo4j)打开40000个文件

1
2
neo4j   soft    nofile  40000
neo4j hard nofile 40000

2.3 配置

常用配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#设置可以通过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

#(可以不配置)
export NEO4J_HOME=/usr/neo4j/neo4j-community-3.1.2

#启动Neo4j数据库
./${NEO4J_HOME}/bin/neo4j start
#停止数据库
neo4j stop

windows 使用neo4j console来启动数据库

使用nohup命令可以使neo4j数据库一直运行
nohup ./bin/neo4j console = neo4j start
要结束该命令时,用 kill -9 进程号 来关闭该进程

设置neo4j开机启动

1
2
3
4
vim /etc/rc.d/rc.local
在文件最后添加如下命令行:
/usr/share/neo4j/bin/neo4j start
其中/usr/share/neo4j/bin/是安装Neo4j的路径

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.

Cypher

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
: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);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
// delete single node
match (n:DictEntity) delete n;

// delete a node and connected relationships
match (n {name: 'name1'})-[r]-() delete n,r

// delete all nodes and relationships
match (n) OPTIONAL match (n)-[r]-() delete n,r

// 删除所有节点和关系
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)

// 查询一共有多少关系 // 不带方向的话结果是2倍
// Count all relationships
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属性上创建唯一性约束

drop constraint on (p:Person) assert p.name is unique;
DROP CONSTRAINT ON (book:Book) ASSERT book.isbn IS UNIQUE 删除isbn属性上的唯一性约束

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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 模糊匹配
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;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// 查询关系最多的节点
// 实际使用时,最好对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]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
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

参考
[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支持节点个数

您的支持将鼓励我继续创作