4
4
from yfiles_jupyter_graphs import GraphWidget
5
5
from IPython .core .magic import (register_line_magic ,
6
6
register_cell_magic )
7
-
7
+ from IPython .display import IFrame
8
+ import json
9
+ import uuid
10
+ import os
11
+ from py2neo import Graph
8
12
9
13
10
14
driver = GraphDatabase .driver (uri = "neo4j://localhost" )
11
15
16
+
17
+ def vis_network (nodes , edges , physics = True ):
18
+ html = """
19
+ <html>
20
+ <head>
21
+ <meta content="text/html;charset=utf-8" http-equiv="Content-Type">
22
+ <meta content="utf-8" http-equiv="encoding">
23
+ <script type="text/javascript" src="https://thedatasociety.github.io/resources/purl/lab-neo4j/graphdrawer/vis.js"></script>
24
+ <link type="text/css" href="https://thedatasociety.github.io/resources/purl/lab-neo4j/graphdrawer/vis.css" rel="stylesheet" >
25
+ </head>
26
+ <body>
27
+
28
+ <div id="{id}"></div>
29
+
30
+ <script type="text/javascript">
31
+ var nodes = {nodes};
32
+ var edges = {edges};
33
+
34
+ var container = document.getElementById("{id}");
35
+
36
+ var data = {{
37
+ nodes: nodes,
38
+ edges: edges
39
+ }};
40
+
41
+ var options = {{
42
+ nodes: {{
43
+ shape: 'dot',
44
+ size: 25,
45
+ font: {{
46
+ size: 14
47
+ }}
48
+ }},
49
+ edges: {{
50
+ font: {{
51
+ size: 14,
52
+ align: 'middle'
53
+ }},
54
+ color: 'gray',
55
+ arrows: {{
56
+ to: {{enabled: true, scaleFactor: 0.5}}
57
+ }},
58
+ smooth: {{enabled: false}}
59
+ }},
60
+ physics: {{
61
+ enabled: {physics}
62
+ }}
63
+ }};
64
+
65
+ var network = new vis.Network(container, data, options);
66
+
67
+ </script>
68
+ </body>
69
+ </html>
70
+ """
71
+
72
+ unique_id = str (uuid .uuid4 ())
73
+ html = html .format (id = unique_id , nodes = json .dumps (nodes ), edges = json .dumps (edges ), physics = json .dumps (physics ))
74
+
75
+ try :
76
+ os .makedirs ('graphs' )
77
+ except OSError as e :
78
+ pass
79
+
80
+ filename = "resources/graphs/graph-{}.html" .format (unique_id )
81
+
82
+ file = open (filename , "w+" )
83
+ file .write (html )
84
+ file .close ()
85
+
86
+ return IFrame (filename , width = "100%" , height = "450" )
87
+
88
+ def draw (graph , options , physics = False , limit = 300 ):
89
+ # The options argument should be a dictionary of node labels and property keys; it determines which property
90
+ # is displayed for the node label. For example, in the movie graph, options = {"Movie": "title", "Person": "name"}.
91
+ # Omitting a node label from the options dict will leave the node unlabeled in the visualization.
92
+ # Setting physics = True makes the nodes bounce around when you touch them!
93
+ query = """
94
+ MATCH (n)
95
+ WITH n, rand() AS random
96
+ ORDER BY random
97
+ LIMIT 300
98
+ OPTIONAL MATCH (n)-[r]->(m)
99
+ RETURN n AS source_node,
100
+ id(n) AS source_id,
101
+ r,
102
+ m AS target_node,
103
+ id(m) AS target_id
104
+ """
105
+
106
+ data = graph .run (query , limit = limit )
107
+
108
+ nodes = []
109
+ edges = []
110
+
111
+ def get_vis_info (node , id ):
112
+ node_label = list (node .labels )[0 ]
113
+ prop_key = options .get (node_label )
114
+ vis_label = node ['label' ]
115
+
116
+ if node ['label' ] == None :
117
+
118
+ return {
119
+ "id" : id ,
120
+ "label" : "\n {}" .format (node .labels ),
121
+ "group" : node_label , "title" : "Type(s) = {} <br/> Properties = " .format (node_label )+ repr (dict (node ))
122
+ }
123
+ else :
124
+
125
+ return {
126
+ "id" : id ,
127
+ "label" : "\n \n {} ({})" .format (node .labels ,node ['label' ]),
128
+ "group" : node_label , "title" : "Type(s) = {} <br/> Properties = " .format (node_label )+ repr (dict (node ))
129
+ }
130
+
131
+
132
+ for row in data :
133
+ source_node = row [0 ]
134
+ source_id = row [1 ]
135
+ rel = row [2 ]
136
+ target_node = row [3 ]
137
+ target_id = row [4 ]
138
+
139
+ source_info = get_vis_info (source_node , source_id )
140
+
141
+ if source_info not in nodes :
142
+ nodes .append (source_info )
143
+
144
+ if rel is not None :
145
+ target_info = get_vis_info (target_node , target_id )
146
+
147
+ if target_info not in nodes :
148
+ nodes .append (target_info )
149
+
150
+ edges .append ({"from" : source_info ["id" ], "to" : target_info ["id" ], "label" : "{}" .format (type (rel ).__name__ )})
151
+
152
+ return vis_network (nodes , edges , physics = physics )
153
+
12
154
def print_counters (counters ):
13
155
for attr in dir (counters ):
14
156
# Filter out private methods and attributes
@@ -35,11 +177,26 @@ def query_neo4j_output_table(cypher_query: str):
35
177
return df
36
178
37
179
180
+ def query_neo4j_output_only_counter (cypher_query : str ):
181
+ query_graph_result = driver .session ().run (cypher_query )
182
+ print_counters (query_graph_result .consume ().counters )
183
+
184
+
185
+
186
+
187
+
38
188
39
189
def cypher (line , cell_content ):
40
190
41
- if line == 'graph' :
191
+ if line == 'show returned graph' :
42
192
query_neo4j_output_graph (cell_content )
193
+ elif line == 'show full graph' :
194
+ if cell_content .replace (" " , "" ).lower ().strip () != 'display' :
195
+ query_neo4j_output_only_counter (cell_content )
196
+ graph = Graph ("bolt://127.0.0.1:7687" )
197
+ iframe = draw (graph , {}, physics = True )
198
+ display (iframe )
199
+
43
200
else :
44
201
query_neo4j_output_table (cell_content )
45
202
0 commit comments