Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Network Visualization Using Cypher and D3.JS

DZone's Guide to

Network Visualization Using Cypher and D3.JS

· Database Zone
Free Resource

Running out of memory? Learn how Redis Enterprise enables large dataset analysis with the highest throughput and lowest latency while reducing costs over 75%! 

We’ve seen some pretty nice visualizations of nodes and their immediate neighbors, but we want to be able to visualize more. So we’re going to prepare a 200 node network, use Cypher to extract the data we want and visualize it with D3.js.

I’m not going to type in the names of 200 people, so we’ll create a method to generate some random text.

def generate_text(length=8)
  chars = 'abcdefghjkmnpqrstuvwxyz'
  name = ''
  length.times { |i| name << chars[rand(chars.length)] }
  name
end

We are once again going to use our the Batch command. We are creating 200 nodes with random names, and randomly connecting them to 0-9 other nodes. This is going to send between 500 and 1000 commands in one batched transaction. This is as big as we want to go for one batch command, if you wanted to create a bigger graph, breaking it up into multiple batches of 1000 commands would be best.

def create_graph
  neo = Neography::Rest.new
  graph_exists = neo.get_node_properties(1)
  return if graph_exists && graph_exists['name']

  names = 200.times.collect{|x| generate_text}

  commands = names.map{ |n| [:create_node, {"name" => n}]}
  names.each_index do |x| 
    follows = names.size.times.map{|y| y}
    follows.delete_at(x)
    follows.sample(rand(10)).each do |f|
      commands << [:create_relationship, "follows", "{#{x}}", "{#{f}}"]    
    end
  end

  batch_result = neo.batch *commands
end

I want this visualization to be a little more dynamic than the last one, so when you take a look you’ll randomly see the network of one of the nodes. With Cypher we’ll parametrize the node and pass it a random number to serve as our node_id. If you’ve been following along you’ll notice this query looks pretty similar to when we used Cypher to get friends of friends. We are just taking it up a notch by going to friends of friends of friends and also getting the count of others.

def follower_matrix
  neo = Neography::Rest.new
  cypher_query =  " START me = node({node_id})"
  cypher_query << " MATCH (me)-[?:follows]->(friends)-[?:follows]->(fof)-[?:follows]->(fofof)-[?:follows]->others"
  cypher_query << " RETURN me.name, friends.name, fof.name, fofof.name, count(others)"
  cypher_query << " ORDER BY friends.name, fof.name, fofof.name, count(others) DESC"
  neo.execute_query(cypher_query, {:node_id => 1 + rand(200)})["data"]
end  

When we get our data it is going to be an array of arrays with the count of others at the end. You are basically looking at paths without the relationships.

[["Max", "Ben", "Rob", "James", 5],
 ["Max", "Ben", "Rob", "Peter", 2],
 ["Max", "Ben", "Musannif", "Pramod", 2]
]

Our visualization is looking for a JSON object that looks more like this:

{"name":"Max",
 "children":[{"name":"Ben",
               "children":[{"name":"Rob",
                            "children":[{"name":"James",
                                         "size":2},
                                        {"name":"Peter",
                                         "size":5}
                                       ]},
                           {"name":"Musannif",
                            "children":[{"name":"Pramod",
                                         "size":2}
                                       ]}
                           ]},
            ]
}

We will do this in two steps. The first is to create a variable called data and fill it with a nested hash of our array of arrays.

get '/followers' do
  data = follower_matrix.inject({}) {|h,i| t = h; i.each {|n| t[n] ||= {}; t = t[n]}; h}
  with_children(data).to_json
end

 We could have created a nice Nested Hash class, or used one of the existing gems that deal with Trees, but Ruby is nice enough to let us do it on one line. Don’t let that one liner freak you out, it is an just an old Ruby trick.

We need a way to turn this nested hash into the final format with children arrays and such, so we’ll write that method as follows:

def with_children(node)
  if node[node.keys.first].keys.first.is_a?(Integer)
    { "name" => node.keys.first,
      "size" => 1 + node[node.keys.first].keys.first 
     }
  else
    { "name" => node.keys.first, 
      "children" => node[node.keys.first].collect { |c| 
        with_children(Hash[c[0], c[1]]) } 
    }
  end
end

 

We are going to use the Size variable to figure out how big to make our leaf nodes. We start with 1 and add the count of others, because if there are zero others, our leaf node would have a zero size and we don’t want that.

Our visualization is about 120 lines long, so I won’t dump it all here. If you want to see it check it out on github.

It follows the D3 force collapsible example, but adds a little color using the size attribute so it won’t look so bland:

var colorscale = d3.scale.category10();

function color(d) {
  return d._children ? "#3182bd" : d.children ? "#c6dbef" : colorscale(d.size);
}

 ee it live on Heroku at http://evening-mountain-5731.herokuapp.com/index.html and make sure to refresh the page to see different results.



Source: http://maxdemarzi.com/2012/02/13/visualizing-a-network-with-cypher/

Running out of memory? Never run out of memory with Redis Enterprise databaseStart your free trial today.

Topics:

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}