0

I am using this Neo4J library and I would like to use promises instead. So I tried using bluebird's promisify. I created the following code...

    var db = new neo4j.GraphDatabase('....'),
        Promise = require('bluebird'),
        Cypher = Promise.promisify(db.cypher);

    var query = [
        'MATCH (node)',
        'OPTIONAL MATCH (node)-[rel]->( )',
        'RETURN DISTINCT node as node, collect(rel) as links'
    ].join('\n');

    var i = 0
    var onSuccess = function (results) {
            res.json(parseGraphResponse(results));
        },
        onFail = function (err) {
            console.log("Error " + err);
        };
    Cypher({
        query: query
    }).then(onSuccess).catch(onFail);

However, now I get the following error that is captured onError...

TypeError: Object # has no method 'http'

This version works fine...

    db.cypher({
        query: query
    }, function (err, results) {
        if (err) {
            console.log("Error " + err);
            return;
        }
        res.json(parseGraphResponse(results));
    });

A little more investigation shows that it is blowing up on this code...

GraphDatabase.prototype.cypher = function(opts, cb, _tx) {
  ...
  // Blows up here....
  return this.http({
        method: method,
        path: path,
        headers: headers,
        body: body,
        raw: true
      }, (function(_this) {
        return function(err, resp) {
          ...
        }
      })
}
Christophe Willemsen
  • 19,399
  • 2
  • 29
  • 36
Jackie
  • 21,969
  • 32
  • 147
  • 289

2 Answers2

1

I bet this will fix it:

Cypher = Promise.promisify(db.cypher.bind(db));

For future reference, the way to debug this is to interpret the error message as saying that this doesn't have an http method, but you know db does, so this must not be set to db. And indeed, passing db.cypher loses the this reference to db.

Aseem Kishore
  • 10,404
  • 10
  • 51
  • 56
  • Ahh gotcha so it is the whole lost context. Maybe this is a pull request to neo then because it seems to me that they should be using something like `var _this = this`. Wouldn't that also prevent it? (of course change `this.http` to `_this.http`) – Jackie Aug 05 '15 at 16:56
  • 1
    That wouldn't help in this case either: because by the time the function runs, `this` has been lost, so `var _this = this` wouldn't help. An alternative is for node-neo4j to proactively `bind` `GraphDatabase.prototype.cypher` to each instance when it's constructed. That tends to be an anti-pattern in JS for performance, but maybe not a big deal here in practice. Food for thought! – Aseem Kishore Aug 05 '15 at 22:03
0

In addition to the answer I would recommend adding the following...

if(db.cypher)
  Promise.promisify(db.cypher.bind(db));

the if will prevent failure if the DB is down. In my case I use an XML file to mock data when I can't reach the server.

Jackie
  • 21,969
  • 32
  • 147
  • 289