本文最終技術(shù)架構(gòu):neo4jd3 + Spring boot + Spring Data Neo + neo4j
當(dāng)我們剛開(kāi)是接觸圖數(shù)據(jù)庫(kù)的時(shí)候,我們進(jìn)行各種關(guān)系查詢,最終會(huì)得到一個(gè)拓?fù)鋱D。和我們以前使用的數(shù)據(jù)庫(kù)不一樣的是,我們的數(shù)據(jù)庫(kù)查詢出來(lái)是一系列的表。
事實(shí)上,我們的圖數(shù)據(jù)返回的的數(shù)據(jù)是類(lèi)似于下面這樣的格式的,然后通過(guò)前端(Neo4j Browser )來(lái)幫我們將返回的數(shù)據(jù)繪制成網(wǎng)絡(luò)拓?fù)鋱D。在我們之前的文章中介紹的Spring Data Neo中,返回的也都是java對(duì)象的數(shù)據(jù)
接下來(lái)本文就是介紹使用一些前端技術(shù)來(lái)幫我們將圖數(shù)據(jù)庫(kù)的數(shù)據(jù)返回給前端進(jìn)行旋繞
1.現(xiàn)成的工具
比如Neo4j Browser 、Neo4j Bloom、這些官方提供的工具,免費(fèi)或者有商業(yè)版權(quán),這些工具特點(diǎn)都是人家已經(jīng)開(kāi)發(fā)好的工具,你安裝上使用就行了。
例如Neo4j Browser,這些工具就好比,我們Navicat 、Sql Log、PL SQL這些客戶端連接工具連接關(guān)系型數(shù)據(jù)庫(kù)(mysql、oracle、post gre)等。本文就不在詳細(xì)介紹。
2.Neo4j JavaScript Driver
Neo4j JavaScript Driver 是一個(gè)用于在 JavaScript 應(yīng)用程序中與 Neo4j 圖數(shù)據(jù)庫(kù)進(jìn)行通信的官方驅(qū)動(dòng)程序。它提供了與 Neo4j 服務(wù)器進(jìn)行連接、執(zhí)行 Cypher 查詢和處理查詢結(jié)果等功能。我們可以在Jquery、React、Angular、Vue等前端框架中使用該驅(qū)動(dòng)。
驅(qū)動(dòng)安裝
npm install neo4j-driver
代碼示例
const neo4j = require('neo4j-driver')
const driver = neo4j.driver(uri, neo4j.auth.basic(user, password))
const session = driver.session()
const personName = 'Alice'
try {
const result = await session.run(
'CREATE (a:Person {name: $name}) RETURN a',
{ name: personName }
)
const singleRecord = result.records[0]
const node = singleRecord.get(0)
console.log(node.properties.name)
} finally {
await session.close()
}
// on application exit:
await driver.close()
官方地址:neo4j-javascript
適用于:前端直接和Neo4J直接連接
3.neovis
Neovis.js 是一個(gè)純 JavaScript 庫(kù),使用 JavaScript 語(yǔ)言編寫(xiě)和開(kāi)發(fā)開(kāi)源框架。它可以在瀏覽器環(huán)境中直接使用,也可以與其他 JavaScript 框架和庫(kù)集成,如 React、Angular 或 Vue.js
- Neovis.js 使用 Neo4j JavaScript Driver 與 Neo4j 圖數(shù)據(jù)庫(kù)進(jìn)行通信。
- Neovis.js 在 Vis.js 的基礎(chǔ)上構(gòu)建了對(duì) Neo4j 數(shù)據(jù)庫(kù)的特定集成和功能
github地址:github-neovis.js
下面這是一個(gè)官方的示例:
Neovis.js可以通過(guò)npm安裝
npm install --save neovis.js
Neovis.js可以從Neo4jLabs CDN獲得
<script src="https://unpkg.com/neovis.js@2.0.2"></script>
<script src="https://unpkg.com/neovis.js@2.0.2/dist/neovis-without-dependencies.js"></script>
代碼示例
需要在代碼中定義每個(gè)節(jié)點(diǎn),邊,例如下乳,查詢用戶和角色
<script type="text/javascript">
let neoViz;
function draw() {
const config = {
containerId: "viz",
neo4j: {
serverUrl: "bolt://localhost:7687",
//neo4j的用戶名和密碼
serverUser: "neo4j",
serverPassword: "neo4j",
},
labels: {
//節(jié)點(diǎn)的標(biāo)簽1(節(jié)點(diǎn)類(lèi)型:用戶)
User: {
//在User類(lèi)型的節(jié)點(diǎn)上,使用userName作為節(jié)點(diǎn)的顯示
label: "userName"
},
//節(jié)點(diǎn)的標(biāo)簽2(節(jié)點(diǎn)類(lèi)型:角色)
Role: {
//在Role類(lèi)型的節(jié)點(diǎn)上,使用roleName作為節(jié)點(diǎn)的顯示
label: "roleName",
}
//節(jié)點(diǎn)的標(biāo)簽3.......
},
relationships: {
//關(guān)系1(邊)
PLAY_THE_ROLE: {
value: "name"
}
},
//Cypher語(yǔ)句
initialCypher: "MATCH (n)-[r:PLAY_THE_ROLE]->(m) RETURN *"
};
neoViz = new NeoVis.default(config);
neoViz.render();
}
</script>
4.neo4jd3
neo4jd3使用D3.js實(shí)現(xiàn)Neo4j圖形可視化。
github地址:githug-neo4jd3,表現(xiàn)效果如下:
4.1neo4jd3和neovis對(duì)比
neo4jd3和neovis是兩個(gè)完全不同的組件,使用方式也不一樣。
-
在底層依賴上:
neovis.js 是基于 Vis.js、neo4j JavaScript Driver 構(gòu)建的,而 neo4jd3.js 基于 D3.js。 -
在功能上:
neovis.js 能夠直接和neo4j 數(shù)據(jù)庫(kù)相連,將數(shù)據(jù)庫(kù)查詢結(jié)果直接進(jìn)行渲染,而neo4jd3則不和數(shù)據(jù)庫(kù)相連,而是通過(guò)數(shù)據(jù)進(jìn)行渲染。所以對(duì)于neo4jd3來(lái)說(shuō),只要能提供數(shù)據(jù),就能渲染,因此我們可以使用任何技術(shù)為neo4jd3來(lái)進(jìn)行獲取數(shù)據(jù),最后將數(shù)據(jù)給neo4jd3。 -
在渲染上:
neovis.js側(cè)重于將數(shù)據(jù)庫(kù)的查詢語(yǔ)句發(fā)送給數(shù)據(jù),然后渲染數(shù)據(jù)庫(kù)返回的結(jié)果值,因此在渲染效果上存在很多的不友好一面。而neo4jd3并不關(guān)心查詢語(yǔ)句如何編寫(xiě),數(shù)據(jù)庫(kù)如何查詢,只對(duì)最后的數(shù)據(jù)進(jìn)行渲染,因此在渲染效果上就體現(xiàn)的非常友好
以下是對(duì)同一個(gè)數(shù)據(jù)的查詢結(jié)果進(jìn)行的渲染對(duì)比,左圖是neovis,右圖neo4jd3,單從拓?fù)鋱D上來(lái)說(shuō),左邊的效果就很差
4.2獲取neo4jd3
從倉(cāng)庫(kù)中下載代碼,在dist目錄下,有css和js
git clone https://github.com/eisman/neo4jd3.git
4.3neo4jd3的數(shù)據(jù)結(jié)構(gòu)
我們先看官網(wǎng)給的兩組Json,也就是需要我們的數(shù)據(jù)組織者按照如下格式進(jìn)行數(shù)據(jù)格式組織
返回節(jié)點(diǎn)和關(guān)系的json
{
"nodes": [
{
"id": "1",
"labels": ["User"],
"properties": {
"userId": "eisman"
}
},
{
"id": "8",
"labels": ["Project"],
"properties": {
"name": "neo4jd3",
"title": "neo4jd3.js",
"description": "Neo4j graph visualization using D3.js.",
"url": "https://eisman.github.io/neo4jd3"
}
}
],
"relationships": [
{
"id": "7",
"type": "DEVELOPES",
"startNode": "1",
"endNode": "8",
"properties": {
"from": 1470002400000
},
"source": "1",
"target": "8",
"linknum": 1
}
]
}
返回繪制圖的Json
{
"results": [
{
"columns": ["user", "entity"],
"data": [
{
"graph": {
"nodes": [
{
"id": "1",
"labels": ["User"],
"properties": {
"userId": "eisman"
}
},
{
"id": "8",
"labels": ["Project"],
"properties": {
"name": "neo4jd3",
"title": "neo4jd3.js",
"description": "Neo4j graph visualization using D3.js.",
"url": "https://eisman.github.io/neo4jd3"
}
}
],
"relationships": [
{
"id": "7",
"type": "DEVELOPES",
"startNode": "1",
"endNode": "8",
"properties": {
"from": 1470002400000
}
}
]
}
}
]
}
],
"errors": []
}
4.4Spring data neo
我們現(xiàn)在已經(jīng)知道了neo4jd3繪制圖的Json格式了,現(xiàn)在就需要我們后臺(tái)查詢數(shù)據(jù),然后返回
4.4.1 定義返回?cái)?shù)據(jù)格式
我們當(dāng)然也能通過(guò)數(shù)據(jù)格式發(fā)現(xiàn),嵌套有點(diǎn)深,這里推薦按照這個(gè)格式來(lái),因?yàn)椴贿@樣的話,你就得要求修改前端組建的源代碼了。下面這個(gè)是前端渲染數(shù)據(jù)的一部分代碼,如果后端返回的數(shù)據(jù)不按照這個(gè)格式來(lái)的話,前端這里就需要你修改代碼了。
這里我避免創(chuàng)建很多單一屬性的類(lèi),因此采用了內(nèi)部類(lèi)的方式,這里你不一定才用內(nèi)部類(lèi),只要能返回和上面的Json格式就行文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-676676.html
4.4.1.1NeoResults
@lombok.Data
public class NeoResults {
private List<Data> results = new ArrayList<>();
public NeoResults() {
super();
results.add(new NeoResults.Data());
}
@lombok.Data
public class Data{
private List<Graph> data = new ArrayList<>();
public Data() {
super();
data.add(new Data.Graph());
}
@lombok.Data
public class Graph{
private GraphVO graph = new GraphVO();
}
}
public void setNodes(List<NodeVO> nodes) {
results.get(0).getData().get(0).getGraph().setNodes(nodes);
}
public void setRelationships(List<ShipVO> relationships) {
results.get(0).getData().get(0).getGraph().setRelationships(relationships);
}
}
4.4.1.2GraphVO
@Data
public class GraphVO {
private List<NodeVO> nodes = new ArrayList<>();
private List<ShipVO> relationships = new ArrayList<>();
}
4.4.1.3NodeVO
@Data
@AllArgsConstructor
@NoArgsConstructor
public class NodeVO{
private Long id;
private List<String> labels;
private Map<String, Object> properties;
}
4.4.1.4ShipVO
@Data
public class ShipVO {
private Long id;
private String type;
private Long startNode;
private Long endNode;
private Map<String, Object> properties;
}
4.4.2 SDN查詢解析
4.4.2.1 Repo查詢語(yǔ)句
public interface D3jsRepo extends Neo4jRepository<Object, Long> {
/**
* @description:查詢路徑:根據(jù)roadName查詢Road標(biāo)簽查詢路徑
* @author:hutao
* @mail:hutao1@epri.sgcc.com.cn
* @date:2023年8月21日 下午2:11:09
*/
@Query("MATCH (n:Road{name:$roadName}) MATCH path=(n)-[*]->(n1) RETURN path")
List<Map<String, InternalPath.SelfContainedSegment[]>> findPathsByRoadName(@Param("roadName") String roadName);
/**
* @description:查詢路徑:根據(jù)標(biāo)簽label和某個(gè)屬性字段查詢路徑,性能比較慢,謹(jǐn)慎使用
* @author:hutao
* @mail:hutao1@epri.sgcc.com.cn
* @date:2023年8月21日 下午3:22:02
*/
@Query("MATCH (n) WHERE $label IN labels(n) AND n[$property] = $value MATCH path=(n)-[*]->(n1) RETURN path")
List<Map<String, InternalPath.SelfContainedSegment[]>> findByLabelAndProperty(@Param("label") String label, @Param("property") String property, @Param("value") String value);
/**
* @description:查詢路徑:根據(jù)標(biāo)主鍵ID查詢路徑
* @author:hutao
* @mail:hutao1@epri.sgcc.com.cn
* @date:2023年8月21日 下午3:42:52
*/
@Query("MATCH (n) WHERE id(n) = $id MATCH path=(n)-[*]->(n1) RETURN path")
List<Map<String, InternalPath.SelfContainedSegment[]>> findPathById(@Param("id") Long id);
}
4.4.2.2 解析Repo查詢
@Service
public class D3jsServiceImpl implements D3jsService{
@Autowired
private D3jsRepo d3jsRepo;
/**
* @description:通過(guò)節(jié)點(diǎn)ID找路徑,以該節(jié)點(diǎn)為起點(diǎn)
* @author:hutao
* @mail:hutao1@epri.sgcc.com.cn
* @date:2023年8月22日 上午11:17:13
*/
@Override
public NeoResults findPathsById(Long id) {
NeoResults neoResult = new NeoResults();
List<NodeVO> nodes = new ArrayList<>();
List<ShipVO> relationships = new ArrayList<>();
List<Map<String, InternalPath.SelfContainedSegment[]>> paths = d3jsRepo.findPathById(id);
for (Map<String, InternalPath.SelfContainedSegment[]> path : paths) {
SelfContainedSegment[] segments = path.get("path");
for (SelfContainedSegment segment : segments) {
addNode(nodes, segment.start());
addNode(nodes, segment.end());
addShip(relationships, segment.relationship());
}
}
neoResult.setNodes(nodes);
neoResult.setRelationships(relationships);
return neoResult;
}
/**
* @description:添加關(guān)系
* @author:hutao
* @mail:hutao1@epri.sgcc.com.cn
* @date:2023年8月16日 下午1:23:54
*/
private void addShip(List<ShipVO> relationships, Relationship shipTemp) {
ShipVO shipVO =new ShipVO();
shipVO.setId(shipTemp.id());
shipVO.setStartNode(shipTemp.startNodeId());
shipVO.setEndNode(shipTemp.endNodeId());
shipVO.setType(shipTemp.type());
shipVO.setProperties(shipTemp.asMap());
relationships.add(shipVO);
}
/**
* @description:添加節(jié)點(diǎn)
* @author:hutao
* @mail:hutao1@epri.sgcc.com.cn
* @date:2023年8月16日 下午2:27:37
*/
private void addNode(List<NodeVO> nodes, Node nodeTemp) {
NodeVO noveVO = new NodeVO();
List<String> labels = new ArrayList<>();
nodeTemp.labels().forEach(labels::add);
noveVO.setId(nodeTemp.id());
noveVO.setLabels(labels);
noveVO.setProperties(nodeTemp.asMap());
nodes.add(noveVO);
}
}
4.4.2.3返回解析結(jié)果
@GetMapping("/node/info/path/{id}")
@ApiOperationSupport(order = 3)
@ApiOperation(value = "3獲取指定節(jié)點(diǎn)為起點(diǎn)的路徑")
public NeoResults queryNodeTopo(@PathVariable Long id) {
NeoResults findPaths = d3jsService.findPathsById(id);
return findPaths;
}
4.4.3前端處理渲染
<link rel="stylesheet" href="/plugin/neod3/css/neo4jd3.min.css">
<script src="/plugin/neod3/js/d3.min.js"></script>
<script src="/plugin/neod3/js/neo4jd3.js"></script>
光路起點(diǎn)<select id = "selectRoad" class="selectpicker" onchange = "changeRoad()" data-live-search="true" data-style="btn-info" title="請(qǐng)選擇起點(diǎn)光路" ></select>
<div id="neo4jd3"></div>
/**
* @description:選擇光路觸發(fā)加載光路的路徑
* @author:hutao
* @mail:hutao1@epri.sgcc.com.cn
* @date:2023年8月17日 下午2:10:18
*/
function changeRoad(){
let select = $('#selectRoad').val();
let url = '/node/info/path/'+select;
let resultData = httpRequestForJson(url,"","GET");
loadNeod3Topo(resultData);
}
/**
* @description:初始化節(jié)點(diǎn)拓?fù)涫噶繄D
* @author:hutao
* @mail:hutao1@epri.sgcc.com.cn
* @date:2023年8月17日 下午2:18:48
*/
var neo4jd3
function loadNeod3Topo(resultData){
neo4jd3 = new Neo4jd3('#neo4jd3', {
//showLabel源代碼中不存在,是我自己添加的,實(shí)現(xiàn)效果為:節(jié)點(diǎn)是否顯示節(jié)點(diǎn)標(biāo)簽
showLabel: true,
minCollision: 100,
//neo4jDataUrl: '/aaa/bbbb',
neo4jData: resultData,
nodeRadius: 25,
onNodeDoubleClick: function(node) {
console.log('double click on node: ' + JSON.stringify(node));
},
onRelationshipDoubleClick: function(relationship) {
console.log('double click on relationship: ' + JSON.stringify(relationship));
},
//自動(dòng)縮放
zoomFit: true,
});
}
4.5實(shí)現(xiàn)效果
文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-676676.html
到了這里,關(guān)于圖數(shù)據(jù)庫(kù)Neo4j學(xué)習(xí)五渲染圖數(shù)據(jù)庫(kù)neo4jd3的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!