How do I changes node labels?

359 Views Asked by At

I have a little Neo graph:

match (f) optional match (f)-[r]-() delete f, r;
merge (DMSrc:DMSys { Org: 'UNK-1', System: 'UNK' }) merge (DMDst:DMSys { Organization: 'UNK-0', System: 'Peoplesoft' }) merge (f:DMFile { Name: 'BAL'}) merge (DMSrc)-[:Provides]->(f)-[:Receives]->(DMDst);
merge (DMSrc:DMSys { Org: 'UNK-2', System: 'Oracle GL' }) merge (DMDst:DMSys { Organization: 'UNK-0', System: 'Peoplesoft' }) merge (f:DMFile { Name: 'BAL'}) merge (DMSrc)-[:Provides]->(f)-[:Receives]->(DMDst);
merge (DMSrc:DMSys { Org: 'X&X', System: 'Classic' }) merge (DMDst:DMSys { Organization: 'UNK-0', System: 'Peoplesoft' }) merge (f:DMFile { Name: 'BAL'}) merge (DMSrc)-[:Provides]->(f)-[:Receives]->(DMDst);
merge (DMSrc:DMSys { Org: 'UNK-3', System: 'Classic' }) merge (DMDst:DMSys { Organization: 'UNK-0', System: 'Peoplesoft' }) merge (f:DMFile { Name: 'BAL'}) merge (DMSrc)-[:Provides]->(f)-[:Receives]->(DMDst);
merge (DMSrc:DMSys { Org: 'UNK-4', System: 'Sun System' }) merge (DMDst:DMSys { Organization: 'UNK-0', System: 'Peoplesoft' }) merge (f:DMFile { Name: 'BAL'}) merge (DMSrc)-[:Provides]->(f)-[:Receives]->(DMDst);
merge (DMSrc:DMSys { Org: 'UNK-5', System: 'Oracle GL' }) merge (DMDst:DMSys { Organization: 'UNK-0', System: 'Peoplesoft' }) merge (f:DMFile { Name: 'BAL'}) merge (DMSrc)-[:Provides]->(f)-[:Receives]->(DMDst);
merge (DMSrc:DMSys { Org: 'UNK-6', System: 'Oracle GL' }) merge (DMDst:DMSys { Organization: 'UNK-0', System: 'Peoplesoft' }) merge (f:DMFile { Name: 'BAL'}) merge (DMSrc)-[:Provides]->(f)-[:Receives]->(DMDst);
merge (DMSrc:DMSys { Org: 'UNK-7', System: 'Direct (No Interface)' }) merge (DMDst:DMSys { Organization: 'UNK-0', System: 'Peoplesoft' }) merge (f:DMFile { Name: 'BAL'}) merge (DMSrc)-[:Provides]->(f)-[:Receives]->(DMDst);

for which I'm trying to produce a graph where the nodes use the data property (node attribute in Neo) 'System', but I can't get it work. here's the code:

var neo = {
    url: 'http://localhost:7474',
    user: 'neo4j',
    password: '***'
};      
sigma.neo4j.cypher(neo,
    'MATCH (n) OPTIONAL MATCH (n)-[r]->(m) RETURN n,r,m LIMIT 100',
    { container: 'graph' },
    function (s) { 
        sigma.plugins.killDesign(s);
        var design = sigma.plugins.design(s);
        design.setStyles({
            nodes: {
                label: { by: 'neo4j_data.System' }
            }   
        });         
        design.apply();
    }       
);

you'll note that unlike the sample references in the documentation which data.something, I use neo4j_data.something as I've found from debugging that for graphs retrieved from Neo4j, that's where node attributes seem to reside.

apparently the place where this data should be accessed is in the applyStyle method defined in sigma.plugins.design.js, specifically line 534, which I quote below:

if (!(visualVar in self.originalVisualVariable[item.id])) {
  // non-writable property
  Object.defineProperty(self.originalVisualVariable[item.id], visualVar, {
   enumerable: true,
   value: item[visualVar]
  });
}

where it may be seen that the value of the label (visualVar is currently set to "label") gets set to item[visualVar]... however item contains:

{
Object
cam0:size: 8
cam0:x: 656.8832126805254
cam0:y: 191.29239469613498
color: "#000000"
id: "0"
label: "0"
neo4j_data: {
    Org: "UNK-1"
    System: "UNK"
}
neo4j_labels: Array[1]
read_cam0:size: 8
read_cam0:x: 61.38321268052541
read_cam0:y: 91.79239469613499
size: 1
x: 0.8536414597183466
y: 0.980357211548835
__proto__: Object
}

from which can be seen that though there is an attribute item.label, what it should really do is retrieve item.neo4j_data.System.

am I misunderstanding this, or how is it supposed to work?

TIA - e

1

There are 1 best solutions below

0
On BEST ANSWER

I was wrong about the code that pulls node attributes. later on there is this fragment:

var newVal = o.styles[visualVar](item);

which refers to this (in the update method):

case 'label':
  self.idx[key][val].styles.label = function(item) {
    return format(byFn(item, key));
  };
  break;

where:

format = that.mappings.label.format || function(item) {
              return item.label;
            };
byFn: function(item, key) { return strToObjectRef(item, key); };

function strToObjectRef(obj, str) {
  // http://stackoverflow.com/a/6393943
  return str.split('.').reduce(function(obj, i) { return obj[i] }, obj);
}

...in looking at the output of byFn we find it's not an object, it's a string! so the dereference in the default format method fails.

so declaring my style like this makes it work:

design.setStyles({
    nodes: {
        label: {
            by: 'neo4j_data.System',
            format: function(item) { return item; }
        }
    }   
});

so the lesson here is that the format method is required when working with Neo4j

* Update I *

a patch to the code to redefine the format method would fix this problem:

format = that.mappings.label.format || function(item) {
              return typeof item === 'string' ? item : item.label;
            };