简要概述

近期公司项目要实现一个知识图谱的功能,所以了解到了neo4j,接着去查询相关的实现方案,就选定了neovis来进行实现,因为它可以直接对接neo4j的数据,不需要对接接口

首先我使用的neovis的版本为^2.1.0,开始使用的是^1.5.0版本的,但是由于这个版本不能对节点进行样式设置,所以就选用了最新的版本2.1.0

准备步骤

首先,要下载neovis

npm install neovis.js@2.1.0

注意:因为我们下面有一些业务上的查询,所以也要同步下载neo4j-driver,这里我下载的是4.4.0版本的

npm install neo4j-driver@4.4.0

接着引入依赖,根据个人情况来

import NeoVis from 'neovis.js';
import neo4j from "neo4j-driver";

可能有些人在一些稍旧点的项目中直接这样引入会报解析错误,那么就需要在配置项中配置一下代码:

const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
  transpileDependencies: ["neovis.js"]
})

当然这也只是个很小的示例错误,具体的还要看自己项目中是什么导致的 

开发阶段

基本配置

首先我们需要进行配置我们的数据节点

const config = {
        containerId: "viz1", // canvas容器
        neo4j: {
          serverUrl: "bolt://地址:7687", // 相对应要连接的地址,其中7687端口是固定的,不需要更改
          serverUser: "neo4j", // 用户名
          serverPassword: "你的密码", // 密码
        },
        visConfig: {
          physics: {
            enabled: true,
            solver: "forceAtlas2Based",
            forceAtlas2Based: {
              springLength: 400,
            },
          },
          nodes: { // 节点配置
            size: 30, // 节点大小
            shape: "circle", // 节点形状,这里是圆形,还可以设置为square方形等
          }, 
          edges: { // 边边配置(关系关联)
            smooth: {
              enabled: false,
              type: "straightCross",
            },
            arrows: { // 箭头设置
              to: { //箭头方向
                enabled: true,
              },
            },
            color: "#45B7D1", // 边边颜色
            width: 0.5, // 边边宽度
          },
        },
        raw: true, // 这个可以不加,我搜出来的说是方便调试,没看出来效果O.o
        labels: labels, // 这里是我写的变量去设置节点显示的内容,下面会具体展示
        relationships: relations, // 这个同理
        // 这个是初始查询语句,可以换成自己的查询语句哦
        initialCypher: `MATCH (n) 
          OPTIONAL MATCH (n)-[r]->(m)
          RETURN n, r, m
          LIMIT 25`,
      };

labels配置如下,仅作参考 

stock : { // stock为Neo4j 节点的 label
      label: "name", // 对应显示的字段属性,在之前是使用caption的,2.1.0换成了label
      [NeoVis.NEOVIS_ADVANCED_CONFIG]: {
        static: {
          shape: "circle",
          font: { // 文字设置
            size: 14,
            color: "#000000",
            strokeWidth: 0, // 文字描边
            strokeColor: null, // 文字描边
          },
        },
        function: { // 对于其他需要特定展示的属性进行设置
          label: (node) => { // label设置为超过三个字符使用…代替
            const raw = node.properties.name || "";
            return raw.length > 3 ? raw.slice(0, 3) + "…" : raw;
          },
          title: (props) => // 鼠标移动上去悬浮的数据
            NeoVis.objectToTitleHtml(props, ["name", "pagerank"]),
        },
      }
}

relationships配置如下

relationships : {
   同行 : { // 这个是需要明确指定的关系,关系类型(同行)必须与 Neo4j 里的完全一致,还需要区分大小写
      [NeoVis.NEOVIS_ADVANCED_CONFIG]: {
        function: {
          label: edge => edge.type, // 这个是要根据表里的哪个字段作为展示的关系文字
        },
        static: {
          width: 2, // 关系线条设置
          font: { size: 14, color: "#000000" },// 这个就是文字的设置
        },
      },
    }
}

基本的配置就是这样,接着去将它渲染到页面上,把这些写进一个函数中,在mounted阶段执行就可以了,这样一个简单的结构图就出来了

// 在config定义后写这个就ok了
this.viz = new NeoVis(config);
this.viz.render();

补充:

这里是需要进行复杂查询的同学看的,我们首先需要创建一个driver实例

created() {
    this.driver = neo4j.driver(
      "bolt://地址:7687",
      neo4j.auth.basic("neo4j", "密码") // 参数分别为用户名和密码
    );
  },

 添加业务

我这个还需要点击父节点进行子节点的查询,这样我们就需要加给节点加上点击事件,代码如下就在初始执行的函数内加入即可

this.viz = new NeoVis(config);
this.viz.render();
this.viz.registerOnEvent("clickNode", (node) => {
   this.nodeClick(node);
});

点击事件如下

async nodeClick(node) {
      if (this.isQueryRunning) return; // 这个是我定义的一个是否执行的标志位,可以不写
      this.isQueryRunning = true;
      const nodeId = node.nodeId; // 这个是我们需要拿到点击node的id
      if (!this.driver) { // 我们在进行查询的时候需要用到neo4j,这个实例已经在前面写过了
        console.error("Driver is not initialized");
        return;
      }
      const session = this.driver.session(); // 这里我们就需要使用driver实例里面的方法去跑我们要执行的查询语句了
      try {
        // 首先我们要确保我们点击的父节点是有子节点的,所以我们要先查询出来它的子节点数量,看是否存在
        const checkResult = await session.run(`
           MATCH (n)-[r]->()
           WHERE id(n) = ${nodeId}
           RETURN count(r) AS relationshipCount
         `);
        const relationshipCount =
          checkResult.records[0].get("relationshipCount");
        const hasRelationships = relationshipCount > 0;
        // 如果不存在直接就不执行了
        if (!hasRelationships) {
          return;
        }
        // 存在所执行的程序,
        const statement = `
          MATCH (n)-[r]->(m)
          WHERE id(n) = ${nodeId}
          RETURN n, r, m
        `;
        // 这里我们使用renderWithCypher方法来进行展示,这个就相当于又重新执行了一遍this.viz.render()
        // 它还有一个updateWithCypher方法,updateWithCypher方法可以让查询出来的子节点和
        // 父节点在一个画布上,我有试过updateWithCypher方法,效果不理想就是我点击多个节点后,它会不断触发nodeClick这个事件,所以我就直接换成了renderWithCypher
        this.viz.renderWithCypher(statement, { reset: false });
      } catch (error) {
        console.error("查询出错:", error);
      } finally {
        await session.close(); // 最后我们需要把它关闭
        this.isQueryRunning = false;
      }
    },

结束

这样就可以让我们点击父节点出现它下面的子节点啦,到此结束,各位小伙伴们可以参考一下这篇文章

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐