图形数据库Neo4j入门

Neo4j是一个高性能的,NOSQL图形数据库,它将结构化数据存储在网络上而不是表中。它是一个嵌入式的基于磁盘的、具备完全的事务特性的Java持久化引擎,但是它将结构化数据存储在网络(从数学角度叫做图)上而不是表中。Neo4j也可以被看作是一个高性能的图引擎,该引擎具有成熟数据库的所有特性。程序员工作在一个面向对象的、灵活的网络结构下而不是严格、静态的表中——但是他们可以享受到具备完全的事务特性、企业级的数据库的所有好处。

一、基本概念

1.1 什么是图数据库

图数据库名字的由来其实与其在底层的存储方式有关,Neo4j底层会以图的方式把用户定义的节点以及关系存储起来,通过这种方式,可是高效的实现从某个节点开始,通过节点与节点间关系,找出两个节点间的联系。

从这段描述中可以猜得到,在Neo4j中最重要的两个元素就是节点和关系。说到节点和关系,就必须引出一个非常重要的概念,属性图模型(Property Graph Model)。如下所示:

  • 一个图中会记录节点和关系
  • 关系可以用来关联两个节点
  • 节点和关系都可以拥有自己的属性
  • 可以赋予节点多个标签(类别)

接下来用一个实际的例子来解释下这个模型,最近在虎扑上被吹捧的特别厉害的恩比德大帝曾经爆出过他自己PS的一张图,隔空示爱蕾哈娜,参考下图

image

我们尝试用Property Graph Model来表示这样的一个关系。

image

上图中定义了两个节点,恩比德和蕾哈娜,类别分别是球员和歌手,分别拥有身高,国籍以及生日,血型属性。两个节点之间通过一个“喜欢”关系关联,“喜欢”关系拥有自己的一个属性“自从”,属性值为2014年。

1.2 Neo4j数据库的优势

  • 在创建节点的时候就已经把关系给建立起来,避免了在复杂查询场景下的处理
  • 由于底层直接以图的形式存储节点和关系,在查询的时候可以使时间复杂度保持在常数级别。
  • 基于JVM实现
  • 提供一套易于理解的查询语言Cypher以及内置的可视化UI
  • 很好的支持ACID,有事务机制

1.3 Neo4j安装及实践

为了方便大家搭建环境,本文还是会用Docker来运行一个包含Neo4j的容器,Docker下载安装参考这里。在下文中会根据之前引用的恩比德大帝的例子来介绍Cypher语句。

从Docker Hub上pull一个Neo4j的镜像

docker pull neo4j

启动一个Neo4j的容器, 需要在你的Home目录下建立一个/neo4j/data文件夹

docker run --publish=7474:7474 --publish=7687:7687 --volume=$HOME/neo4j/data:/data neo4j 

在浏览器中访问loalhost:7474可以看到如下的UI界面,输入默认用户名和密码neo4j即可连接到neo4j数据库。

创建一个球员节点恩比德

CREATE (embiid: PLAYER {name: 'Embiid', height: '2.13m', nationality: 'Cameroon'}) 

image

可以通过点击Neo4j左边的侧边栏中的PLAYER标签看到刚才新建的节点。

创建歌手蕾哈娜节点以及“喜欢”关系

MATCH (embiid:PLAYER{name:'Embiid'})
MERGE (embiid)-[:LIKES{since:'2014'}]->(Rihanna: SINGER{name:'Rihanna', dob:'1988/2/20',bloodType:'O'})

创建完以后UI如下图所示,图中可以看出新建的蕾哈娜节点和之前的恩比德节点通过LIKES关系关联了起来

查询返回所有喜欢蕾哈娜的PLAYER节点

MATCH(p:PLAYER)-[:LIKES]->(:SINGER{name:'Rihanna'})
RETURN p;

image
image

1.4 Neo4j常用使用场景

  • 社交网络
  • 基于图的搜索
  • 推荐引擎
  • 企业基础设施及网络架构
  • 等等

1.5 neo4j存储节点

neo4j存储节点

1.5.1 标签(Label)

Neo4j中,一个节点可以有一个以上的标签,从现实世界的角度去看,一个标签可以认为节点的某个类别,比如BOOKMOVIE等等。

1.5.2 节点(Node)

节点是指一个实实在在的对象,这个对象可以有好多的标签,表示对象的种类,也可以有好多的属性,描述其特征,节点与节点之间还可以形成多个有方向(或者没有方向)的关系。

1.5.3 关系(Relationship)

用来描述节点与节点之间的关系,这也是图数据与与普通数据库最大的区别,正是因为有这些关系的存在,才可以描述那些我们普通行列数据库所很难表示的网状关系,比如我们复杂的人际关系网,所谓的六度理论,就可以很方便的用图数据库进行模拟,比如我们大脑神经元之间的连接方式,都是一张复杂的网。

有一点需要重点注意,关系可以拥有属性。

1.5.4 属性(Property)

描述节点的特性,采用的是Key-Value结构,可以随意设定来描述节点的特征。

二、查询语法(CQL)

序号 关键字 关键字作用
1 CREATE 创建
2 MATCH 匹配
3 RETURN 加载
4 WHERE 过滤检索条件
5 DELETE 删除节点和关系
6 REMOVE 删除节点和关系的属性
7 ORDER BY 排序
8 SET 添加或更新属性

1. 基本查找match return

neo4j使用的查询语法是Cypher语法与我们常用的SQL查询语法不一样,但是在初步的学习之后,觉得他们之间使用的思路有很多重叠的地方,整个语句的执行流程也和SQL有比较多相似的地方。

# 创建两个节点,一个子节点(Mask),一个父节点(Old_mask),他们之间是属于父子关系
# 其中create表示新建
# p 表示这个节点的别名
# PERSON 表示节点p的标签PERSON的属性
# {} 大括号中间的键值对,表示p这个节点作为PERSON这个标签类别所拥有的属性
# -[r:SON_OF]-> 表示节点p指向节点f,他们之间的关系是SON_OF,这个关系的别名是r,r可以拥有属于自己的属性
# return 表示执行这段语句之后,需要返回的对象,return p,r,f 表示返回 节点p,节点f,以及他们之间的关系r
create(p:PERSON {name:"Mask",age:30,heigh:180,weight:80})-[r:SON_OF]->(f:PERSON {name:"OLD_Mask",age:55,heigh:160,weight:60}) return p,r,f
两个带有关系的节点

返回数据:

返回数据列表

2. 查找指定节点、指定属性、指定关系的节点、关系

# MATCH 匹配命令
# return 后面的别名p还可以利用as 设置指定的返回值名称,如 p as userName

match (p:PERSON {name:"Mask"})-[r]-(n) return p,r,n

命令执行结果:

查找结果
查找结果

where关键字类似于SQL里面的where关键字,可以通过运算符== >= ...来过滤一些查询条件。

3. 对查找结果进行排序order by,并限制返回条数 limit

order by关键字与SQL里面是一样的操作,后面跟上需要根据排序的关键字,limit的操作是指定输出前几条

# 这里利用order by来指定返回按照Person.name来排序
# limit 表示只返回前3条数据
match(p:Person) return p order by p.name limit 3

查找结果:

返回排序后前3条结果

4. 删除节点delete命令

删除节点的操作也是通过dlete来操作,如果被删除的节点存在Relationship,那么单独删除该节点的操作会不成功,所以如果想删除一个已经存在关系的节点,需要同时将关系进行删除。

删除一个不存在Relationship节点,会报错:

删除一个存在relationship的节点不成功

删除一个节点记忆与他有关的关系,成功:

删除节点以及与它有关的关系
# 删除指定条件的节点
# 先通过匹配关键字match找到匹配元素,然后通过delete关键字指定删除
match(p:PERSON {name:"teacher_wange"}) delete p

# 删除节点和节点相关的关系
match (p:Person {name:"lisi"})-[r]-() delete p,r

5. 删除属性remove命令

remove命令是用来删除节点或者关系的属性

删除属性前的节点:

删除属性前的节点

删除birthday属性后的节点:

删除birthday属性后的节点

6. neo4j的字符串函数

upper,lower,substring,replac四种字符串的操作,其中upperlower在将来的版本中会被修改为toupper/tolower

大写转换操作结果:

大小写转换

7. 聚合函数AGGREGATION

常用的聚合函数有COUNTMAXMINAVGSUM等五种。

match(p:Person) return p.name as name,p.age as age,count(p) as count,max(p.age) as maxAge,min(p.age) as minAge,avg(p.age) as avgAge,sum(p.age) as sumAge

聚合函数操作结果:

聚合函数

8. 关系函数

序号 函数名 函数功能描述
1 STARTNODE 查找关系的起始点
2 ENDNODE 查找关系的终点
3 ID 查找关系的ID
4 TYPE 查找关系的类型,也就是我们在图表中看到的名称
# 先获取关系,然后通过关系函数来获取关系的id、type、起始节点、终止节点等等信息

match ()-[r:SON_OF]-() return startnode(r).name as start_node, endnode(r).name as end_node,id(r) as relationship_id ,type(r) as realtionship_type

关系查询结果:

关系查询结果

三、 在Java中使用

3.1 原生的Neo4j Java API

Neo4j Java API的设计思路及基本概念:

  1. Label接口,表示标签,实现这个接口的类,就可以当标签使用;
  2. Relationship接口,别是关系,实现这个接口的类,就可以表示关系;
  3. 通过GraphDatabaseFactory这个类的实例化对象可以获取GraphDatabaseService实例;
  4. GraphDatabaseService实例对象,可以获取一个操作事务,通过这个事务可以实现任何操作异常的回滚,操作成功需要调用tx.success()方法;
  5. GraphDatabaseService 对象可以创建节点node;
  6. 节点node可以设置属性setProperty(key,value);
  7. 节点node可以创建关系Relationship,Relationship也可以通过setProperty(key,value)来设置属性;

枚举标签Label

package com.tp.ne4oj.java.examples;
import org.neo4j.graphdb.Label;
public enum Tutorials implements Label {
    JAVA,SCALA,SQL,NEO4J;
}

枚举关系Realationship

package com.tp.neo4j.java.examples;
import org.neo4j.graphdb.RelationshipType;
public enum TutorialRelationships implements RelationshipType{
    JVM_LANGIAGES,NON_JVM_LANGIAGES;
}

获取操作对象

GraphDatabaseFactory dbFactory = new GraphDatabaseFactory();
GraphDatabaseService db= dbFactory.newEmbeddedDatabase("C:/TPNeo4jDB");

启动neo4j数据库事务

try (Transaction tx = graphDb.beginTx()) {
    // Perform DB operations                
    tx.success();
}

整体代码

package com.tp.neo4j.java.examples;

import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.Transaction;
import org.neo4j.graphdb.factory.GraphDatabaseFactory;

public class Neo4jJavaAPIDBOperation {
public static void main(String[] args) {
    GraphDatabaseFactory dbFactory = new GraphDatabaseFactory();
    GraphDatabaseService db= dbFactory.newEmbeddedDatabase("C:/TPNeo4jDB");
    try (Transaction tx = db.beginTx()) {

        Node javaNode = db.createNode(Tutorials.JAVA);
        javaNode.setProperty("TutorialID", "JAVA001");
        javaNode.setProperty("Title", "Learn Java");
        javaNode.setProperty("NoOfChapters", "25");
        javaNode.setProperty("Status", "Completed");                

        Node scalaNode = db.createNode(Tutorials.SCALA);
        scalaNode.setProperty("TutorialID", "SCALA001");
        scalaNode.setProperty("Title", "Learn Scala");
        scalaNode.setProperty("NoOfChapters", "20");
        scalaNode.setProperty("Status", "Completed");

        Relationship relationship = javaNode.createRelationshipTo
        (scalaNode,TutorialRelationships.JVM_LANGIAGES);
        relationship.setProperty("Id","1234");
        relationship.setProperty("OOPS","YES");
        relationship.setProperty("FP","YES");

        tx.success();
    }
       System.out.println("Done successfully");
}
}

3.2 Cypher执行引擎,让Java执行原生CQL语句

package com.tp.neo4j.java.cql.examples;

import org.neo4j.cypher.javacompat.ExecutionEngine;
import org.neo4j.cypher.javacompat.ExecutionResult;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.factory.GraphDatabaseFactory;

public class JavaNeo4jCQLRetrivalTest {
   public static void main(String[] args) {
      // 1\. 获取graphDB
      GraphDatabaseFactory graphDbFactory = new GraphDatabaseFactory();
      GraphDatabaseService graphDb = graphDbFactory.newEmbeddedDatabase("C:/TPNeo4jDB");
      // 2\. 获取Cypher执行引擎
      ExecutionEngine execEngine = new ExecutionEngine(graphDb);
      ExecutionResult execResult = execEngine.execute("MATCH (java:JAVA) RETURN java");
      // 3\. 获取执行结果
      String results = execResult.dumpToString();
      System.out.println(results);
   }
}

3.3 Spring Data neo4j 的操作

操作思路:

  1. 创建一个与图数据库存储数据对应的实体类entity,并进行必要的注解;
  2. dao层接口继承Spring data Neo4jGraphRepositoryGraphTemplateCrudRepositoryPaginationAndSortingRepository,这个和springDataJPA也比较类似;

基本导包操作,pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" 
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
   http://maven.apache.org/xsd/maven-4.0.0.xsd">

   <modelVersion> 4.0.0 </modelVersion>
   <groupId> com.tp.neo4j </groupId>
   <artifactId> springdata-neo4j </artifactId>
   <version> 1.0 </version>  

   <dependencies>
      <dependency>   
         <groupId> org.springframework.data </groupId>
         <artifactId> spring-data-neo4j </artifactId>
         <version> 3.1.2.RELEASE </version>
      </dependency>

      <dependency>
         <groupId> org.neo4j </groupId>
         <artifactId> neo4j-kernel </artifactId>
         <version> 2.1.3 </version>
      </dependency>  

      <dependency>
         <groupId> javax.transaction </groupId>
         <artifactId> jta </artifactId>
         <version> 1.1 </version>
      </dependency>

      <dependency>
         <groupId>javax.validation</groupId>
         <artifactId>validation-api</artifactId>
         <version>1.0.0.GA</version>
      </dependency>

   </dependencies>   
</project>

最后

neo4j与java的结合有很多的方式,据目前我所知道的就有原生apidriver方式springData neo4j等三种方式。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,440评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,814评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,427评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,710评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,625评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,014评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,511评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,162评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,311评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,262评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,278评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,989评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,583评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,664评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,904评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,274评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,856评论 2 339

推荐阅读更多精彩内容