当前位置:   article > 正文

利用Spring boot、neo4j、echarts可视化雪中悍刀行

利用Spring boot、neo4j、echarts可视化雪中悍刀行

效果如下:

搜索李淳罡:

 

添加节点和关系,在这里添加李淳罡-喜欢-绿袍,如下多出李淳罡和绿袍的关系和绿袍的节点

 

在neo4j中,存储如下图

SDN似乎无法任意存储关系,故在这里使用的neo4j-java-driver完成的以上功能:

[neo4jConfig.java]

加载驱动和提供session

  1. package com.sandalen.water.config;
  2. import com.sandalen.water.PropertiesClass.Neo4jProperties;
  3. import org.neo4j.driver.v1.AuthTokens;
  4. import org.neo4j.driver.v1.Driver;
  5. import org.neo4j.driver.v1.GraphDatabase;
  6. import org.neo4j.driver.v1.Session;
  7. import org.springframework.beans.factory.annotation.Autowired;
  8. import org.springframework.context.annotation.Bean;
  9. import org.springframework.context.annotation.Configuration;
  10. @Configuration
  11. public class Neo4jConfig {
  12. @Autowired
  13. private Neo4jProperties neo4jProperties;
  14. @Bean(name="NeoSession")
  15. public Session initDriver(){
  16. Session session = null;
  17. try
  18. {
  19. Driver driver = GraphDatabase.driver(neo4jProperties.getURI(), AuthTokens.basic(neo4jProperties.getUsername(), neo4jProperties.getPassword()));
  20. session = driver.session();
  21. return session;
  22. }
  23. catch (Exception e){
  24. e.printStackTrace();
  25. return session;
  26. }
  27. }
  28. }

 [CypherUtils.java]

这里主要定义了一些cypher语句

  1. package com.sandalen.water.other;
  2. import org.springframework.stereotype.Component;
  3. @Component
  4. public class CypherUtils {
  5. public static String createSingle(String entity){
  6. return "merge (m:Person{name:\""+entity+"\"}) return m";
  7. }
  8. public static String createRelationByTwoNodes(String entityFrom,String relation,String entityTo){
  9. return "match(m:Person{name:\""+entityFrom+"\"}),(n:Person{name:\""+entityTo+"\"}) merge (m)-[r:"+relation+"]->(n) return m,n,r";
  10. }
  11. public static String searchAll(String entityName){
  12. if(entityName == "" || entityName == null){
  13. return "match (m)-[edge]->(n) return m,edge,n";
  14. }
  15. return "match (m)-[edge]-(n) where n.name='" + entityName + "' return m,edge,n";
  16. }
  17. }

[Neo4jUtils.java]

添加、查找关系和节点的逻辑实现,其中添加的逻辑是先查询输入的两个实体,若任意实体不存在则先创建,再根据这两个节点添加关系。

查找的逻辑很简单,不再赘述。

  1. package com.sandalen.water.util;
  2. import com.sandalen.water.other.CypherUtils;
  3. import org.neo4j.driver.v1.*;
  4. import org.neo4j.driver.v1.types.Relationship;
  5. import org.springframework.beans.factory.annotation.Autowired;
  6. import org.springframework.stereotype.Component;
  7. import java.util.*;
  8. @Component
  9. public class Neo4jUtils {
  10. private static Session session;
  11. @Autowired
  12. public Neo4jUtils(Session NeoSession){
  13. Neo4jUtils.session= NeoSession;
  14. }
  15. public static boolean create(String entityFrom,String relation,String entityTo){
  16. try {
  17. StatementResult statementResult = session.run(CypherUtils.createSingle(entityFrom));
  18. List<Record> entityFromlist = statementResult.list();
  19. StatementResult entityToResult = session.run(CypherUtils.createSingle(entityTo));
  20. List<Record> entityToList = entityToResult.list();
  21. if(entityFromlist.size() == 0){
  22. session.run(CypherUtils.createSingle(entityFrom));
  23. }
  24. if(entityToList.size() == 0){
  25. session.run(CypherUtils.createSingle(entityTo));
  26. }
  27. StatementResult result = session.run(CypherUtils.createRelationByTwoNodes(entityFrom, relation, entityTo));
  28. return true;
  29. }
  30. catch (Exception e){
  31. e.printStackTrace();
  32. return false;
  33. }
  34. }
  35. public static Map<String,Object> searchAll(String entityName){
  36. StatementResult result = session.run(CypherUtils.searchAll(entityName));
  37. List<Record> list = result.list();
  38. Map<String,Object> resultMap = new HashMap<>();
  39. Set<String> nodes = new HashSet<>();
  40. List<String> relationships = new ArrayList<>();
  41. for (Record r : list){
  42. String start_node = r.get("m").get("name").toString().replace("\"","");
  43. String relationship = r.get("edge").asRelationship().type().replace("\"","");
  44. String end_node = r.get("n").get("name").toString().replace("\"","");
  45. // System.out.println(start_node + "-" + relationship + "->" + end_node );
  46. relationship = start_node + "-" + relationship + "-" + end_node;
  47. nodes.add(start_node);
  48. nodes.add(end_node);
  49. relationships.add(relationship);
  50. }
  51. resultMap.put("nodes",nodes);
  52. resultMap.put("relationships",relationships);
  53. return resultMap;
  54. }
  55. }

[controller]

  1. package com.sandalen.water.controller;
  2. import com.sandalen.water.bean.RespBean;
  3. import com.sandalen.water.service.KgService;
  4. import com.sandalen.water.util.Neo4jUtils;
  5. import org.springframework.beans.factory.annotation.Autowired;
  6. import org.springframework.web.bind.annotation.RequestMapping;
  7. import org.springframework.web.bind.annotation.RestController;
  8. import java.util.Map;
  9. @RequestMapping("/kg")
  10. @RestController
  11. public class KgController {
  12. @Autowired
  13. private KgService kgService;
  14. @RequestMapping("/saveEntity")
  15. public RespBean saveEntity(String entityFrom,String relation,String entityTo){
  16. boolean isCreated = Neo4jUtils.create(entityFrom, relation, entityTo);
  17. if(isCreated){
  18. return RespBean.ok("创建数据成功");
  19. }
  20. return RespBean.error("创建失败");
  21. }
  22. @RequestMapping("/search")
  23. public RespBean searchAll(String entityName){
  24. Map<String, Object> map = Neo4jUtils.searchAll(entityName);
  25. return RespBean.ok("获取数据成功",map);
  26. }
  27. }

[前端]

前端代码只提供页面的,我使用的是element Ui和vue

  1. <template>
  2. <div class="kgContainer">
  3. <div class="alert alert-info" role="alert" style="margin-bottom: 20px;">
  4. <h1>Prompt</h1>
  5. <font size="4">输入你想要搜索的知识</font>
  6. </div>
  7. <div class="search" style="text-align: center;margin-bottom: 50px;">
  8. <el-input v-model="entityName" placeholder="请输入内容" style="width: 400px;"></el-input>
  9. <el-button type="primary" icon="el-icon-search" @click="initData">搜索</el-button>
  10. <el-input v-model="form.entityFrom" placeholder="请输入内容" style="width: 100px;"></el-input>
  11. <el-input v-model="form.relation" placeholder="请输入内容" style="width: 100px;"></el-input>
  12. <el-input v-model="form.entityTo" placeholder="请输入内容" style="width: 100px;"></el-input>
  13. <el-button type="primary" icon="el-icon-search" @click="add">添加</el-button>
  14. </div>
  15. <div class="kgShow" id="kgShow" style="width: 100%;">
  16. </div>
  17. <!--<div class="test" id="tst" style="width: 100%;height: 500px;"></div>-->
  18. </div>
  19. </template>
  20. <script>
  21. var echarts = require('echarts');
  22. export default {
  23. data() {
  24. return {
  25. entityName: '',
  26. option: '',
  27. form: {
  28. entityFrom: '',
  29. relation: '',
  30. entityTo: ''
  31. },
  32. nodes: [],
  33. relationships: []
  34. }
  35. },
  36. mounted() {
  37. this.initData()
  38. this.tst()
  39. },
  40. inject: ['reload'],
  41. methods: {
  42. initData() {
  43. this.$store.dispatch('kg/search', this.entityName).then(response => {
  44. const nodes = response.nodes
  45. const relationship = response.relationships
  46. const nodesList = []
  47. for(let i = 0; i < nodes.length; i++) {
  48. const tmp = {
  49. name: nodes[i],
  50. symbolSize: 50,
  51. itemStyle: {
  52. normal: {
  53. show: true,
  54. }
  55. }
  56. }
  57. nodesList.push(tmp)
  58. }
  59. const links = []
  60. for(let i = 0; i < relationship.length; i++) {
  61. const relationshipArray = relationship[i].split("-")
  62. const tmp = {
  63. source: relationshipArray[0],
  64. target: relationshipArray[2],
  65. name: relationshipArray[1]
  66. }
  67. links.push(tmp)
  68. }
  69. const option = {
  70. title: {
  71. text: '雪中悍刀行'
  72. },
  73. tooltip: {
  74. formatter: function(x) {
  75. return x.data.name;
  76. }
  77. },
  78. animationDurationUpdate: 1500,
  79. animationEasingUpdate: 'quinticInOut',
  80. series: [{
  81. type: 'graph',
  82. layout: 'force',
  83. symbolSize: 80,
  84. roam: true,
  85. label: {
  86. normal: {
  87. show: true,
  88. }
  89. },
  90. edgeSymbol: ['circle', 'arrow'],
  91. edgeSymbolSize: [4, 10],
  92. edgeLabel: {
  93. normal: {
  94. textStyle: {
  95. fontSize: 20
  96. }
  97. }
  98. },
  99. force: {
  100. repulsion: 3000, //斥力
  101. edgeLength: [20, 80] //默认距离
  102. },
  103. // layout:'circular',
  104. draggable: true,
  105. lineStyle: {
  106. normal: {
  107. width: 2,
  108. color: '#4b565b',
  109. curveness: 0.2,
  110. length: 20
  111. }
  112. },
  113. edgeLabel: {
  114. normal: {
  115. show: true,
  116. formatter: function(x) {
  117. return x.data.name;
  118. }
  119. }
  120. },
  121. data: nodesList,
  122. links: links
  123. }]
  124. }
  125. this.option = option
  126. this.showKg()
  127. })
  128. },
  129. add() {
  130. this.$store.dispatch('kg/save', this.form).then(response => {
  131. if(response.status == 200) {
  132. this.$message({
  133. message: response.msg,
  134. type: 'success'
  135. })
  136. this.reload()
  137. }
  138. })
  139. },
  140. getInfo(params) {
  141. alert(params)
  142. },
  143. showKg() {
  144. const kgShow = document.getElementById('kgShow')
  145. const myChart = echarts.init(kgShow, 'light')
  146. myChart.setOption(this.option)
  147. let data = myChart._model.option.series[0].data;
  148. myChart.on("click", (chartParam) => {
  149. // console.log(myChart._model)
  150. // console.log(chartParam)
  151. const entityName = data[chartParam.dataIndex].name
  152. this.entityName = entityName
  153. this.initData()
  154. });
  155. myChart.on("mouseover", (chartParam) => {
  156. console.log(chartParam)
  157. })
  158. }
  159. }
  160. }
  161. </script>
  162. <style>
  163. .kgContainer {
  164. width: 100%;
  165. background-color: #FFFFFF;
  166. padding: 32px;
  167. }
  168. .kgShow {
  169. width: 100%;
  170. height: 500px;
  171. }
  172. </style>

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/904253
推荐阅读
相关标签
  

闽ICP备14008679号