var client = new elasticsearch.Client(); var indx = "mh370-2"; function clientSearch(qstr, index) { client.search({ index: indx, body: eval(qstr) }).then(function (response) { var hits = response.aggregations[qstr]; drawNodes(qstr, hits); }, function (error) { console.log(error.message); }); } function drawNodes (qstr, data) { //console.log(qstr, data); var width = 960, height = 500, root; var force = d3.layout.force() .linkDistance(50) .charge(-150) .gravity(.02) .size([width, height]) .on("tick", tick); var svg = d3.select("body").append("svg") .attr("width", width) .attr("height", height); // add zoom behavior var currentScale = 1.0; var zoom = d3.behavior.zoom() .scaleExtent([0.5,2.5]) .on("zoom", redraw); var zoomPanel = svg .attr('id', 'zoomPanel') .call(zoom) .style("fill", "#bbb") .append('svg:g'); function redraw() { currentScale = d3.event.scale; // scale must be updated to be synced with the buttons' zoom level zoomPanel.attr("transform", "translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")"); } var link = zoomPanel.selectAll(".link"), node = zoomPanel.selectAll(".node"); var radScale = d3.scale.linear().nice() .domain([1, 160]) .range([3, 35]); // flatten and parse the elasticsearch data root = parseData(data); update(); function update() { console.log('ROOT', root); var tree = d3.layout.tree(), nodes = flatten(root), links = tree.links(nodes); console.log('NODES', nodes.length, nodes[0]); console.log('LINKS', links.length, links[0]); // restart the force layout force .nodes(nodes) .links(links) .start(); // for dragging bkd and zoom behavior var drag = force.drag() .on("dragstart", dragstarted) .on("drag", dragged) .on("dragend", dragended); // update the links link = link.data(links, function(d) { return d.target.id; }); // exit any old links link.exit().remove(); // enter any new links link.enter().insert("line", ".node") .attr("class", "link") .style("stroke-width",0.5) .style("stroke-opacity",0.5) .style("stroke","#6d9cc4") .attr("x1", function(d) { return d.source.x; }) .attr("y1", function(d) { return d.source.y; }) .attr("x2", function(d) { return d.target.x; }) .attr("y2", function(d) { return d.target.y; }); // update the nodes node = node.data(nodes, function(d) { return d.id; }); // exit any old nodes node.exit().remove(); // enter any new nodes var nodeEnter = node.enter().append("g") .attr("class", "node") .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; }) .on("click", click) .call(drag); nodeEnter.append("circle") .attr("r", function(d) { var rr = Math.sqrt(d.doc_count); return radScale(rr) || 4; }); nodeEnter.append("text") .attr("dy", "0.35em") .attr("dx", function(d) { var rr = radScale(Math.sqrt(d.doc_count)) + 2; return rr || 6; }) .text(function(d){ var num = ''; if (d.doc_count) { num = ': ' + addCommas(d.doc_count); } return d.key + num; }) .style("fill", "#999") .style("font-size", "6px") .style("text-anchor", "start") node.select("circle") .style("stroke-width", 1) .style("stroke","#6d9cc4") .style("fill", color); } function tick() { link.attr("x1", function(d) { return d.source.x; }) .attr("y1", function(d) { return d.source.y; }) .attr("x2", function(d) { return d.target.x; }) .attr("y2", function(d) { return d.target.y; }); node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; }); } function color(d) { return d._children ? "#3182bd" : d.children ? "#c6dbef" : "#fd8d3c"; } // toggle children on click function click(d) { if (!d3.event.defaultPrevented) { if (d.children) { d._children = d.children; d.children = null; } else { d.children = d._children; d._children = null; } update(); } } function dragstarted(d) { d3.event.sourceEvent.stopPropagation(); } function dragged(d) { d3.event.sourceEvent.stopPropagation(); d3.select(this).classed("dragging", true); } function dragended(d) { var nd = this; //_.delay(function(){ // d3.select(nd).classed("dragging", false); //}, 300); } function parseData(dataObj) { var root = {}; root.key = "mh370"; root.children = dataObj.buckets; root.children.forEach(function (d) { d.children = d.n.buckets; }); return root; } function flatten(root) { var nodes = [], i = 0; function recurse(node) { if (node.children) node.children.forEach(recurse); if (!node.id) node.id = ++i; nodes.push(node); } recurse(root); return nodes; } function addCommas(nStr) { nStr += ''; x = nStr.split('.'); x1 = x[0]; x2 = x.length > 1 ? '.' + x[1] : ''; var rgx = /(\d+)(\d{3})/; while (rgx.test(x1)) { x1 = x1.replace(rgx, '$1' + ',' + '$2'); } return x1 + x2; } } // some aggregation searches var hashtags = { "aggs": { "hashtags": { "terms": { "field": "hashtag.text", "size": 100 } } } }; var hashtagsuser = { "aggs": { "hashtagsuser": { "terms": { "field": "hashtag.text", "size": 20 }, "aggs": { "n": { "terms": { "field": "user.screen_name", "size": 10 } } } } } }; var byminute = { "aggs": { "byminute": { "date_histogram": { "field": "created_at", "interval": "10m", "format": "yyyy-mm-dd hh:mm:ss" } } } }; clientSearch("hashtagsuser"); /* { "_index": "mh370-2", "_type": "status", "_id": "445219404327178240", "_score": 1, "_source": { "text": "RT @Maddad696: Logically #mh370Alive had to have been #hijacked , and a strong possibility #MH370 was diverted to #Africa I believe Al Qaed…", "created_at": "2014-03-16T15:25:52.000Z", "source": "Twitter for iPhone", "truncated": false, "language": "en", "mention": [ { "id": 22185930, "name": "Frank Belzil", "screen_name": "Maddad696", "start": 3, "end": 13 } ], "retweet_count": 0, "retweet": { "id": 445214773160849400, "user_id": 22185930, "user_screen_name": "Maddad696", "retweet_count": 1 }, "hashtag": [ { "text": "mh370Alive", "start": 25, "end": 36 }, { "text": "hijacked", "start": 54, "end": 63 }, { "text": "MH370", "start": 91, "end": 97 }, { "text": "Africa", "start": 114, "end": 121 } ], "link": [], "user": { "id": 337690411, "name": "Coração Tricolor", "screen_name": "Amilnyleve", "location": "Rio de Janeiro", "description": "I like sports, played tennis & football, not soccer (pretty good, they say) - Software engineering is my business.", "profile_image_url": "http://pbs.twimg.com/profile_images/1448491558/Foto-0007_normal.jpg", "profile_image_url_https": "https://pbs.twimg.com/profile_images/1448491558/Foto-0007_normal.jpg" } } } */