做Django项目时,要求根据绘制echarts三级力导向图,搜索了很多文章,好像都没有这种做法,因此记录一下。
如果对echarts绘力导向图还不是很了解,可以参考:
功能: 在前端界面输入要查的实体,在neo4j中搜索后,显示与这个实体相连的三层级echarts图。
1. 先在neo4j中创捷节点和关系
CREATE(p:标签1{title:"实体"})
CREATE(p:标签1{title:"实体1"})
CREATE(p:标签1{title:"实体2"})
CREATE(p:标签1{title:"实体11"})
CREATE(p:标签1{title:"实体22"})
MATCH (e:标签1{title:"实体"}),(cc:标签1{title:"实体1"})
CREATE (e)-[:RELATION{type:"关系1"}]->(cc)
MATCH (e:标签1{title:"实体"}),(cc:标签1{title:"实体2"})
CREATE (e)-[:RELATION{type:"关系2"}]->(cc)
MATCH (e:标签1{title:"实体1"}),(cc:标签1{title:"实体11"})
CREATE (e)-[:RELATION{type:"关系11"}]->(cc)
MATCH (e:标签1{title:"实体2"}),(cc:标签1{title:"实体22"})
CREATE (e)-[:RELATION{type:"关系22"}]->(cc)
2. 实体查询代码
在relation_view.py定义search_entity函数,用来查找数据,并对数据进行处理
from toolkit.pre_load import neo_con
import os
def search_entity(request):
ctx = {}
#根据传入的实体名称搜索出关系
if(request.GET):
entity = request.GET['user_text']
#连接数据库
db = neo_con
# 在entityRelation中第一个值记录实体2的数量
entityRelation = []
entityRelation = db.getEntityRelationbyEntity(entity)
entitycount = len(entityRelation)
# 添加第二层实体的实体名、关系
for i in range(entitycount):
# 获取第二层实体名称
entity2 = {'entity2': entityRelation[i]['entity2']['title']}
# 按照第二层实体名称查找第三层实体及其关系
entityRelation_two = []
entityRelation_two = db.getEntityRelationbyEntity(entity2['entity2'])
# 将查到的第三层实体以及关系存储到nextRelation中
entityRelation[i]['nextRelation'] = entityRelation_two
if len(entityRelation) == 0:
#若数据库中无法找到该实体,则返回数据库中无该实体
ctx= {'title' : '<h1>数据库中暂未添加该实体</h1>'}
return render(request,'entity.html',{'ctx':json.dumps(ctx,ensure_ascii=False)})
else:
#返回查询结果
#将查询结果按照"关系出现次数"的统计结果进行排序
entityRelation = sortDict(entityRelation)
return render(request, 'entity.html', {'entityRelation': json.dumps(entityRelation, ensure_ascii=False)})
return render(request,"entity.html",{'ctx':ctx})
其中,相关的函数(neo_models.py)如下所示:
# 根据entity的名称返回关系
def getEntityRelationbyEntity(self, value):
answer = self.graph.run("MATCH (entity1) - [rel] -> (entity2) WHERE entity1.title = \"" +str(value)+"\" RETURN rel,entity2").data()
return answer
def sortDict(relationDict):
for i in range( len(relationDict) ):
relationName = relationDict[i]['rel']['type']
relationCount = relationCountDict.get(relationName)
if(relationCount is None ):
relationCount = 0
relationDict[i]['relationCount'] = relationCount
relationDict = sorted(relationDict,key = lambda item:item['relationCount'],reverse = True)
return relationDict
打印 entityRelation ,即通过search_entity处理后的数据,如图所示:
注意: 此处的nextRelation是创建三层级的重点,后续就是根据nextRelation来判断此节点是否具有下层节点。
同理,如果想创建更多层级的数据,可以在对应的节点中加入nextRelation,将下层节点的信息存储其中。
entity.html用于展示查询实体界面,后边嵌入js代码 ,对search_entity返回的数据做处理,并通过echarts显示。
{% extends "base.html" %} {% block mainbody %}
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title></title>
<meta charset="utf-8" />
<script src="/static/js/echarts.js"></script>
{# <script type="text/javascript" src="http://echarts.baidu.com/gallery/vendors/echarts/echarts-all-3.js"></script>#}
</head>
<title>实体</title>
<div class="container">
<div class="row">
<!--head start-->
<div class="col-md-12">
<h3 class="page-header"><i class="fa fa-share-alt" aria-hidden="true"></i> 实体查询 </h3>
<ol class="breadcrumb">
<li><i class="fa fa-share-alt" aria-hidden="true"></i>实体查询</li>
</ol>
</div>
<div class = "col-md-12">
<div class="panel panel-default ">
<header class = "panel-heading">
查询条件:
</header>
<div class = "panel-body">
<!--搜索框-->
<form method = "get" id = 'searchEntityForm'>
<div >
<div class="input-group">
<input type="text" id = "user_text" name = "user_text" class="form-control" placeholder="输入实体名称" aria-describedby="basic-addon1">
<span class="btn btn-primary input-group-addon" type="button" id="relationSearchButton" style="background-color:#4592fe ; padding:6px 38px" onclick="document.getElementById('searchEntityForm').submit();">查询</span>
</div>
</div>
</form>
</div>
</div>
</div>
<p>
<div class = "col-md-12">
{% if ctx %}
<div class="panel panel-default">
<header class ="panel-heading">
<div class = "panel-body">
<h2>数据库中暂未添加该实体</h2>
</div>
</header>
</div>
{% endif %}
</div>
</p>
<!--relation start-->
{% if entityRelation %}
<!-- entityRelation是relation_view.py中的 -->
<!-- 为ECharts准备一个具备大小(宽高)的Dom -->
<div class = "col-md-12">
<div class="panel panel-default ">
<header class="panel-heading">
关系图 :
</header>
<div class = "panel-body ">
<div id="graph" style="width: 90%;height:600px;"></div>
</div>
</div>
</div>
{% endif %}
{% if entityRelation %}
<div class = "col-md-12">
<div class="panel panel-default">
<header class="panel-heading">
关系列表 :
</header>
<div class = "panel-body">
<table class = "table" data-paging = "true" data-sorting="true"></table>
</div>
</div>
</div>
{% endif %}
</div>
</div>
{% if entityRelation %}
<script src="/static/js/jquery.min.js"></script>
<script type="text/javascript">
// 将后端的查询结果使用echarts展示
var ctx = [ {{ ctx|safe }} ] ;
//{entity2,rel}
var entityRelation = [ {{ entityRelation|safe }} ] ;
console.log(entityRelation);
var data = [] ;
var links = [] ;
if(ctx.length == 0){
var node = {} ;
var url = decodeURI(location.search) ;
var str = "";
if(url.indexOf("?") != -1){
str = url.split("=")[1]
}
//实体1
node['name'] = str;
//alert(document.getElementById('user_text').value)
node['draggable'] = true ;
var id = 0;
node['id'] = id.toString() ;
data.push(node) ;
//获取实体2,存储在data中,如果实体2已经存在于data中,则不push
var maxDisPlayNode = 15 ;
for( var i = 0 ;i < Math.min(maxDisPlayNode,entityRelation[0].length) ; i++ ){
node = {} ;
node['name'] = entityRelation[0][i]['entity2']['title'] ;
// alert( entityRelation[0][i]['entity1']['title']);
node['draggable'] = true ;//一个可拖动的段落:
if('url' in entityRelation[0][i]['entity2']){
node['category'] = 1 ;
}
else{
node['category'] = 2 ;
}
id = i + 1
node['id'] = id.toString();
var flag = 1 ;
relationTarget = id.toString() ;
for(var j = 0 ; j<data.length ;j++){
if(data[j]['name'] === node['name']){
flag = 0 ;
relationTarget = data[j]['id'] ;
break ;
}
}
relation = {}
relation['source'] = data[0]['id'] ;
relation['target'] = relationTarget ;
relation['category'] = 0 ;
if(flag === 1){ //如果没有相同的节点,则push relation和node
data.push(node) ;
relation['value'] = entityRelation[0][i]['rel']['type'] ;
relation['symbolSize'] = 10
links.push(relation) ;
}
else{
//如果有相同的节点,则只push relation,不push node ,即不创建新的节点
relation['value'] = entityRelation[0][i]['rel']['type'] ;
relation['symbolSize'] = 10
links.push(relation) ;
}
//判断是否有下级,即,entityRelation[0][i]['nextRelation']中是否有数据,有的话循环添加
var maxDisPlayNode = 15 ;
for( var j = 0 ;j < Math.min(maxDisPlayNode,entityRelation[0][i]['nextRelation'].length) ; j++ ){
var flag = i;
node = {} ;
node['name'] = entityRelation[0][i]['nextRelation'][j]['entity2']['title'] ;
// alert( entityRelation[0][i]['nextRelation']['entity1']['title']);
node['draggable'] = true ;//一个可拖动的段落:
node['category'] = 3 ;
id = 15*(i+1)+j+1 ;
node['id'] = id.toString();
var flag = 1 ;
relationTarget = id.toString() ;
for(var k = 0 ; k<data.length ;k++){
if(data[k]['name'] === node['name']){
flag = 0 ;
relationTarget = data[k]['id'] ;
break ;
}
}
relation = {} ;
var sourceId = i+1 ;
sourceId = sourceId.toString();
relation['source'] = sourceId ;
relation['target'] = relationTarget ;
relation['category'] = 1 ;
if(flag === 1){ //如果没有相同的节点,则push relation和node
data.push(node) ;
relation['value'] = entityRelation[0][i]['nextRelation'][j]['rel']['type'] ;
relation['symbolSize'] = 10
links.push(relation) ;
}
else{
//如果有相同的节点,则只push relation,不push node ,即不创建新的节点
relation['value'] = entityRelation[0][i]['nextRelation'][j]['rel']['type'] ;
relation['symbolSize'] = 10;
links.push(relation) ;
}
}
}
}
console.log(data);
console.log(links);
// 基于准备好的dom,初始化echarts实例
var myChart = echarts.init(document.getElementById('graph'));
option = {
title: {
text: ''
},//标题
tooltip: {},//提示框配置
animationDurationUpdate: 1500,
animationEasingUpdate: 'quinticInOut',
label: {//节点上的标签
normal: {
show: true,
textStyle: {
fontSize: 12
},
}
},
legend: {
x: "center",
show: false
},
series: [
{
type: 'graph',
layout: 'force',
symbolSize: 60,
focusNodeAdjacency: true,
roam: true,
edgeSymbol: ['none', 'arrow'],
categories: [{
name: '查询实体',
itemStyle: {
normal: {
color: "#4592FF",
}
}
}, {
name: 'HudongItem',
itemStyle: {
normal: {
color: "#C71585",
}
}
}, {
name: 'NewNode',
itemStyle: {
normal: {
color: "#C71585",
}
}
},
{
name: 'NewNode',
itemStyle: {
normal: {
color: "#4592FF",
}
}
}],
label: {
normal: {
show: true,
textStyle: {
fontSize: 12,
},
}
},
force: {
repulsion: 800
},
edgeSymbolSize: [4, 50],
edgeLabel: {
normal: {
show: true,
textStyle: {
fontSize: 10
},
formatter: "{c}"
}
},
data : data,//节点
links: links,//节点间的关系
lineStyle: {//连接线的风格
normal: {
opacity: 0.9,
width: 1.3,
curveness: 0,
color:"#262626"
}
}
}
]
};
// 使用刚指定的配置项和数据显示图表。
myChart.setOption(option);
//用表格列出所有的关系
tableData = []
for (var i = 0 ; i < entityRelation[0].length ; i++){//想要得到某一行有多少列 可以写arr[0].length ,
relationData = {} ;
relationData['entity1'] = str ;
relationData['relation'] = entityRelation[0][i]['rel']['type'] ;
relationData['entity2'] = entityRelation[0][i]['entity2']['title'] ;
tableData.push(relationData) ;
for (var j = 0 ; j < entityRelation[0][i]['nextRelation'].length ; j++){
relationData = {} ;
relationData['entity1'] = entityRelation[0][i]['entity2']['title'] ;
relationData['relation'] = entityRelation[0][i]['nextRelation'][j]['rel']['type'] ;
relationData['entity2'] = entityRelation[0][i]['nextRelation'][j]['entity2']['title'] ;
tableData.push(relationData) ;
}
}
jQuery(function(){
$('.table').footable({
"columns": [{"name":"entity1",title:"Entity1"} ,
{"name":"relation",title:"Relation"},
{"name":"entity2",title:"Entity2"}],
"rows": tableData
});
});
</script>
{% endif %}
{% endblock %}
下图是打印出来的data的内容,可以看到data中主要存储各个节点的名字,id(很重要,并且必须是字符串,不能是数字,否则会出现错乱)
下图是打印出来的links的内容,可以看到links中主要存储两个节点之间的关系,source就是源节点的id, target是目标节点的id(再强调一遍,id为字符串)
3. 结果展示
在实体查询界面输入要查询的 ”实体“,查询结果如图所示: