Explorar o código

feat: 增加了获取图谱、图谱关系相关操作

kokomi %!s(int64=3) %!d(string=hai) anos
pai
achega
c7ea34a13e

+ 3 - 0
src/main/java/com/seeckg/knowledgegraph_backend/convert/KnowledgeConvert.java

@@ -16,6 +16,9 @@ public class KnowledgeConvert {
                 .context(from.getContext())
                 .creatorId(from.getCreatorId())
                 .bindClassId(from.getBindClassId())
+                .define(from.getDefine())
+                .similar(from.getSimilar())
+                .sub(from.getSub())
                 .build();
     }
 }

+ 1 - 0
src/main/java/com/seeckg/knowledgegraph_backend/dao/KnowledgeDao.java

@@ -5,4 +5,5 @@ import org.springframework.data.neo4j.repository.Neo4jRepository;
 
 public interface KnowledgeDao extends Neo4jRepository<Knowledge,Long> {
     Knowledge findByName(String name);
+
 }

+ 21 - 0
src/main/java/com/seeckg/knowledgegraph_backend/enums/knowledgeType.java

@@ -0,0 +1,21 @@
+package com.seeckg.knowledgegraph_backend.enums;
+
+/**
+ * author:kokomi
+ * created 2023/5/20 17:16
+ */
+public enum knowledgeType {
+    /**
+     * 定义关系 A定义B是A DEFINE B
+     */
+    DEFINE,
+    /**
+     * 相似关系,A SIMILAR B
+     */
+    SIMILAR,
+    /**
+     * 从属关系,也就是子节点的关系 A SUB B A是B的子节点
+     */
+    SUB
+
+}

+ 40 - 0
src/main/java/com/seeckg/knowledgegraph_backend/pojo/Knowledge.java

@@ -6,6 +6,10 @@ import org.springframework.data.neo4j.core.schema.Node;
 
 import lombok.Builder;
 import lombok.Data;
+import org.springframework.data.neo4j.core.schema.Relationship;
+
+import java.util.HashSet;
+import java.util.Set;
 
 @Node
 @Builder
@@ -34,4 +38,40 @@ public class Knowledge {
      */
     private String context;
 
+    /**
+     * Neo4j doesn't REALLY have bi-directional relationships. It just means when querying
+     * to ignore the direction of the relationship.
+     * https://dzone.com/articles/modelling-data-neo4j
+     */
+    //定义关系
+    @Relationship(type = "DEFINE")
+    public Set<Knowledge> define;
+
+    public void defineRelationship(Knowledge knowledge) {
+        if (define == null) {
+            define = new HashSet<>();
+        }
+        define.add(knowledge);
+    }
+
+    //相似关系
+    @Relationship(type = "SIMILAR")
+    public Set<Knowledge> similar;
+    public void similarRelationship(Knowledge knowledge) {
+        if (similar == null) {
+            similar = new HashSet<>();
+        }
+        similar.add(knowledge);
+    }
+
+    //子知识点关系
+    @Relationship(type = "SUB")
+    public Set<Knowledge> sub;
+    public void subRelationship(Knowledge knowledge) {
+        if (sub == null) {
+            sub = new HashSet<>();
+        }
+        sub.add(knowledge);
+    }
+
 }

+ 17 - 0
src/main/java/com/seeckg/knowledgegraph_backend/pojo/KnowledgeVO.java

@@ -2,6 +2,8 @@ package com.seeckg.knowledgegraph_backend.pojo;
 
 import lombok.Data;
 
+import java.util.Set;
+
 /**
  * @author chenjiawei09 <chenjiawei09@kuaishou.com>
  * Created on 2023-05-19
@@ -28,4 +30,19 @@ public class KnowledgeVO {
      * 知识点内容
      */
     private String context;
+
+    /**
+     * 定义关系
+     */
+    public Set<Knowledge> define;
+
+    /**
+     * 相似关系
+     */
+    public Set<Knowledge> similar;
+
+    /**
+     * 子知识点关系
+     */
+    public Set<Knowledge> sub;
 }

+ 31 - 0
src/main/java/com/seeckg/knowledgegraph_backend/pojo/Relationship.java

@@ -0,0 +1,31 @@
+package com.seeckg.knowledgegraph_backend.pojo;
+
+import lombok.Data;
+
+/**
+ * author:kokomi
+ * created 2023/5/21 0:14
+ */
+@Data
+public class Relationship {
+    /**
+     * 起始节点的index
+     */
+    private int source;
+
+    /**
+     * 目标节点的index
+     */
+    private int target;
+
+    /**
+     * 关系名称
+     */
+    private String relationship;
+
+    public Relationship(int source, int target, String relationship){
+        this.source=source;
+        this.target=target;
+        this.relationship=relationship;
+    }
+}

+ 122 - 6
src/main/java/com/seeckg/knowledgegraph_backend/service/KnowledgeService.java

@@ -1,6 +1,6 @@
 package com.seeckg.knowledgegraph_backend.service;
 
-import java.util.Objects;
+import java.util.*;
 
 import javax.annotation.Resource;
 
@@ -8,13 +8,11 @@ import com.seeckg.knowledgegraph_backend.convert.KnowledgeConvert;
 import com.seeckg.knowledgegraph_backend.dao.KnowledgeDao;
 import com.seeckg.knowledgegraph_backend.enums.ReturnCode;
 import com.seeckg.knowledgegraph_backend.enums.UserRole;
-import com.seeckg.knowledgegraph_backend.pojo.CourseVO;
-import com.seeckg.knowledgegraph_backend.pojo.Knowledge;
-import com.seeckg.knowledgegraph_backend.pojo.KnowledgeVO;
-import com.seeckg.knowledgegraph_backend.pojo.Response;
-import com.seeckg.knowledgegraph_backend.pojo.UserVO;
+import com.seeckg.knowledgegraph_backend.enums.knowledgeType;
+import com.seeckg.knowledgegraph_backend.pojo.*;
 import com.seeckg.knowledgegraph_backend.remote.SeeCoderRemoteService;
 
+import com.seeckg.knowledgegraph_backend.util.GetKGReturnFormat;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
@@ -47,6 +45,7 @@ public class KnowledgeService {
                 return Response.buildFailed(ReturnCode.INVALID_USER.getCode(), ReturnCode.INVALID_USER.getDesc());
             }
         }
+
         Knowledge knowledge = KnowledgeConvert.convert(knowledgeVO);
         knowledgeDao.save(knowledge);
         return Response.buildSuccess();
@@ -82,6 +81,7 @@ public class KnowledgeService {
             log.error("[KnowledgeService.addKnowledge] 查无该知识点 knowledgeVO = {}", knowledgeVO);
             return Response.buildFailed(ReturnCode.PARAM_ERROR.getCode(), "知识点信息错误,查无该知识点");
         }
+
         Knowledge knowledgeNew = KnowledgeConvert.convert(knowledgeVO);
         knowledgeDao.save(knowledgeNew);
         return Response.buildSuccess();
@@ -128,4 +128,120 @@ public class KnowledgeService {
         return knowledgeDao.findByName(name);
     }
 
+    /**
+     *
+     * 添加关系
+     */
+    public Response addRelationship(long startKnowledgeId, long endKnowledgeId, knowledgeType type ,long userId) {
+        UserVO userVO = seeCoderRemoteService.getUserById(userId);
+        // 垂直权限校验
+        if (!userVO.getRole().equals(UserRole.TEACHER)) {
+            log.error("[KnowledgeService.addRelationship] 用户无权限 knowledgeId = {} and {} ,userVO = {}", startKnowledgeId, endKnowledgeId,userVO);
+            return Response.buildFailed(ReturnCode.INVALID_USER.getCode(), ReturnCode.INVALID_USER.getDesc());
+        }
+
+        Knowledge knowledgeStart = knowledgeDao.findById(startKnowledgeId).orElse(null);
+        if (Objects.isNull(knowledgeStart)){
+            log.error("[KnowledgeService.addRelationship] 查无该知识点 knowledgeId  = {}", startKnowledgeId );
+            return Response.buildFailed(ReturnCode.PARAM_ERROR.getCode(), "知识点信息错误,查无该知识点");
+        }
+        Knowledge knowledgeEnd = knowledgeDao.findById(endKnowledgeId).orElse(null);
+        if (Objects.isNull(knowledgeEnd)){
+            log.error("[KnowledgeService.addRelationship] 查无该知识点 knowledgeId  = {}", endKnowledgeId );
+            return Response.buildFailed(ReturnCode.PARAM_ERROR.getCode(), "知识点信息错误,查无该知识点");
+        }
+
+        try {
+            if(type==knowledgeType.DEFINE){
+                knowledgeStart.defineRelationship(knowledgeEnd);
+            }else if (type==knowledgeType.SIMILAR){
+                knowledgeStart.similarRelationship(knowledgeEnd);
+            }else {
+                knowledgeStart.subRelationship(knowledgeEnd);
+            }
+        }catch (Exception e){
+            return Response.buildFailed(ReturnCode.SERVER_ERROR.getCode(), e.getMessage());
+        }
+
+        return Response.buildSuccess();
+    }
+
+
+    public Response deleteRelationship(long startKnowledgeId, long endKnowledgeId, knowledgeType type ,long userId){
+        UserVO userVO = seeCoderRemoteService.getUserById(userId);
+        if (!userVO.getRole().equals(UserRole.TEACHER)) {
+            log.error("[KnowledgeService.deleteRelationship] 用户无权限 knowledgeId = {} and {} ,userVO = {}", startKnowledgeId, endKnowledgeId,userVO);
+            return Response.buildFailed(ReturnCode.INVALID_USER.getCode(), ReturnCode.INVALID_USER.getDesc());
+        }
+
+
+        Knowledge knowledgeStart = knowledgeDao.findById(startKnowledgeId).orElse(null);
+        if (Objects.isNull(knowledgeStart)){
+            log.error("[KnowledgeService.deleteRelationship] 查无该知识点 knowledgeId  = {}", startKnowledgeId );
+            return Response.buildFailed(ReturnCode.PARAM_ERROR.getCode(), "知识点信息错误,查无该知识点");
+        }
+
+        Knowledge knowledgeEnd = knowledgeDao.findById(endKnowledgeId).orElse(null);
+        if (Objects.isNull(knowledgeEnd)){
+            log.error("[KnowledgeService.deleteRelationship] 查无该知识点 knowledgeId  = {}", endKnowledgeId );
+            return Response.buildFailed(ReturnCode.PARAM_ERROR.getCode(), "知识点信息错误,查无该知识点");
+        }
+
+        try{
+            if(type==knowledgeType.SIMILAR){
+                Set<Knowledge> similar=knowledgeStart.getSimilar();
+                if(similar.contains(knowledgeEnd)){
+                    //正常运行
+                    similar.remove(knowledgeEnd);
+                }else{
+                    return Response.buildFailed(ReturnCode.PARAM_ERROR.getCode(), "关系不存在!");
+                }
+            }else  if(type==knowledgeType.DEFINE){
+                Set<Knowledge> define=knowledgeStart.getDefine();
+                if(define.contains(knowledgeEnd)){
+                    define.remove(knowledgeEnd);
+                }else {
+                    return Response.buildFailed(ReturnCode.PARAM_ERROR.getCode(),"关系不存在!" );
+                }
+            }else {
+                Set<Knowledge> sub=knowledgeStart.getSub();
+                if(sub.contains(knowledgeEnd)){
+                    sub.remove(knowledgeEnd);
+                }else {
+                    return Response.buildFailed(ReturnCode.PARAM_ERROR.getCode(), "关系不存在!");
+                }
+            }
+        }catch (Exception e){
+            return  Response.buildFailed(ReturnCode.SERVER_ERROR.getCode(), e.getMessage());
+        }
+        return Response.buildSuccess();
+    }
+
+    /**
+     * 获取图谱所有节点和边
+     * 返回的数据格式是{list:nodes,list:edges}
+     * @return 包含上述数据的Response
+     */
+    public Response getAllNodesAndEdges(){
+        List<Knowledge> allNodes=knowledgeDao.findAll();
+        List<Relationship> relationList=new ArrayList<>();
+        for(int i=0;i<allNodes.size();i++){
+            Set<Knowledge> define=allNodes.get(i).getDefine();
+            for(Knowledge knowledge:define){
+                relationList.add(new Relationship(i,allNodes.indexOf(knowledge),"define"));
+            }
+            Set<Knowledge> similar=allNodes.get(i).getSimilar();
+            for(Knowledge knowledge:similar){
+                relationList.add(new Relationship(i,allNodes.indexOf(knowledge),"similar"));
+            }
+
+            Set<Knowledge> sub=allNodes.get(i).getSub();
+            for (Knowledge knowledge:sub){
+                relationList.add(new Relationship(i,allNodes.indexOf(knowledge),"sub"));
+            }
+        }
+        GetKGReturnFormat getKGReturnFormat=new GetKGReturnFormat(allNodes,relationList);
+        return Response.buildSuccess(getKGReturnFormat);
+    }
+
 }

+ 16 - 0
src/main/java/com/seeckg/knowledgegraph_backend/util/GetKGReturnFormat.java

@@ -0,0 +1,16 @@
+package com.seeckg.knowledgegraph_backend.util;
+
+
+import com.seeckg.knowledgegraph_backend.pojo.Knowledge;
+import com.seeckg.knowledgegraph_backend.pojo.Relationship;
+
+import java.util.List;
+public class GetKGReturnFormat {
+    private List<Knowledge> knowledgeList;
+    private List<Relationship> relationshipList;
+
+    public GetKGReturnFormat(List<Knowledge> kList, List<Relationship> rList){
+        this.knowledgeList=kList;
+        this.relationshipList=rList;
+    }
+}

+ 1 - 1
src/test/java/com/seeckg/knowledgegraph_backend/Neo4jTest.java

@@ -19,6 +19,6 @@ public class Neo4jTest {
 
     @Test
     void testInsert() {
-        knowledgeDao.save(Knowledge.builder().creatorId(111L).name("name").context("context").bindClassId(0L).build());
+        knowledgeDao.save(Knowledge.builder().creatorId(111L).name("name2").context("context").bindClassId(0L).build());
     }
 }