Search code examples
javascriptcytoscape.jscytoscape

cytoscape.js - how to merge attributes of the same node from different sources


I have a working graph which displays some nodes & their attributes. Then I get a JSON with different data, where some nodes may already exist on my graph. How to combine both data sources, to make them both visible on the same graph - BUT nodes with the same ID must be combined into one and contain attributes from both data sources (not just from one, as is by default)?

Example:

Node from source 1 => "id": "1", "name": "1", "param1": 100;
Node from source 2 => "id": "1", "name": "1", "param2": 200;

What I wish to see on the graph is one node with attributes:

"id": "1", "name": "1", "param1": 100, "param2": 200

Solution

  • I'm in the middle of writing code in my own application to do exactly what you're asking. The code below works, though I suspect that it's not the most efficient way. So, please don't accept this answer without waiting at least a few days for someone more experienced to post a better answer or to add a comment criticizing this answer.

    The trick is to query cy (the cytoscape.js core object) for a "collection object" containing just the node with the given id, and then query the collection object to see if it's empty. If the node doesn't exist, you cy.add() it. If the node does exist, you call node.data() on the collection object to update it.

    function updateGraph(g) {  // g is the output from JSON.parse(), containing graph from server
      gg = g;  // save pointer to g, for console debugging
    
      // Import nodes from server graph
      for (const sn of g.nodes) {  // sn = node as represented on the server
        var node = cy.$id(sn.id)   // node = cytoscape.js's representation of node
        if (node.empty()) {
          node = cy.add({group: 'nodes', data: {
            id: sn.id,
            label: sn['display-name'],  // peculiar to my application
            parent: sn.memberOf         // peculiar to my application
            /* . . . and whatever other data you want to copy to the cytoscape.js graph . . . */
          }});
          node.addClass(sn.class);
        } else {
          /* Update `node` with data from `sn`.*/
          node.data( /* your overriding data goes here */ );
        }
      }
    }
    
    var gg; // We save the last graph dict from the server here so we can look at
            // it in the Chrome debugger even after updateGraph() returns.
    

    The gg variable isn't necessary, of course, but I've found it indispensible for seeing what's going on in the Chrome debugger.

    In your application, you might be able to call Object.assign() to merge the data before calling node.data(). That would be simpler and more efficient than my code above, where data from the source has different keys than the keys expected by cytoscape.js.