1

I have an open source project that deals with mongodb database. I am trying to make a function that queries the database to check if entry exists.

The problem is when if_exists() returning true or false it returns undefined since the mongodb driver function is asynchronous. The file is Query.js and I have tried the solution here to workaround the problem What is the right way to make a synchronous MongoDB query in Node.js? but still I get an undefined result with the get method.

What is the best way to make this work?

The output from the unit tests is as the following:

running unit tests...
add query test
exists tests:
get: undefined
{}
should be true: undefined
get: undefined
{}
should be false:undefined
Captains Logs listening on port 3000
Captains_Logs v0.5.0-21
[ { name: 'rhcp', _id: 50cbdcbe9c3cf97203000002 } ]
[ { name: 'os', _id: 50cbdcbe9c3cf97203000001 } ]

You can browse the whole codes at WeaponXI/cplog

Or for a quick look the query.js code is:

var DB = require('../../lib/db.js').DB;

function methods() {
  //query object
  var Q = {};
  //will act as our private variables to workaround asynchronous functions.
  //will delete non-required ones when done -- we don't have to, but just for continuity.
  exports.privates = {};




  //add tag to collection
  Q.add = function(tag) {
    if (typeof tag === "string") {
      //maybe we are adding a tag by name
      var obj = {
        name: tag
      };
    } else if (typeof tag === "object" && tag.name) {
      //maybe the tag object was specified, and tag's name was provided
      var obj = tag;
    }

    require('mongodb').connect(DB.mongo_url, function(err, db) {
      db.collection('tags', function(err, coll) {
        coll.insert(obj, {
          safe: true
        }, function(err, result) {
          console.log(result);

        });

      });
    });
  }
  var callback = {
    _set: function(key, val) {
      exports.privates[key] = val;
      //console.log(JSON.stringify(privates));
    },
    _get: function(key) {
      console.log("get: "+exports.privates.key);
      console.log(JSON.stringify(exports.privates));
      return exports.privates[key];
    },
    _unset: function(key) {
      delete privates[key];
    }
  }
  var if_exists = function(query, where, callback) {

    require('mongodb').connect(DB.mongo_url, function(err, db) {
      db.collection(where, function(err, coll) {
        coll.findOne(query, function(e, r) {
          //console.log(r);
          if (r === null) {
            callback._set("does_exist", false);
          } else {
            callback._set("does_exist", true);
          }

        });
      });
    });

    var result = callback._get("does_exist");
    // delete privates.does_exist;

    return result;
  }

  Q.if_exists = function(query, where) {
    if_exists(query, where, callback);

  }



  return Q;
}

var query = exports.query = methods();

function unit_test_add() {
  console.log("add query test");
  query.add("os");
  query.add({
    name: "rhcp"
  });
}

function unit_test_if_exists() {
  console.log("exists tests:");
  console.log("should be true: " + query.if_exists({
    name: "os"
  }, "tags"));
  console.log("should be false:" + query.if_exists({
    name: "ossuruk"
  }, "tags"));

}

function unit_tests() {
  console.log("running unit tests...");
  unit_test_add();
  unit_test_if_exists();

}
unit_tests();

Solution:

Query.js Query.test.js Gists

Thanks JohnnyHK!

Community
  • 1
  • 1
Logan
  • 10,649
  • 13
  • 41
  • 54

1 Answers1

6

You cannot use an asynchronous result as the return value from a function. It's that simple. You have to deliver the asynchronous result to the caller via a callback that is provided as a parameter to the function (or use futures/promises and effectively defer that step, but that's more involved).

if_exists should look like this instead:

var if_exists = function(query, where, callback) {

  require('mongodb').connect(DB.mongo_url, function(err, db) {
    db.collection(where, function(err, coll) {
      coll.findOne(query, function(e, r) {
        //console.log(r);
        if (r === null) {
          callback(e, false);
        } else {
          callback(e, true);
        }
        // You should either close db here or connect during start up
        // and leave it open.
        db.close();
      });
    });
  });
}
JohnnyHK
  • 305,182
  • 66
  • 621
  • 471
  • Ah thank you, I will try that now. Btw, there are two functions that connect the database and I thought it closed when the function was at its end (I sort of just copied code from the examples and they didn't close). In my case then the script opens two connections and never closes them? Can I connect in beginning like this: `require('mongodb').connect(DB.mongo_url, function(err, db) { DB.conn = db; });` and use `DB.conn.collection( ... )` in if_exists function? If I do something like that where do I close? – Logan Dec 15 '12 at 03:51
  • @Logan Yes, something like that should work. It's actually a pool of connections that you're opening via the `connect` call, and you would close it during your app shut down (or just let the system clean that up when your app goes away). – JohnnyHK Dec 15 '12 at 04:18
  • thank you! it worked as you suggested. I can't believe I was trying to do it in a really obscure way until I realized how callbacks should have been done. Also thank you for the notes for connection issues. I will try that too. Here is the solution that worked with your suggestions. https://gist.github.com/4291691.git – Logan Dec 15 '12 at 06:20
  • I don't get it though, doesn't this limit the application? Why isn't it so that it is possible to set a variable according to the result of the query, thus object orienting the database queries... I hope there is a good reason for that. – Logan Dec 15 '12 at 07:25
  • What is it that makes these functions asynchronous? Is it called multi-threading? Can't it be hacked? – Logan Dec 15 '12 at 08:33
  • @Logan Those are big questions not easily answered in comments. Keep reading up on node.js until it becomes clearer and post new questions as you need to. Start [here](http://www.nodebeginner.org/) if you haven't read that yet. – JohnnyHK Dec 15 '12 at 14:59
  • I understand, I will do more reading on this subject. If you could give me a few keywords though I would appreciate it. Should I look up multi-thread (is it what its called?) I need a little guidance in terms of what to research, and the terminology. – Logan Dec 16 '12 at 02:57
  • 1
    @Logan No, your node.js code all runs in a single thread; the key concept here async functions and callbacks. – JohnnyHK Dec 16 '12 at 07:25