Neo4j, Facebook graph, php and d3.js Visualizing your social graph.

Neo4j, Facebook graph, php and d3.js Visualizing your social graph.

  • @Tsartsaris

    Posted on 2013-10-21

    ​Dealing with data in Neo4j is great. We can make a call to the Facebook graph API, retrieve several info and store them in our graph in "0.1". But there are times we need to visualize this data giving some visual feedback to the user. I will try to give you some food for thought about visualizing data with d3.js. In the end you will be able to achieve something like the picture below.

    Image titlehttp://tsartsaris.gr/visgraph.html

    The first thing we are going to do is to retrieve some data from facebook. I am the user node for which we are going to retrieve facebook friends info.
    You will need access tokens to communicate with facebook api through php.

    Let's make the call.

    $friends=array();  //an array to keep the data
    $graph_url = "https://graph.facebook.com/me/friends?access_token=" . $access_token; //the call to facebook graph API
    $myfriends = json_decode(file_get_contents($graph_url)); //get the answer
    foreach($myfriends as $row){
    foreach($row as $info){
    if(isset($info->name)&&isset($info->id)){ //in case something goes wrong
    $friendname = $info->name;  //get friend name
            $friendid = $info->id;  //get friend fb id
    $friends[$friendname] = $friendid;  //key=>value pairs of names and facebook ids
    }}}

    Next step is to put the array in our Neo4j database. Since we have facebook ids we can construct a url for the profile picture and a url to link to the profile.We are going to use them later on for the visualization.

    As I told you before I am the user, call me at this moment the $master_user, I am a node in the database with an id let's say "1047".

    $user_id = 1047;  //the id of the node
    $client = new Everyman\Neo4j\Client('localhost', 7474);
    $master_user=$client->getNode($user_id); //the node by id in php

    We will put the data in neo4j by iterating the array and creating each time a node and setting some properties to the node.  

    foreach($friends as $key=>$uid2){
    $friend_node = $client->makeNode(); //create the node
    $friend_node->setProperty('facebook_uid', $uid2) //set properties
    ->setProperty('name',$key)
            ->setProperty('imgurl','https://graph.facebook.com/'.$uid2.'/picture')
            ->setProperty('url','http://www.facebook.com/'.$uid2)
    ->save();
    $master_user->relateTo($friend_node, 'facebook_friend') //create the relationship between the master user and his friends.
            ->save();
    }

    At this point we have all of our friends as nodes in the neo4j databse and a relationship created that indicates they are facebook friends.
    To make our visualization we are going to use d3.js and we are going to work with the force directed graph
    What we need is a json file with our nodes and links.
    The json file looks like this

        {
            "nodes":[
                {"name":"Myriel","group":1}, //explanation
                {"name":"Napoleon","group":1}
                    ],
            "links":[
                {"source":1,"target":0,"value":1}, //explanation
                {"source":2,"target":0,"value":8}
                    ]
        }

    In order to create such a json with our data in php we will have to make two arrays. The first one will have our nodes and the second our links from node to node(relationships "facebook_friend") we created in neo4j.
    Those 2 arrays, we are going to merge in a new one setting key values of "nodes" and "links" respectively.

    $dataset = array(); //to merge the 2 arrays later
    $fbnodes = array();  //to put all our friends nodes
    $single_node = array(); //to keep the properties for each single node
    $single_node['name']='Tsartsaris';  //creating the root node record
    $single_node['imgurl']='https://graph.facebook.com/100004988613905/picture';  //giving the image url property
    $single_node['group']=1; //defining the group
    array_push($fbnodes,$single_node);  //put it in our nodes

    for our json file we have the first line of our nodes 'root node' and it looks like this.

        { "nodes":[ { "name":"Tsartsaris", "imgurl":"https://graph.facebook.com/100004988613905/picture", "group":1 } ] }
    $links = array();  //to store our relationships
    $single_link = array();  //to have the properties of each link/relationship
    $single_link['source']=0;  //this is for the root node again
    $single_link['target']=0;  //pointing to itself
    $single_link['value']=10;  //a value for the link
    array_push($links,$single_link);  //adding the link to links
    $i=1;  //instantiate a counter for the friends nodes

    for our json file we have created the first line of our links. At this point the visualization has a node pointing back to itself.

           {
            "nodes":[
           {
            "name":"Tsartsaris",
            "imgurl":"https://graph.facebook.com/100004988613905/picture",
            "group":1
            }
            ],
            "links":[
            {
            "source":0,
            "target":0,
            "value":10
            }
            ]
            }
    $client = new Everyman\Neo4j\Client('tsartsaris.gr', 7474);
    $queryString = "START n=node(1047) ".
        "MATCH (n)-[:facebook_friend]->(x)".
        "RETURN n,x";
    $query = new Everyman\Neo4j\Cypher\Query($client, $queryString);
    $result = $query->getResultSet();

    with the cypher query above we will take all nodes connected to the master user with the relationship "facebook_friend" we created earlier.

    foreach ($result as $row) {  //iterate the result set
        $node_name=$row['x']->getProperty('name'); //get node name
        $node_image=$row['x']->getProperty('imgurl');  //get node profile image url
        $node_url=$row['x']->getProperty('url'); //get node profile link url
        $single_node['name']=$node_name;  //put them in the array
        $single_node['imgurl']=$node_image;
        $single_node['url']=$node_url;
        $single_node['group']=1;  //same group as root node
        array_push($fbnodes,$single_node); //add the array to $fbnodes
        $single_link['source']=$i; //the source node from the counter
        $single_link['target']=0; //pointing back to the root node
        $single_link['value']=10+$i;  //trick to make the visualization a little bit more interesting, play with this a bit yourself
        $i=$i+1;  //increase the counter by one to be used for the next node
        array_push($links,$single_link);  //put the single link array to the links array
                                }
    $dataset['nodes']=$fbnodes;  //putting the nodes array in the dataset with key value "nodes"
    $dataset['links']=$links;  //putting the links array in the dataset with key value of "links"
    $json = json_encode($dataset, JSON_PRETTY_PRINT); //create the json file, to use the pretty print you have to use version>php5.4
    $json =  str_replace('\\/', '/',$json); //make it readable for the links
    $json =  str_replace(' ', '',$json); //remove spaces, this is critical or else the d3.js library will not be able to read the links, I don't know why.
    $fp = fopen('results.json', 'w');
    fwrite($fp, $json);
    fclose($fp); //save the json file.

    Lets take a look at our d3.js file.It should look like this.

        <!DOCTYPE html>
        <meta charset="utf-8">
        <script src="http://d3js.org/d3.v3.min.js"></script>
        <script src="js/glow.js"></script>
        <style>

        .link {
          stroke: #ccc;
        }

        .node text {
          pointer-events: none;
          font: 10px sans-serif;
        }

        </style>
        <body>
        <script>
        var width = 1680,
            height = 1020

        var svg = d3.select("body").append("svg")
            .attr("width", width)
            .attr("height", height);

        var force = d3.layout.force()
            .gravity(.1)
            .distance(150)
            .charge(-500)
            .size([width, height]);

        d3.json("results.json", function(json) {   //load the json file
          force
              .nodes(json.nodes)
              .links(json.links)
              .start();

        d3.select("body").transition()
            .style("background-color", "black");

          var link = svg.selectAll(".link")
              .data(json.links)
            .enter().append("line")
              .attr("class", "link");

          var node = svg.selectAll(".node")
              .data(json.nodes)
            .enter().append("g")
              .attr("class", "node")
              .call(force.drag);    //in order to work the on click function on the image think of it like giving a href to a img tag in pure html.

        node.append("image:a")   //we append images to the nodes
          .attr("xlink:href", function(d){return d.url;})  //giving profile links on click
          .append("image")
              .attr("xlink:href", function(d) { return d.imgurl; })  //retrieving the images from image links
              .attr("x", -8)
              .attr("y", -8)
              .attr("width", 34)
              .attr("height", 34);

          node.append("text")  //make friends name visible
              .attr("dx", 18)
              .attr("dy", ".35em")
              .attr("fill", "#fff")
              .text(function(d) { return d.name }); //get the names from the json file

          force.on("tick", function() {
            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 + ")"; });
          });
        });
        </script>

Tag-cloud

webNeo4Jphpd3jsubuntuworkcypherinternetbootstrapdevelopmentflaskpython

Social Me!

Twitter Logo LinkedIn Logo Google+ Logo Tumblr Logo