Как я и обещал в моем предыдущем посте , я хотел бы сделать кое — что на D3.js .
Мы собираемся взять один из их примеров визуализации и визуализировать следующий график.
Чтобы создать наш график, мы возьмем имена 20 человек: создадим для них узлы, добавим их в индекс и случайным образом свяжем их вместе.
Обратите внимание, что мы используем команду Neography Batch для одновременного создания всего графика.
01 def create_graph 02 neo = Neography::Rest.new 03 graph_exists = neo.get_node_properties(1) 04 return if graph_exists && graph_exists['name'] 05 06 names = %w[Max Agam Lester Musannif Adel Andrey Ryan James Bruce Tim Pinaki Mark Peter Anne Helene Corey Ben Rob Pramod Prasanna] 07 08 commands = names.map{ |n| [:create_node, {"name" => n}]} 09 names.each_index do |x| 10 commands << [:add_node_to_index, "nodes_index", "type", "User", "{#{x}}"] 11 follows = names.size.times.map{|y| y} 12 follows.delete_at(x) 13 follows.sample(1 + rand(5)).each do |f| 14 commands << [:create_relationship, "follows", "{#{x}}", "{#{f}}"] 15 end 16 end 17 18 batch_result = neo.batch *commands 19 end
Мы не будем делать ошибку, оставляя метод create_graph снова общедоступным, поэтому создадим для него задачу Rake.
1 require 'neography/tasks' 2 require './d3.rb' 3 4 namespace :neo4j do 5 task :create do 6 create_graph 7 end 8 end
Мы будем использовать Cypher для создания матрицы последователей, которую мы будем использовать для заполнения нашего скрипта D3.
1 def follower_matrix 2 neo = Neography::Rest.new 3 cypher_query = " START a = node:nodes_index(type='User')" 4 cypher_query << " MATCH a-[:follows]->b" 5 cypher_query << " RETURN a.name, collect(b.name)" 6 neo.execute_query(cypher_query)["data"] 7 end
Функция collect возвращает строку с массивом внутри нее, поэтому нам нужно немного перебрать строки, чтобы превратить ее в правильный массив, а затем преобразовать все в JSON.
1 get '/follows' do 2 follower_matrix.map{|fm| {"name" => fm[0], "follows" => fm[1][1..(fm[1].size - 2)].split(", ")} }.to_json 3 end
Наша функция D3 — небольшая вариация на примере аккордового аккорда в репозитории github D3:
01 var r1 = 960 / 2, 02 r0 = r1 - 120; 03 04 var fill = d3.scale.category20c(); 05 06 var chord = d3.layout.chord() 07 .padding(.04) 08 .sortSubgroups(d3.descending) 09 .sortChords(d3.descending); 10 11 var arc = d3.svg.arc() 12 .innerRadius(r0) 13 .outerRadius(r0 + 20); 14 15 var svg = d3.select("body").append("svg") 16 .attr("width", r1 * 2) 17 .attr("height", r1 * 2) 18 .append("g") 19 .attr("transform", "translate(" + r1 + "," + r1 + ")"); 20 21 d3.json("follows", function(follows) { 22 var indexByName = {}, 23 nameByIndex = {}, 24 matrix = [], 25 n = 0; 26 27 function name(name) { 28 return name 29 } 30 31 // Compute a unique index for each name. 32 follows.forEach(function(d) { 33 d = name(d.name); 34 if (!(d in indexByName)) { 35 nameByIndex[n] = d; 36 indexByName[d] = n++; 37 } 38 }); 39 40 // Construct a square matrix counting relationships. 41 follows.forEach(function(d) { 42 var source = indexByName[name(d.name)], 43 row = matrix[source]; 44 if (!row) { 45 row = matrix[source] = []; 46 for (var i = -1; ++i < n;) row[i] = 0; 47 } 48 d.follows.forEach(function(d) { row[indexByName[name(d)]]++; }); 49 }); 50 51 chord.matrix(matrix); 52 53 var g = svg.selectAll("g.group") 54 .data(chord.groups) 55 .enter().append("g") 56 .attr("class", "group"); 57 58 g.append("path") 59 .style("fill", function(d) { return fill(d.index); }) 60 .style("stroke", function(d) { return fill(d.index); }) 61 .attr("d", arc); 62 63 g.append("text") 64 .each(function(d) { d.angle = (d.startAngle + d.endAngle) / 2; }) 65 .attr("dy", ".35em") 66 .attr("text-anchor", function(d) { return d.angle > Math.PI ? "end" : null; }) 67 .attr("transform", function(d) { 68 return "rotate(" + (d.angle * 180 / Math.PI - 90) + ")" 69 + "translate(" + (r0 + 26) + ")" 70 + (d.angle > Math.PI ? "rotate(180)" : ""); 71 }) 72 .text(function(d) { return nameByIndex[d.index]; }); 73 74 svg.selectAll("path.chord") 75 .data(chord.chords) 76 .enter().append("path") 77 .attr("class", "chord") 78 .style("stroke", function(d) { return d3.rgb(fill(d.source.index)).darker(); }) 79 .style("fill", function(d) { return fill(d.source.index); }) 80 .attr("d", d3.svg.chord().radius(r0)); 81 82 });
Весь код доступен на Github .
Наконец, мы разместим все это на Heroku, как я уже показывал:
1 git clone [email protected]:maxdemarzi/d3_js_intro.git 2 cd d3_js_intro 3 bundle install 4 heroku create --stack cedar 5 heroku addons:add neo4j 6 git push heroku master 7 heroku run rake neo4j:create
Довольно, не правда ли?
Источник:
http://maxdemarzi.com/2012/02/02/graph-visualization-and-neo4j-part-three/