Neo4J - ECharts力导向图

🎁重点的refs:

1. Neo4J创建数据

根据个人业务来,不做赘述。一般需要提前创建nodes和links两类csv
ps: 这数据库也不好写,写得不得劲儿。Neo4J有没有类似Navicat那种的可视化界面啊。

  1. 导入csv(⚠️提前手动保存在本地Neo4J的import文件夹中);
  2. 创建节点nodes;
  3. 创建连接links。

2. 前端连接Neo4J

index.html

<!doctype html>
<html>
<head>
    <title>Neovis.js Simple Example</title>
</head>

<!-- 顺序别反了 -->
<script src="https://unpkg.com/neo4j-driver"></script>
<script type="text/javascript" src="prepare_neo4j.js"></script>

</html>

prepare_neo4j.js

(async () => {
  // URI examples: 'neo4j://localhost', 'neo4j+s://xxx.databases.neo4j.io'
  const URI = 'bolt://localhost:7687';
  const USER = 'neo4j';
  const PASSWORD = '12345678';
  let driver

  try {
    driver = neo4j.driver(URI, neo4j.auth.basic(USER, PASSWORD))
    const serverInfo = await driver.getServerInfo()
    console.log('Connection established')
    console.log(serverInfo)
  } catch(err) {
    console.log(`Connection error\n${err}\nCause: ${err.cause}`)
  }
})();

运行html控制台结果


3. 可视化工具

3.1. 知识图谱的可视化形式

知识图谱主要是以图形式实现的,而图形式在可视化领域有多种类别,重点就是关注节点和连线的排布情况。下面的参考帖子也并没有列举完所有的布局,但是写的很好!
美团 - 知识图谱布局
来源: 美团 - 知识图谱布局

3.2. 前端工具选择

  1. D3.js:太复杂了,不好写,而且近年社区没那么活跃了;现在期待Threejs、p5js之类的多多开发可视化的东东!我需要特别美观的!D3看到的所有示例都不好看。
  2. Neovis.js:运行成功了,但是发现不美观,而且自定义程度太低了,参考文档等于没有;
  3. ECharts:还得是ECharts,不仅开箱就美观,定制程度也高。

3.3. ECharts实现力导向图

index.html

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Title</title>
	<style>
    html, body {
      height: 100%;
      margin: 0;
      padding: 0;
    }
    #main {
      width: 100%;
      height: 100%;
    }
	</style>
</head>

<body>
<div id="main"></div>
</body>

<script src="echarts.js"></script>
<!-- browser项目声明 -->
<script src="https://unpkg.com/neo4j-driver"></script>
<script src="prepare_neo4j.js"></script>

</html>

prepare_neo4j.js

(async () => {
  const URI = 'bolt://localhost:7687';
  const USER = 'neo4j';
  const PASSWORD = '12345678';
  let driver;

  try {
    driver = neo4j.driver(URI, neo4j.auth.basic(USER, PASSWORD));
    const serverInfo = await driver.getServerInfo();
    console.log('Connection established');
    console.log(serverInfo);

    const session = await driver.session();
    // !!! 这句CYPHER尽量保持这种path=(node_source)-[relationship]->(node_target)形式,不然后面代码会一直改来改去!!!
    const result = await session.run(
      'MATCH path=(i:Ingredient)-[r:HAS_BENEFIT]->(b)\nRETURN path'
    );

    // 打印 Neo4j 返回的数据
    console.log(result.records.map((record) => record.get('path')));

    // 将 Neo4J 数据转换为 ECharts 所需的格式
    function convertNeo4jDataToEchartsData(paths) {
      const nodes = new Map();
      const links = [];
      const categories = new Set();

      paths.forEach((path) => {
        path.segments.forEach(({ start, end, relationship }) => {
          // 添加起始节点
          addNode(nodes, categories, start);

          // 添加结束节点
          addNode(nodes, categories, end);

          // 添加关系
          addRelationship(links, nodes, relationship);
        });
      });

      return {
        nodes: Array.from(nodes.values()),
        links,
        categories: Array.from(categories),
      };
    }

    function addNode(nodes, categories, node) {
      const category = node.labels[0];
      categories.add(category);

      const nodeName = `${node.properties.id}`;
      nodes.set(node.identity.low, {
        id: node.identity.low,
        name: nodeName,
        category,
        ...node.properties,
      });
    }
    
	// 我这里为了后面连线颜色,互换了source和target的
	// source原本应该是start,target是end
    function addRelationship(links, nodes, relationship) {
      links.push({
        source: nodes.get(relationship.end.low).id,
        target: nodes.get(relationship.start.low).id,
        label: relationship.type,
        properties: relationship.properties,
      });
    }

    // 获取 ECharts 需要的数据并显示
    const { nodes, links, categories } = convertNeo4jDataToEchartsData(result.records.map((record) => record.get('path')));
    displayEchartsGraph(nodes, links, categories);

    // 关闭连接
    await session.close();
    await driver.close();
  } catch (err) {
    console.log(`Connection error\n${err}\nCause: ${err.cause}`);
  }
})();

function displayEchartsGraph(nodes, links, categories) {
  const myChart = echarts.init(document.getElementById('main'));
  
  console.log("Nodes:", nodes);
  console.log("Links:", links);
  console.log("Categories:", categories);

  const categoryColors = {
    "category1": "#BFC4C4",
    "category2": "#91cc75",
    "category3": "#fac858"
  };

  const option = {
    backgroundColor: '#000000',
    tooltip: {
      trigger: 'item',
      formatter: (params) => {
        if (params.dataType === 'node') {
          return `${params.data.name}<br>${params.data.description}`;
        } else {
          return `${params.data.source} --[${params.data.label}]--> ${params.data.target}`;
        }
      }
    },
    legend: [
      {
        data: categories,
        orient: 'vertical',
        left: 'left',
        top: 'center',
        textStyle: { color: '#fff' }
      }
    ],
    series: [
      {
        type: 'graph',
        layout: 'force',
        data: nodes,
        links: links,
        categories: Object.keys(categoryColors).map((name) => ({
          name: name,
          itemStyle: {
            color: categoryColors[name],
          },
        })),
        roam: true,
        // label: {
        //   show: true,
        //   position: 'inside'
        // },
        lineStyle: {
          color: 'source',
          curveness: 0.3,
          width: 7
        },
        nodeScaleRatio: 0.6,
        force: {
          repulsion: 108,
          gravity: 0.1,
          edgeLength: [0, 7]
        },
        visualMap: {
          min: 0.2,
          max: 2,
          precision: 2,
          calculable: true,
          realtime: true,
          orient: 'horizontal',
          left: 'center',
          top: 'top',
          color: ['#800026', '#ffffcc']
        },
        scaleLimit: {
          min: 0.1,
          max: 0.26
        },
        animation: {
          duration: 50000, // 设置动画持续时间为5秒
          loop: true // 循环播放动画
        },
      }
    ]
  };

  myChart.setOption(option);
}

展示

力导向图的原本的终态

互换了source和target还有linewidth,美哭了
筛选了节点的效果

还设计了海报,真的绝美(自卖自夸),等毕设结束了放上来:)

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值