Skip to content

Instantly share code, notes, and snippets.

@itamarhaber
Last active March 29, 2019 02:26
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save itamarhaber/e9a87c39467075e2ba76 to your computer and use it in GitHub Desktop.
Save itamarhaber/e9a87c39467075e2ba76 to your computer and use it in GitHub Desktop.
Red is Beautiful: A Visualization of Redis Commands
<!DOCTYPE html>
<!--
Red is Beautiful: A Visualization of Redis Commands
By Itamar Haber, Redis Labs
Adopted from Mike Bostock's Zoomable Pack Layout example: http://mbostock.github.io/d3/talk/20111116/pack-hierarchy.html
!-->
<html>
<head>
<style>
.node {
cursor: pointer;
}
.node:hover {
stroke: #fff;
stroke-width: 10px;
}
.node--leaf:hover {
stroke: hsl(7,83%,39%);
stroke-width: 5px;
}
.label {
font: 14px Arial, sans-serif;
font-weight: 700;
text-anchor: middle;
fill: white;
text-shadow: 1px 0 9px hsl(7,83%,16%), 1px 0 9px hsl(7,83%,16%), 1px 0 9px hsl(7,83%,16%), 1px 0 9px hsl(7,83%,16%);
cursor: pointer;
stroke: black 10px;
}
label,
.node--root {
pointer-events: none;
}
</style>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
</head>
<body>
<script>
var margin = 10,
diameter = 625;
var color = d3.scale.ordinal()
.domain([-1, 0, 1, 2])
.range(["hsl(100,100%,100%)", "hsl(7,83%,39%)", "hsl(100,100%,100%)", "hsl(7,83%,39%)"]);
var pack = d3.layout.pack()
.padding(5)
.size([diameter - margin, diameter - margin])
.value(function(d) { return d.size; })
var svg = d3.select("body").append("svg")
.attr("width", diameter)
.attr("height", diameter)
.append("g")
.attr("transform", "translate(" + diameter / 2 + "," + diameter / 2 + ")");
d3.json("https://cdn.rawgit.com/antirez/redis-doc/master/commands.json", function(error, root) {
if (error) return console.error(error);
// transform Redis commands.json to an hierarchical structure based on group
var bygroup = [];
for (var i in root) {
group = root[i]["group"].toUpperCase().replace("_"," ");
if(bygroup[group] == null) {
bygroup[group] = [];
}
// naively set size based on command complexity
complexity = String(root[i]["complexity"]).split(/[ .]/)[0];
switch(complexity) {
case "O(1)":
size = 1;
break;
case "O(log(N))":
size = 2;
case "O(log(N)+M)":
size = 4;
case "O(N)":
size = 5;
break;
case "O(N+M)":
case "O(S+N)":
size = 8;
break;
case "O(N+M*log(M))":
size = 9;
break;
case "O(M*log(N))":
size = 10;
case "O(N*M)":
size = 16;
break;
case "O(N)+O(M": // O(N)+O(M log(M))?
case "O(N)+O(M*log(M))":
complexity = "O(N)+O(M*log(M))";
size = 20;
break;
case "O(N*K)+O(M*log(M))":
size = 32;
break;
default:
complexity = "";
size = 0.5;
}
bygroup[group].push({ "name": i, "size": size, "complexity": complexity });
}
// build a JSON from transformed data
var groot = { "name": "Redis Commands", "children": [] };
for (var i in bygroup) {
t = [];
for (var j in bygroup[i]) {
t.push({ "name": bygroup[i][j]["name"], "size": bygroup[i][j]["size"], "complexity": bygroup[i][j]["complexity"] });
}
groot["children"].push({ "name": i, "children": t });
}
var focus = groot,
nodes = pack.nodes(groot),
view;
var circle = svg.selectAll("circle")
.data(nodes)
.enter().append("circle")
.attr("class", function(d) { return d.parent ? d.children ? "node" : "node node--leaf" : "node node--root"; })
.style("fill", function(d) { return color(d.depth); })
.on("click", function(d) { if (focus !== d && d.children)
zoom(d);
else if (!d.children)
if (focus === d.parent)
window.open("http://redis.io/commands/" + d.name.replace(" ", "-"), "_blank");
else
zoom(d.parent);
d3.event.stopPropagation();});
var text = svg.selectAll("text")
.data(nodes)
.enter().append("text")
.attr("class", "label")
.style("fill-opacity", function(d) { return d.parent === groot ? 1 : 0; })
.style("display", function(d) { return d.parent === groot ? null : "none"; })
.on("click", function(d) { if (focus !== d && d.children)
zoom(d);
else if (!d.children)
if (focus === d.parent)
window.open("http://redis.io/commands/" + d.name.replace(" ", "-"), "_blank");
else
zoom(d.parent);
d3.event.stopPropagation();});
var names = text.append("tspan")
.text(function (d) { return d.name; });
var complexities = text.append("tspan")
.attr("x", "0")
.attr("y", "1.4em")
.text(function (d) { return (d.complexity != "" && d.complexity) ? d.complexity : ""; });
var node = svg.selectAll("circle,text");
d3.select("body")
.style("background", color(-1))
.on("click", function() { zoom(groot); });
zoomTo([groot.x, groot.y, groot.r * 2 + margin]);
function zoom(d) {
var focus0 = focus; focus = d;
var transition = d3.transition()
.duration(d3.event.altKey ? 7500 : 750)
.tween("zoom", function(d) {
var i = d3.interpolateZoom(view, [focus.x, focus.y, focus.r * 2 + margin]);
return function(t) { zoomTo(i(t)); };
});
transition.selectAll("text")
.filter(function(d) { return d.parent === focus || this.style.display === "inline"; })
.style("fill-opacity", function(d) { return d.parent === focus ? 1 : 0; })
.each("start", function(d) { if (d.parent === focus) this.style.display = "inline"; })
.each("end", function(d) { if (d.parent !== focus) this.style.display = "none"; });
}
function zoomTo(v) {
var k = diameter / v[2]; view = v;
node.attr("transform", function(d) { return "translate(" + (d.x - v[0]) * k + "," + (d.y - v[1]) * k + ")"; });
circle.attr("r", function(d) { return d.r * k; });
}
});
d3.select(self.frameElement).style("height", diameter + "px");
</script>
</body>
</html>
@itamarhaber
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment