Neo4j图数据库
数据库安装
-
下载安装包
https://neo4j.com/artifact.php?name=neo4j-community-4.1.1-windows.zip -
解压
bin:数据库启动等文件
neo4j.bat 服务端启动文件
cypher-shell.bat 不适用浏览器,使用shell输命令执行CQL语言
neo4j-admin.bat neo4j管理员工具,数据库备份导出这些的
conf: 数据库配置文件
data: 数据库持久化默认文件夹,可以在配置文件修改默认目录
logs: 数据库日志文件
-
cmd进入bin目录,输入
neo4j.bat console可以看出,可以在本机的7474端口可以访问
-
浏览器输入
http://localhost:7474/这里用户名和密码默认都是
neo4j,进去就会修改默认密码,修改成功就可 -
进入neo4j控制台
数据库案例初识
图的基本理念
一个图数据库使用以下理念能存储任何数据
-
节点 - 图的数据记录
-
关系 - 连接节点
-
属性 - 标识数据值
图数据库节点和属性
-
节点就是记录在图中的数据
-
数据以Properties的方式储存
-
Properties就是简单的键值对
图数据库的标签
标签是用来关联一组节点的,也就说说节点使用标签分类,标签就是类,而节点就是这个类的实例
-
一个节点可以有>=0个标签
-
标签不能有属性
约束自由
节点可以有不同的属性
-
同一类节点可以有不同的标签,这里的类表示拥有相同的标签
-
属性可以是字符串、数字、布尔型
-
Neo4j可以储存数十亿个节点
图的关系
连接图中节点的
-
关系有方向性
-
关系有类型
-
关系不是任意定义的,它也有数据模型
关系属性
关系属性用来储存两个节点之间的共享信息
数据库查询语言CQL
Cypher : Neo4j的图查询语言
概述
-
使用模式描述图形数据
-
熟悉的类似SQL的查询语句
-
说明性的,描述要查找的内容,而不是如何查找
Cypher
创建节点CREATE
使用create创建一个节点
CREATE (ee:Person { name: "Emil", from: "Sweden", klout: 99 })
解释:创建一个标签为Person的节点ee,他的属性有name是Emil、from是Sweden和klout是99
查询节点MATCH
使用match语句查找节点
MATCH (ee:Person) WHERE ee.name = "Emil" RETURN ee;
解释: 查找标签为Person,节点的name属性为Emil的,节点定义为ee,然后把这个节点ee返回,这里的ee不一定是数据库创建时的那个名字
创建多个节点和多个关系
MATCH (ee:Person) WHERE ee.name = "Emil"
CREATE (js:Person { name: "Johan", from: "Sweden", learn: "surfing" }),
(ir:Person { name: "Ian", from: "England", title: "author" }),
(rvb:Person { name: "Rik", from: "Belgium", pet: "Orval" }),
(ally:Person { name: "Allison", from: "California", hobby: "surfing" }),
(ee)-[:KNOWS {since: 2001}]->(js),(ee)-[:KNOWS {rating: 5}]->(ir),
(js)-[:KNOWS]->(ir),(js)-[:KNOWS]->(rvb),
(ir)-[:KNOWS]->(js),(ir)-[:KNOWS]->(ally),
(rvb)-[:KNOWS]->(ally)
解释:
-
MATCH (ee:Person) WHERE ee.name = "Emil"将第一次创建的那个节点引入,因为要和其他节点建立关系 -
(ee)-[:KNOWS {since: 2001}]->(js)给ee节点和js节点建立一个关系,从ee指向js的,关系上共享数据是since 2001,也就是说ee自从2001年了解到js的
查看劳动成果
MATCH (abc:Person) RETURN abc
关系查询
MATCH (ee:Person)-[:KNOWS]-(friends) WHERE ee.name = "Emil" RETURN ee, friends
解释: 查询name属性为Emil的Person,和他有KNOWS关系的人,然后返回这个人和所有符合的朋友
执行结果:
注意:如果不返回ee的话,只返回他的朋友
MATCH (ee:Person)-[:KNOWS]-(friends) WHERE ee.name = "Emil" RETURN friends
思考:
MATCH (ee:Person)-[:KNOWS]-(friends) RETURN friends MATCH (ee:Person) RETURN ee
这两句的返回结果一样?
推荐节点
MATCH (js:Person)-[:KNOWS]-()-[:KNOWS]-(surfer) WHERE js.name = "Johan" AND surfer.hobby = "surfing" RETURN DISTINCT surfer
解释: 找到和Johan有相同爱好surfing的人
执行的任务可视化
使用关键字:EXPLAIN 和 PROFILE
-
EXPLAIN:是解释机制,加入该关键字的Cypher语句可以预览执行的过程但并不实际执行,所以也不会产生任何结果。
-
PROFILE:则是画像机制,查询中使用该关键字,不仅能够看到执行计划的详细内容,也可以看到查询的执行结果。
例子
PROFILE MATCH (js:Person)-[:KNOWS]-()-[:KNOWS]-(surfer) WHERE js.name = "Johan" AND surfer.hobby = "surfing" RETURN DISTINCT surfer
注意:可以根据执行分析对CQL进行
电影案例
-
返回姓名为Tom Hanks的演员
match (aa:Person) where aa.name="Tom Hanks" return aa;
match (aa {name:"Tom Hanks"}) return aa; -
查找演员名字最多10个(使用limit关键字)
match (p:Person) return p.name limit 10
-
查找电影在20世纪90年代发布的电影
match (m:Movie) where m.released >= 1900 and m.released < 2000 return m.title;
-
查找Tom Hanks参与过的电影
match (p:Person)-[:ACTED_IN]->(m:Movie) where p.name="Tom Hanks" return p,m;
MATCH (tom:Person {name: "Tom Hanks"})-[:ACTED_IN]->(tomHanksMovies) RETURN tom,tomHanksMovies -
谁参与表演了电影Cloud Atlas
match (m:Movie)<-[:DIRECTED]-(p:Person) where m.title="Cloud Atlas" return p.name;
MATCH (cloudAtlas {title: "Cloud Atlas"})<-[:DIRECTED]-(directors) RETURN directors.name -
查找和Tom Hanks一起演出的人
match (p:Person) -[:ACTED_IN]->(m:Movie)<-[:ACTED_IN]-(co:Person) where p.name="Tom Hanks" return co.name;
MATCH (tom:Person {name:"Tom Hanks"})-[:ACTED_IN]->(m)<-[:ACTED_IN]-(coActors) RETURN coActors.name -
和电影Cloud Atlas有关系的人和关系类型
match (p:Person) -[rel]-(m:Movie) where m.title="Cloud Atlas" return p.name,Type(rel),rel;
MATCH (people:Person)-[relatedTo]-(:Movie {title: "Cloud Atlas"}) RETURN people.name, Type(relatedTo), relatedTo -
能通过最大4个节点找到的节点
MATCH (bacon:Person {name:"Kevin Bacon"})-[*1..4]-(hollywood) RETURN DISTINCT hollywood这里
[*1..4]表示1到4 ,[*3]表示3 -
查找Kevin Bacon和Meg Ryan能关联到的关系的最短路径
MATCH p=shortestPath( (bacon:Person {name:"Kevin Bacon"})-[*]-(meg:Person {name:"Meg Ryan"}) ) RETURN p这里
*表示任何关系 -
查找没和Tom Hanks一起演出的人,但是他们之间有联系
MATCH (tom:Person {name:"Tom Hanks"})-[:ACTED_IN]->(m)<-[:ACTED_IN]-(coActors), (coActors)-[:ACTED_IN]->(m2)<-[:ACTED_IN]-(cocoActors) WHERE NOT (tom)-[:ACTED_IN]->()<-[:ACTED_IN]-(cocoActors) AND tom <> cocoActors RETURN cocoActors.name AS Recommended, count(*) AS Strength ORDER BY Strength DESC分析:
-
(tom:Person {name:"Tom Hanks"})-[:ACTED_IN]->(m)<-[:ACTED_IN]-(coActors)这句话coActors表示和Tom Hanks一起表演过的人 -
(coActors)-[:ACTED_IN]->(m2)<-[:ACTED_IN]-(cocoActors)这句话cocoActors表示和coActors一起表演过的人,这里意味着找到了朋友的朋友 -
NOT (tom)-[:ACTED_IN]->()<-[:ACTED_IN]-(cocoActors)这句话排除了既是朋友的朋友,又是朋友的人 -
tom <> cocoActors这句话保证了所得的朋友的朋友不是自己 -
count(*) AS Strength这里统计了Tom Hanks到朋友的朋友能到达的路径条数,可以用这个表达推荐的优先级 -
ORDER BY Strength DESC按照Strength 降序排序
-
-
查找既参演过Tom Hanks演过的电影,又参演了Tom Cruise演过的电影的
MATCH (tom:Person {name:"Tom Hanks"})-[:ACTED_IN]->(m)<-[:ACTED_IN]-(coActors), (coActors)-[:ACTED_IN]->(m2)<-[:ACTED_IN]-(cruise:Person {name:"Tom Cruise"}) RETURN tom, m, coActors, m2, cruise一张图看懂这句话
技巧
查看图中所有节点和关系
MATCH (n) RETURN n
删除所有节点
MATCH (n) DETACH DELETE n
判断属性是否存在
is null
SpringBoot整合Neo4j
构建图
依赖导入
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-neo4j</artifactId> </dependency>
配置文件
spring.data.neo4j.uri=bolt://localhost spring.data.neo4j.username=neo4j spring.data.neo4j.password=860820
编写实体类
import lombok.Data;
import org.neo4j.ogm.annotation.Id;
import org.neo4j.ogm.annotation.NodeEntity;
@NodeEntity(label = "USER")
@Data
public class User {
@Id
private Long id;
private Integer userId;
private String userName;
private Integer age;
private String password;
}
@RelationshipEntity(type = "FRIEND_WITH")
@Data
public class FriendRelation {
@Id
private Long id;
@StartNode
private User startNode;
@EndNode
private User endNode;
private Date since;
}
编写DAO层接口
import com.example.neo4jdemo.pojo.User;
import org.springframework.data.neo4j.annotation.Query;
import org.springframework.data.neo4j.repository.Neo4jRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface UserDao extends Neo4jRepository<User,Long> {
/**
* 查询所有用户
* @return
*/
@Query("MATCH (user:USER) RETURN user")
List<User> getUserList();
/**
* 按照姓名查询
* @param name
* @return
*/
@Query("MATCH (user:USER) WHERE user.userName=$userName RETURN user")
List<User> getUserByName(@Param("userName") String name);
}
import com.example.neo4jdemo.pojo.FriendRelation;
import com.example.neo4jdemo.pojo.User;
import org.springframework.data.neo4j.annotation.Query;
import org.springframework.data.neo4j.repository.Neo4jRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface FriendRelationDao extends Neo4jRepository<FriendRelation,Long> {
/**
* 查找指定姓名的朋友
* @param name
* @return
*/
@Query("MATCH (u:USER)-[:FRIEND_WITH]->(rl) WHERE u.userName=$name RETURN rl")
List<User> getFriend(@Param("name") String name);
/**
* 按照姓名查找朋友的朋友
* @param name
* @return
*/
@Query("MATCH (u:USER{userName:$name})-[:FRIEND_WITH]->(rl:USER),(rl)-[:FRIEND_WITH]->(newf:USER) WHERE NOT (u)-[:FRIEND_WITH]->(newf) AND u<> newf RETURN newf")
List<User> getMayBeFriend(@Param("name") String name);
}import com.example.neo4jdemo.pojo.FriendRelation;
import com.example.neo4jdemo.pojo.User;
import org.springframework.data.neo4j.annotation.Query;
import org.springframework.data.neo4j.repository.Neo4jRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface FriendRelationDao extends Neo4jRepository<FriendRelation,Long> {
@Query("MATCH (u:USER)-[:FRIEND_WITH]->(rl) WHERE u.userName=$name RETURN rl")
List<User> getFriend(@Param("name") String name);
@Query("MATCH (u:USER{userName:$name})-[:FRIEND_WITH]->(rl:USER),(rl)-[:FRIEND_WITH]->(newf:USER) WHERE NOT (u)-[:FRIEND_WITH]->(newf) AND u<> newf RETURN newf")
List<User> getMayBeFriend(@Param("name") String name);
}
编写服务接口和实现类
(这里懒,没有写接口)
import com.example.neo4jdemo.dao.FriendRelationDao;
import com.example.neo4jdemo.dao.UserDao;
import com.example.neo4jdemo.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserService {
@Autowired
private UserDao userDao;
@Autowired
private FriendRelationDao friendRelationDao;
public List<User> findByName(String name){
return userDao.getUserByName(name);
}
public List<User> findAll(){
return userDao.getUserList();
}
public List<User> getFriend(String name){
return friendRelationDao.getFriend(name);
}
public List<User> recommend(String name){
return friendRelationDao.getMayBeFriend(name);
}
}
编写Controller
import com.example.neo4jdemo.pojo.User;
import com.example.neo4jdemo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;
@Controller
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/get")
@ResponseBody
public List<User> get(){
return userService.findAll();
}
@GetMapping("/get/{name}")
@ResponseBody
public List<User> get(@PathVariable String name){
return userService.findByName(name);
}
@GetMapping("/friend/{name}")
@ResponseBody
public List<User> getFriend(@PathVariable String name){
return userService.getFriend(name);
}
@GetMapping("/recommend/{name}")
@ResponseBody
public List<User> getMeyBeFriend(@PathVariable String name){
return userService.recommend(name);
}
}