一 、开发背景
最近公司里面要做知识图谱功能,需要后端支持一些查询操作,所以写了一个基本的例子。我自己对于知识图谱的理解也加深了很多。因为我是从零开始的,就是奔着出功能去的,所以可能有些操作会有过时了,或者实现不是很好的等问题,望担待。
二、一些基础知识
知识图谱相关:
主要是了解知识图谱是个啥玩意,包括哪几部分等
cypher基本语法(neo4j数据库使用的,类似于mysql数据库使用的是sql语法。。。大概这样)
三、代码编写
1.pom.xml文件
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-neo4j</artifactId>
</dependency>
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-ogm-http-driver</artifactId>
<version>3.2.11</version>
</dependency>
这里需要注意如果只是用了 spring-boot-starter-data-neo4j 的话,连接驱动默认使用的是bolt协议。bolt协议好像底层使用二进制操作,更高效。如果要是用http协议的话要加入neo4j-ogm-http-driver 。版本要和neo4j一样,在依赖里面查下。
2.配置文件
spring:
data:
neo4j:
uri: bolt://127.0.0.1:7687
username: neo4j
password: 1234
使用bolt协议,端口使用7687,使用http协议,端口使用7474,这些是默认端口。这个端口可以在neo4j配置,如果配置了就去查下对应的端口。
3.关系节点
/**
* 关系节点
*/
@NodeEntity(label = "Element")
public class SupplyGraph {
private long id;
private String desc;
@Id
private String eleId;
private String eleName;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public String getEleId() {
return eleId;
}
public void setEleId(String eleId) {
this.eleId = eleId;
}
public String getEleName() {
return eleName;
}
public void setEleName(String eleName) {
this.eleName = eleName;
}
}
@NodeEntity 指定当前类为节点类,label需要和neo4j中的Node label中的对应。
4. repository编写
public interface SupplyRepository extends Neo4jRepository<SupplyGraph,Long> {
SupplyGraph findByEleName(String name);
@Query("Match (p:Element)-[*]->(s:Element) where p.eleId = {eleId} return s ")
List<SupplyGraph> findAllByEleId(@Param("eleId") String eleId);
}
repository可以继承Neo4jRepository 按照jpa的方式编写或者实现自定义的查询。查询语句中的实体名称(Element)需要和neo4j中的一样,不能放自己定义的实体类名称(SupplyGraph)。
5.编写controller访问
@Autowired
private SupplyRepository supplyRepository;
@GetMapping("/hello")
private WebResult hello() {
WebResult r = new WebResult();
List<SupplyGraph> allByEleId = supplyRepository.findAllByEleId("5");
r.setData(allByEleId);
return r;
}
6,查询节点和节点间的关系
使用session的方式来查询。
先将session配置进去,配置类如下:
@Configuration
public class Neo4jSourceConfig {
@Value("${spring.data.neo4j.uri}")
private String url;
@Value("${spring.data.neo4j.username}")
private String username;
@Value("${spring.data.neo4j.password}")
private String password;
@Bean(name = "session")
public Session neo4jSession() {
Driver driver = GraphDatabase.driver(url, AuthTokens.basic(username, password));
return driver.session();
}
}
然后使用session执行查询语句,查询语句中要讲节点和关系返回,那么返回结果里面会包含节点信息和关系信息。
执行查询语句如下:
@Autowired
private Session session;
private void test(){
if (session.isOpen()) {
String cql = String.format("Match (p:Element)-[r]->(s:Element) where p.eleId ='%s' return s,r", supplyGraph.getEleId());
Result run = session.run(cql);
}
}
处理返回结果:
while (run.hasNext()) {
Record next = run.next();
GraphNodeResult nodeResult = new GraphNodeResult(); //节点类
GraphRelationResult relationResult = new GraphRelationResult(); //关系类
for (Value value : next.values()) {
if (TextUtils.equals(value.type().name(), "RELATIONSHIP")) { //是关系类型的数据
Relationship relationship = value.asRelationship();
Map<String, Object> map = relationship.asMap(); //返回关系所有属性的map
Set<String> keySet = map.keySet();
relationResult.setTarget(nodeResult.getName());
for (String s : keySet) {
if (TextUtils.equals(s, "type")) {
relationResult.setName((String) map.get(s));
relationResult.setDes((String) map.get(s));
}
}
} else { //是节点类型的数据NODE
Node node = value.asNode();
Map<String, Object> map = node.asMap(); //这里是一个节点所有属性的map
Set<String> keySet = map.keySet();
nodeResult.setCategory(category + 1);
for (String s1 : keySet) {
if (TextUtils.equals(s1, "eleName")) {
nodeResult.setName((String) map.get(s1));
} else if (TextUtils.equals(s1, "desc")) {
nodeResult.setDes((String) map.get(s1));
}
}
}
}
}
}
四、碰到的问题
1.Make sure you are not trying to connect to the http endpoint (HTTP defaults to port 7474 whereas BOLT defaults to port 7687)
原因:配置文件中端口和协议不对应,我使用的是bolt协议,但是端口是7474
解决办法:修改端口,和协议对应。bolt使用7687端口,http使用7474端口
2.SupplyRepository 自定义的查询不执行
原因:实体类型和neo4j数据库中的类型不对应
解决办法:将语句中的实体类型修改为neo4j中的数据类型,而不是代码中实体的类名。。。。
参考资料:
1.什么是知识图谱?
3.基本语法
4.常用查询
5.玩转neo4j