Skip to content

Instantly share code, notes, and snippets.

@jexp
Last active August 30, 2018 22:45
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jexp/f4ae19be0e06d8a19867f294e09f0759 to your computer and use it in GitHub Desktop.
Save jexp/f4ae19be0e06d8a19867f294e09f0759 to your computer and use it in GitHub Desktop.
Charting Neo4j Cypher Results and Database Statistics
<!-- License MIT -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Neo4j Charts</title>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css"
integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap-theme.min.css"
integrity="sha384-fLW2N01lMqjakBkx3l/M9EahuwpSfeNvV63J5ezn3uZzapT0u7EYsXMjQV+0En5r" crossorigin="anonymous">
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"
integrity="sha384-0mSbJDEHialfmuBBQP6A4Qrprq5OVfW37PRR3j5ELqxss1yVqOtnepnHVP9aJ7xS"
crossorigin="anonymous"></script>
<script type="text/javascript"
src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.1.3/Chart.bundle.min.js"></script>
<script src="https://cdn.rawgit.com/neo4j/neo4j-javascript-driver/1.1/lib/browser/neo4j-web.min.js"></script>
<script type="text/javascript">
var samples = 10;
var statsChart, queryChart;
function newStatsChart() {
// tag::chart-setup[]
return new Chart($("#statsChart"), {
label: 'Neo4j Database Statistics',
type: 'line', height: 1000, width: 800,
fill: false, lineTension: 0, showLines: true,
data: { labels: [], datasets: [] },
options: {
scales: {
xAxes: [{
type: 'time', time: {unit: 'second', displayFormats: {second: "HH:mm:ss"}},
ticks: { callback: function (label, index) { return index % 2 == 0 ? label : "" } }
}],
yAxes: [{
type: 'logarithmic', min: 0,
ticks: { callback: function (label) { return label; } } // no scientific notation
}]
}
}
});
// end::chart-setup[]
}
$(document).ready(function () {
statsChart = newStatsChart();
statsChart.update();
queryChart = new Chart($("#queryChart"), {
type: 'line', label: 'Query Results', fill: false, lineTension: 0, showLines: true,
data: {labels: [], datasets: []}
});
queryChart.update();
});
var running = undefined;
function toggle() {
if (!running) {
running = setInterval(stats, parseInt($("#refresh").val()) * 1000);
stats();
stats();
$("#stats").text("Stats: Stop");
} else {
clearInterval(running);
running = undefined;
$("#stats").text("Stats: Start");
}
}
// tag::stats-query[]
function stats() {
var neo = neo4j.v1;
var driver = neo.driver($("#url").val(), neo.auth.basic("neo4j", $("#password").val()));
var session = driver.session();
var session2 = driver.session();
var d = Date.now();
session2.run("MATCH () RETURN count(*)").then(function (result) {
update(d, "nodes", result.records[0]._fields[0].toNumber());
});
session2.run("MATCH ()-->() RETURN count(*)").then(function (result) {
update(d, "rels", result.records[0]._fields[0].toNumber());
});
session.run("CALL db.labels()").then(function (result) {
result.records.forEach(function (r) {
var l = r._fields[0];
var stmt = "MATCH (:`" + l + "`) RETURN count(*)";
session2.run(stmt).then(function (result2) {
update(d, l, result2.records[0]._fields[0].toNumber());
});
});
});
session.run("CALL db.relationshipTypes()").then(function (result) {
result.records.forEach(function (r) {
var l = r._fields[0];
var stmt = "MATCH ()-[:`" + l + "`]->() RETURN count(*)";
session2.run(stmt).then(function (result2) {
update(d, l, result2.records[0]._fields[0].toNumber());
});
});
});
}
// end::stats-query[]
// tag::chart-update[]
// from http://colorbrewer2.org/ via http://www.zingchart.com/blog/2015/12/09/color-charts/
var colors = ['#d53e4f', '#f46d43', '#fdae61', '#fee08b', '#ffffbf', '#e6f598', '#abdda4', '#66c2a5', '#3288bd'];
function update(time, label, value) {
var labels = statsChart.data.labels;
var datasets = statsChart.data.datasets;
var title = "# of " + label;
var insert = labels.indexOf(time);
// new timestamp
if (insert == -1) {
// too many samples, remove first entry
if (labels.length > samples) {
labels.shift();
datasets.forEach(function (ds) { ds.data.shift(); });
}
insert = labels.length;
labels[insert] = time;
}
// find dataset
var idx = datasets.findIndex(function(ds) { return ds.label == title});
// add new dataset
if (idx == -1) {
idx = datasets.length;
datasets.push({ label: title, data: labels.map(function(){return 1;}), borderColor: colors[idx % colors.length], fill: false});
}
datasets[idx].data[insert] = value;
statsChart.update();
}
// end::chart-update[]
function query() {
var neo = neo4j.v1;
var driver = neo.driver($("#url").val(), neo.auth.basic("neo4j", $("#password").val()));
var session = driver.session();
session.run($("#query").val()).then(function (result) {
var data = queryChart.data;
var first = true;
data.labels = [];
result.records.forEach(function (r) {
if (data.labels.length == 0) {
queryChart.label = 'Query Results' + r.keys[0];
for (var i = 1; i < r.keys.length; i++) {
data.datasets[i - 1] = {
label: r.keys[i],
data: [],
fill: false,
borderColor: colors[i % colors.length]
};
}
}
var l = r._fields[0].toString();
data.labels.push(l);
for (var i = 1; i < r._fields.length; i++) {
var v = r._fields[i].toNumber ? r._fields[i].toNumber() : r._fields[i];
data.datasets[i - 1].data.push(v);
}
});
queryChart.update(1000, true);
session.close();
driver.close();
});
}
</script>
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar"
aria-expanded="false" aria-controls="navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Neo4j Charts</a>
</div>
<div id="navbar" class="navbar-collapse collapse">
<form class="navbar-form navbar-left">
<div class="form-group">
<input type="text" id="url" value="bolt://localhost" class="form-control">
</div>
<div class="form-group">
<input type="password" id="password" value="test" class="form-control">
</div>
<div class="form-group">
<input type="number" id="refresh" value="60" title="Refresh in seconds" class="form-control">
</div>
<button id="stats" class="btn btn-success" onclick="toggle();return false;">Stats: Start</button>
</form>
</div><!--/.navbar-collapse -->
</div>
</nav>
<div class="container">
<div class="row">
<div class="col-md-6">
<h2>Database Statistics</h2>
<canvas id="statsChart" height="400"></canvas>
</div>
<div class="col-md-6">
<h2>Render your own results</h2>
<canvas id="queryChart"></canvas>
<div class="row">
<div class="col-md-11">
<textarea id="query" cols="70" rows="4">MATCH (n)
RETURN labels(n), count(*)</textarea>
</div>
<div class="col-md-1">
<button class="btn btn-success" onClick="query()">Run</button>
</div>
</div>
</div>
</div>
</div>
<footer>
<p>Built using the <a target="_blank" href="http://github.com/neo4j/neo4j-javascript-driver">Neo4j Javascript
Driver</a> and <a target="_blank" href="http://chartjs.org">Chart.js</a>. <a
href="https://gist.github.com/jexp/f4ae19be0e06d8a19867f294e09f0759" target="_blank">Source Code on
GitHub</a></p>
</footer>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment