图数据库neo4j搭建与使用

2,629 阅读3分钟

图数据库neo4j

1.图数据库介绍

1.1 什么是图数据库

图数据库(Graph database)指的是以图数据结构的形式来存储和查询数据的数据库。

  • 一个开源
  • 无Schema
  • Cypher进行类似Sql操作
  • 基于Java开发,运行于JVM之上

图数据库是基于图论实现的一种NoSQL数据库,其数据存储结构和数据查询方式都是以图论为基础的,图数据库主 要用于存储更多的连接数据

1.2 图形数据结构

在一个图中包含两种基本的数据类型:Nodes(节点)Relationships(关系)Nodes 和 Relationships 包含 key/value形式的属性。Nodes通过Relationships所定义的关系相连起来,形成关系型网络结构。

图是一组节点和连接这些节点的关系,图形以属性的形式将数据存储在节点和关系中,属性是用于表示数据的键值 对。在图论中,我们可以表示一个带有圆的节点,节点之间的关系用一个箭头标记表示。

image-20220819170130967

1.3 Neo4j的构建元素

Neo4j图数据库主要有以下构建元素:

  • 节点
  • 属性
  • 关系
  • 标签
  • 数据浏览器
节点

节点(Node)是图数据库中的一个基本元素,用来表示一个实体记录,就像关系数据库中的一条记录一 ​ 样。在Neo4j中节点可以包含多个属性(Property)和多个标签(Label)。

  • 节点是主要的数据元素
  • 节点通过关系连接到其他节点
  • 节点可以具有一个或多个属性(即,存储为键/值对的属性)
  • 节点有一个或多个标签,用于描述其在图表中的作用
属性

属性(Property)是用于描述图节点和关系的键值对。其中Key是一个字符串,值可以通过使用任何Neo4j数据 类型来表示

  • 属性是命名值,其中名称(或键)是字符串
  • 属性可以被索引和约束
  • 可以从多个属性创建复合索引
关系

关系(Relationship)同样是图数据库的基本元素。当数据库中已经存在节点后,需要将节点连接起来构成 图。关系就是用来连接两个节点,关系也称为图论的边(Edge) ,其始端和末端都必须是节点,关系不 ​ 能指向空也不能从空发起。关系和节点一样可以包含多个属性,但关系只能有一个类型(Type) 。

  • 关系连接两个节点
  • 关系是方向性
  • 节点可以有多个甚至递归的关系
  • 关系可以有一个或多个属性(即存储为键/值对的属性)

基于方向性,Neo4j关系被分为两种主要类型:

  • 单向关系
  • 双向关系
标签

标签(Label)将一个公共名称与一组节点或关系相关联, 节点或关系可以包含一个或多个标签。 我们可以 为现有节点或关系创建新标签, 我们可以从现有节点或关系中删除标签

  • 一个节点可以具有多个标签
  • 标签用于将节点分组对标签
  • 进行索引以加速在图中查找节点
  • 本机标签索引针对速度进行了优化

2. 环境搭建

2.1 docker安装neo4j

docker pull neo4j
​
docker run \
    --publish=7474:7474 --publish=7687:7687 \
    --volume=$HOME/neo4j/data:/data \
    --rm -d neo4j

浏览器输入http://localhost:7474/browser 账号密码默认neo4j

2.2 Neo4j Desktop 安装

官网地址:neo4j.com/product/dev…

image-20220819180010405

3. Neo4j - CQL使用

3.1 Neo4j - CQL简介

Neo4j的Cypher语言是为处理图形数据而构建的,CQL代表Cypher查询语言。像Oracle数据库具有查询语言SQL,Neo4j具有CQL作为查询语言。

  • 它是Neo4j图形数据库的查询语言。
  • 它是一种声明性模式匹配语言
  • 它遵循SQL语法。
  • 它的语法是非常简单且人性化、可读的格式。
CQL命令用法
CREATE创建节点,关系和属性
MATCH检索有关节点,关系和属性数据
RETURN返回查询结果
WHERE提供条件过滤检索数据
DELETE删除节点和关系
REMOVE删除节点和关系的属性
ORDER BY排序检索数据
SET添加或更新标签

3.2 常用命令

neo4j.com/docs/cypher…

创建节点
CREATE (m:Member {name:'周一',level:3}) RETURN m;
CREATE (m:Member {name:'周二',level:2}) RETURN m;
CREATE (m:Member {name:'周三',level:1}) RETURN m;
CREATE (m:Member {name:'周四',level:2}) RETURN m;
CREATE (m:Member {name:'周五',level:1}) RETURN m;
CREATE (m:Member {name:'周六',level:3}) RETURN m;
CREATE (m:Member {name:'周日',level:2}) RETURN m;
CREATE (m:Member {name:'周八',level:0}) RETURN m;
创建关系
MATCH (a:Member {name:'周一'}), (b:Member {name:'周二'}) MERGE (a)-[:Fans {binging_time:'2022-08-16 17:34:51'}]->(b);
MATCH (a:Member {name:'周二'}), (b:Member {name:'周三'}) MERGE (a)-[:Fans {binging_time:'2022-08-16 17:34:51'}]->(b);
MATCH (a:Member {name:'周一'}), (b:Member {name:'周四'}) MERGE (a)-[:Fans {binging_time:'2022-08-16 17:34:51'}]->(b);
MATCH (a:Member {name:'周四'}), (b:Member {name:'周五'}) MERGE (a)-[:Fans {binging_time:'2022-08-16 17:34:51'}]->(b);
MATCH (a:Member {name:'周一'}), (b:Member {name:'周六'}) MERGE (a)-[:Fans {binging_time:'2022-08-16 17:34:51'}]->(b);
MATCH (a:Member {name:'周六'}), (b:Member {name:'周日'}) MERGE (a)-[:Fans {binging_time:'2022-08-16 17:34:51'}]->(b);
MATCH (a:Member {name:'周日'}), (b:Member {name:'周八'}) MERGE (a)-[:Fans {binging_time:'2022-08-16 17:34:51'}]->(b);
或者创建节点时创建关系
CREATE (a:Member {name:'王一',level:3})-[r:Fans]->(b:Member {name:'王二',level:2});
增加或者修改节点属性
MATCH (m:Member {name:'王一'}) SET m.level=3;
MATCH (m:Member) where m.name = '王一' SET m.level=3;
查询所有下级节点,包含当前节点
match (m:Member {name:'周一'})-[*0..]->(result) return result;

image-20220816181024354

查询所有下级节点,不包含当前节点
match (m:Member {name:'周一'})-[*1..]->(result) return result;

image-20220816181242175

向上遍历所有节点
match (m:Member{name:'周八'})<-[*0..]-(result) return result;

image-20220816181501252

根据Id删除
match (m) where id(m) = 4 delete m
删除两个节点关系
match (m:Member {name:'周四'}) -[r:Fans]-> (me:Member{name:'周三'}) delete r;
查询直接关系
match (n:Member) -[*1]->(n2:Member) where n.name= '周一' return n2;

查询间接关系

match (n:Member) -[*2..]->(n2:Member) where n.name= '周一' return n2;
白名单与黑名单节点

neo4j.com/labs/apoc/4…

apoc.path.subgraphAll

match (u:User {name:'L2'})
match (end:User)
where end.type = 0
with u, collect(end) as endNodes
call apoc.path.subgraphAll(u, {
    minLevel: 0,
    blacklistNodes: endNodes
})
YIELD nodes, relationships
RETURN nodes, relationships;

apoc.path.spanningTree

match (u:User {name:'L2'})
match (end:User)
where end.type = 0
WITH u, collect(end) AS endNodes
call apoc.path.spanningTree(u, {
    minLevel: 0,
    blacklistNodes: endNodes
}) 
YIELD path
RETURN path;
// 创建人物节点
create (person:Person {user_no: 'A', level: 2});
create (person:Person {user_no: 'B', level: 1});
create (person:Person {user_no: 'C', level: 0});
create (person:Person {user_no: 'D', level: 2});
create (person:Person {user_no: 'E', level: 0});
create (person:Person {user_no: 'F', level: 1});
create (person:Person {user_no: 'G', level: 0});
create (person:Person {user_no: 'H', level: 0});
​
// 创建人物关系
match (a:Person {user_no: 'A'}),(b:Person {user_no: 'B'}) merge (a)-[:INVITE]->(b);
match (a:Person {user_no: 'B'}),(b:Person {user_no: 'C'}) merge (a)-[:INVITE]->(b);
match (a:Person {user_no: 'C'}),(b:Person {user_no: 'D'}) merge (a)-[:INVITE]->(b);
match (a:Person {user_no: 'D'}),(b:Person {user_no: 'E'}) merge (a)-[:INVITE]->(b);
match (a:Person {user_no: 'A'}),(b:Person {user_no: 'F'}) merge (a)-[:INVITE]->(b);
match (a:Person {user_no: 'F'}),(b:Person {user_no: 'G'}) merge (a)-[:INVITE]->(b);
match (a:Person {user_no: 'C'}),(b:Person {user_no: 'H'}) merge (a)-[:INVITE]->(b);
​
// 获取A所有直属
match (a:Person {user_no: 'A'})-[:INVITE]->(n:Person) where n.level < 2 return n
// 获取A所有团队
match p = (a:Person {user_no: 'A'})-[:INVITE*2..]->(n:Person) where all (x in nodes(p) where x.user_no = 'A' or x.level < 2) return n
// 获取A所有直属+团队链路(包含自己)
match p = (a:Person {user_no: 'A'})-[:INVITE*]->(n:Person) where all (x in nodes(p) where x.user_no = 'A' or x.level < 2) return p
// 获取A所有直属+团队链路(不包含自己)
match p = (a:Person {user_no: 'A'})-[:INVITE*]->(n:Person) where all (x in nodes(p) where x.user_no = 'A' or x.level < 2) return n
// 统计A直属人数
match (a:Person {user_no: 'A'})-[:INVITE]->(n:Person) where n.level < 2 return count(*)
// 统计A团队人数
match p = (a:Person {user_no: 'A'})-[:INVITE*2..]->(n:Person) where all (x in nodes(p) where x.user_no = 'A' or x.level < 2) return count(*)
// 上面配合分页查询,获取当前页所有用户id,获取他们的直属人数
match (a:Person)-[:INVITE]->(n:Person) where a.user_no IN ['B', 'F'] and n.level < a.level return a.user_no, count(*)
// 上面配合分页查询,获取当前页所有用户id,获取他们的团队人数
match p = (a:Person)-[:INVITE*2..]->(n:Person) where a.user_no IN ['B', 'F'] and all (x in nodes(p) where x.user_no IN ['B', 'F'] or x.level < 2) return a.user_no, count(*)
​
​
创建唯一索引
create constraint constraint_memberId for (m:Member) require m.memberId is unique

4. Spring Boot 整合Neo4j

添加依赖
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-neo4j</artifactId>
</dependency>
修改配置
:server status
spring:
    neo4j:
      uri: bolt://localhost:7687
      username: neo4j
      password: youpik
创建实体

@NodeEntity:标明是一个节点实体

@RelationshipEntity:标明是一个关系实体

@Id:实体主键

@Property:实体属性

@GeneratedValue:实体属性值自增

@StartNode:开始节点

@EndNode:结束节点

@Data
@NodeEntity("user")
public class User {
    @Id
    @GeneratedValue
    private Long id;
    @Property(name = "memberId")
    private Long memberId;
    private Long recommendId;
    @Property(name = "name")
    private String name;
    @Property(name = "level")
    private Integer level;
}
​
@RelationshipEntity(type = "fans")
@Data
public class UserRelationShip {
    @Id
    @GeneratedValue
    private Long id;
    @StartNode
    private User parent;
    @EndNode
    private User child;
    @Property(name = "bandingTime")
    private Long bandingTime;
}
创建接口继承Neo4jRepository
/**
 * 用户
 *
 * @author sjj
 * @date 2022/08/19
 */
@Repository
public interface UserRepository extends Neo4jRepository<User, Long> {
    /**
     * 获取团队数量
     *
     * @param memberId 成员id
     * @return {@link Integer}
     */
    @Query("match (u:user)-[*1..]->(u2:user) where u.memberId = $memberId return count(*)")
    Integer getTotalCount(@Param("memberId") Long memberId);
}
​
/**
 * 用户关系
 *
 * @author sjj
 * @date 2022/08/19
 */
@Repository
public interface UserRelationShipRepository extends Neo4jRepository<UserRelationShip, Long> {
}
​