#1
authoradam <anton@adamansky.com>
Mon, 5 Nov 2012 12:36:20 +0000 (19:36 +0700)
committeradam <anton@adamansky.com>
Mon, 5 Nov 2012 12:36:20 +0000 (19:36 +0700)
node/ejdb.js
node/ejdb_native.cc
node/tests/t1.js
node/tests/t2.js

index 72d61ab..3bc84ab 100644 (file)
@@ -36,8 +36,28 @@ EJDB.prototype.ensureCollection = function(cname, copts) {
     return this._impl.ensureCollection(cname, copts || {});
 };
 
-EJDB.prototype.rmCollection = function(cname, prune) {
-    return this._impl.rmCollection(cname, !!prune);
+
+/**
+ *  Call variations:
+ *      - rmCollection(cname)
+ *      - rmCollection(cname, cb)
+ *      - rmCollection(cname, prune, cb)
+ *
+ * @param cname
+ * @param prune
+ * @param cb
+ * @return {*}
+ */
+EJDB.prototype.removeCollection = function(cname, prune, cb) {
+    if (arguments.length == 2) {
+        cb = prune;
+        prune = false;
+    }
+    if (!cb) {
+        cb = function() {
+        };
+    }
+    return this._impl.removeCollection(cname, !!prune, cb);
 };
 
 EJDB.prototype.save = function(cname, jsarr, cb) {
@@ -78,10 +98,11 @@ EJDB.prototype.remove = function(cname, oid, cb) {
 
 /**
  *
- * Calling variations:
- *       - query(cname, qobj, qobjarr, hints, cb)
- *       - query(cname, qobj, hints, cb)
- *       - query(cname, qobj, cb)
+ * Call variations:
+ *       - find(cname, qobj, cb)
+ *       - find(cname, qobj, hints, cb)
+ *       - find(cname, qobj, qobjarr, cb)
+ *       - find(cname, qobj, qobjarr, hints, cb)
  *
  * @param cname
  * @param qobj
@@ -89,11 +110,15 @@ EJDB.prototype.remove = function(cname, oid, cb) {
  * @param hints
  * @param cb
  */
-EJDB.prototype.query = function(cname, qobj, orarr, hints, cb) {
+EJDB.prototype.find = function(cname, qobj, orarr, hints, cb) {
     if (arguments.length == 4) {
         cb = hints;
-        hints = orarr;
-        orarr = [];
+        if (orarr && orarr.constructor === Array) {
+            hints = {};
+        } else {
+            hints = orarr;
+            orarr = [];
+        }
     } else if (arguments.length == 3) {
         cb = orarr;
         orarr = [];
@@ -105,10 +130,10 @@ EJDB.prototype.query = function(cname, qobj, orarr, hints, cb) {
     if (typeof cname !== "string") {
         throw new Error("Collection name 'cname' argument must be specified");
     }
-    if (typeof hints !== "object") {
+    if (!hints || typeof hints !== "object") {
         hints = {};
     }
-    if (typeof qobj !== "object") {
+    if (!qobj || typeof qobj !== "object") {
         qobj = {};
     }
     return this._impl.query(cname,
@@ -117,6 +142,53 @@ EJDB.prototype.query = function(cname, qobj, orarr, hints, cb) {
             cb);
 };
 
+/*
+ * Call variations:
+ *       - findOne(cname, qobj, cb)
+ *       - findOne(cname, qobj, hints, cb)
+ *       - findOne(cname, qobj, qobjarr, cb)
+ *       - findOne(cname, qobj, qobjarr, hints, cb)
+ */
+EJDB.prototype.findOne = function(cname, qobj, orarr, hints, cb) {
+    if (arguments.length == 4) {
+        cb = hints;
+        if (orarr && orarr.constructor === Array) {
+            hints = {};
+        } else {
+            hints = orarr;
+            orarr = [];
+        }
+    } else if (arguments.length == 3) {
+        cb = orarr;
+        orarr = [];
+        hints = {};
+    }
+    if (typeof cb !== "function") {
+        throw new Error("Callback 'cb' argument must be specified");
+    }
+    if (typeof cname !== "string") {
+        throw new Error("Collection name 'cname' argument must be specified");
+    }
+    if (!hints || typeof hints !== "object") {
+        hints = {};
+    }
+    if (!qobj || typeof qobj !== "object") {
+        qobj = {};
+    }
+    hints["$max"] = 1;
+    return this._impl.query(cname, [qobj].concat(orarr, hints), 0,
+            function(err, cursor) {
+                if (err) {
+                    cb(err);
+                    return;
+                }
+                if (cursor.next()) {
+                    cb(null, cursor.object());
+                } else {
+                    cb(null, null);
+                }
+            });
+};
 
 module.exports = EJDB;
 
index 118cc15..9744ff8 100644 (file)
@@ -296,7 +296,6 @@ namespace ejdb {
                     }
                     break;
                 case BSON_BINDATA:
-                    //TODO test it!
                     if (obt == BSON_ARRAY) {
                         ret->Set(knum,
                                 Buffer::New(String::New(bson_iterator_bin_data(it),
@@ -387,6 +386,10 @@ namespace ejdb {
                     sf.append("m");
                 }
                 bson_append_regex(bs, *spn, *sr, sf.c_str());
+            } else if (Buffer::HasInstance(pv)) {
+                bson_append_binary(bs, *spn, BSON_BIN_BINARY,
+                        Buffer::Data(Handle<Object>::Cast(pv)),
+                        Buffer::Length(Handle<Object>::Cast(pv)));
             } else if (pv->IsObject() || pv->IsArray()) {
                 if (pv->IsArray()) {
                     bson_append_start_array(bs, *spn);
@@ -399,10 +402,6 @@ namespace ejdb {
                 } else {
                     bson_append_finish_object(bs);
                 }
-            } else if (Buffer::HasInstance(pv)) {
-                bson_append_binary(bs, *spn, BSON_BIN_BINARY,
-                        Buffer::Data(Handle<Object>::Cast(pv)),
-                        Buffer::Length(Handle<Object>::Cast(pv)));
             }
         }
     }
@@ -427,19 +426,17 @@ namespace ejdb {
             cmdSave = 1, //Save JSON object
             cmdLoad = 2, //Load BSON by oid
             cmdRemove = 3, //Remove BSON by oid
-            cmdQuery = 4 //Query collection
+            cmdQuery = 4, //Query collection
+            cmdRemoveColl = 5 //Remove collection
         };
 
-        //Any bson cmd data
-
-        struct BSONCmdData {
+        struct BSONCmdData { //Any bson related cmd data
             std::string cname; //Name of collection
             std::vector<bson*> bsons; //bsons to save|query
             std::vector<bson_oid_t> ids; //saved updated oids
             bson_oid_t ref; //Bson ref
 
-            BSONCmdData(const char* cname) {
-                this->cname = cname;
+            BSONCmdData(const char* _cname) : cname(_cname) {
                 memset(&ref, 0, sizeof (ref));
             }
 
@@ -452,9 +449,7 @@ namespace ejdb {
             }
         };
 
-        //Query cmd data
-
-        struct BSONQCmdData : public BSONCmdData {
+        struct BSONQCmdData : public BSONCmdData { //query cmd data
             TCLIST *res;
             int qflags;
             uint32_t count;
@@ -470,9 +465,18 @@ namespace ejdb {
             }
         };
 
+        struct RMCollCmdData { //remove collection
+            std::string cname; //Name of collection
+            bool prune;
+
+            RMCollCmdData(const char* _cname, bool _prune) : cname(_cname), prune(_prune) {
+            }
+        };
+
         typedef EIOCmdTask<NodeEJDB> EJBTask;
         typedef EIOCmdTask<NodeEJDB, BSONCmdData> BSONCmdTask;
         typedef EIOCmdTask<NodeEJDB, BSONQCmdData> BSONQCmdTask;
+        typedef EIOCmdTask<NodeEJDB, RMCollCmdData> RMCollCmdTask;
 
         static Persistent<FunctionTemplate> constructor_template;
 
@@ -668,13 +672,14 @@ namespace ejdb {
             HandleScope scope;
             REQ_STR_ARG(0, cname);
             REQ_VAL_ARG(1, prune);
+            REQ_FUN_ARG(2, cb);
             NodeEJDB *njb = ObjectWrap::Unwrap< NodeEJDB > (args.This());
             if (!ejdbisopen(njb->m_jb)) {
                 return scope.Close(ThrowException(Exception::Error(String::New("Operation on closed EJDB instance"))));
             }
-            if (!ejdbrmcoll(njb->m_jb, *cname, prune->BooleanValue())) {
-                return scope.Close(ThrowException(Exception::Error(String::New(njb->_jb_error_msg()))));
-            }
+            RMCollCmdData *cmdata = new RMCollCmdData(*cname, prune->BooleanValue());
+            RMCollCmdTask *task = new RMCollCmdTask(cb, njb, cmdRemoveColl, cmdata, RMCollCmdTask::delete_val);
+            uv_queue_work(uv_default_loop(), &task->uv_work, s_exec_cmd_eio, s_exec_cmd_eio_after);
             return scope.Close(args.This());
         }
 
@@ -704,6 +709,9 @@ namespace ejdb {
                 case cmdRemove:
                     remove((BSONCmdTask*) task);
                     break;
+                case cmdRemoveColl:
+                    rm_collection((RMCollCmdTask*) task);
+                    break;
             }
         }
 
@@ -722,6 +730,37 @@ namespace ejdb {
                 case cmdRemove:
                     remove_after((BSONCmdTask*) task);
                     break;
+                case cmdRemoveColl:
+                    rm_collection_after((RMCollCmdTask*) task);
+                    break;
+
+            }
+        }
+
+        void rm_collection(RMCollCmdTask *task) {
+            if (!_check_state((EJBTask*) task)) {
+                return;
+            }
+            RMCollCmdData *cmdata = task->cmd_data;
+            assert(cmdata);
+            if (!ejdbrmcoll(m_jb, cmdata->cname.c_str(), cmdata->prune)) {
+                task->cmd_ret = CMD_RET_ERROR;
+                task->cmd_ret_msg = _jb_error_msg();
+            }
+        }
+
+        void rm_collection_after(RMCollCmdTask *task) {
+            HandleScope scope;
+            Local<Value> argv[1];
+            if (task->cmd_ret != 0) {
+                argv[0] = Exception::Error(String::New(task->cmd_ret_msg.c_str()));
+            } else {
+                argv[0] = Local<Primitive>::New(Null());
+            }
+            TryCatch try_catch;
+            task->cb->Call(Context::GetCurrent()->Global(), 1, argv);
+            if (try_catch.HasCaught()) {
+                FatalException(try_catch);
             }
         }
 
@@ -862,12 +901,13 @@ namespace ejdb {
             if (!_check_state((EJBTask*) task)) {
                 return;
             }
+            TCLIST *res = NULL;
             bson oqarrstack[8]; //max 8 $or bsons on stack
             BSONQCmdData *cmdata = task->cmd_data;
-            EJCOLL *coll = ejdbcreatecoll(m_jb, cmdata->cname.c_str(), NULL);
-            if (!coll) {
-                task->cmd_ret = CMD_RET_ERROR;
-                task->cmd_ret_msg = _jb_error_msg();
+            EJCOLL *coll = ejdbgetcoll(m_jb, cmdata->cname.c_str());
+            if (!coll) { //No collection -> no results
+                cmdata->res = tclistnew2(1);
+                cmdata->count = 0;
                 return;
             }
             std::vector<bson*> &bsons = cmdata->bsons;
@@ -877,7 +917,6 @@ namespace ejdb {
             for (int i = 1; i < (int) bsons.size() - 1; ++i) {
                 oqarr[i - 1] = *(bsons.at(i));
             }
-            TCLIST *res = NULL;
             EJQ *q = ejdbcreatequery(
                     m_jb,
                     bsons.front(),
@@ -989,10 +1028,9 @@ finish:
             NODE_SET_PROTOTYPE_METHOD(constructor_template, "query", s_query);
             NODE_SET_PROTOTYPE_METHOD(constructor_template, "lastError", s_ecode);
             NODE_SET_PROTOTYPE_METHOD(constructor_template, "ensureCollection", s_ensure_collection);
-            NODE_SET_PROTOTYPE_METHOD(constructor_template, "rmCollection", s_rm_collection);
+            NODE_SET_PROTOTYPE_METHOD(constructor_template, "removeCollection", s_rm_collection);
             NODE_SET_PROTOTYPE_METHOD(constructor_template, "isOpen", s_is_open);
 
-
             //Symbols
             target->Set(String::NewSymbol("NodeEJDB"), constructor_template->GetFunction());
         }
index d2fb056..9d58cd4 100644 (file)
@@ -15,7 +15,7 @@ module.exports.testOpenClose = function(test) {
 };
 
 
-module.exports.testEnsureCollection = function(test) {
+module.exports.testEnsureAndRemoveCollection = function(test) {
     var jb = EJDB.open("var/tdbt12", EJDB.JBOWRITER | EJDB.JBOCREAT | EJDB.JBOTRUNC);
     test.equal(jb.isOpen(), true);
     var c1opts = {
@@ -26,17 +26,22 @@ module.exports.testEnsureCollection = function(test) {
     };
     jb.ensureCollection("c1", c1opts);
     test.ok(fs.existsSync("var/tdbt12_c1"));
-    jb.rmCollection("c1", true);
-    test.ok(!fs.existsSync("var/tdbt12_c1"));
-    jb.close();
-    var err = null;
-    try {
-        jb.rmCollection("c1", true);
-    } catch (e) {
-        err = e;
-    }
-    test.ok(err);
-    test.done();
+
+    jb.removeCollection("c1", true, function(err) {
+        test.ifError(err);
+        test.ok(!fs.existsSync("var/tdbt12_c1"));
+
+        //Test operation on closed database instance
+        jb.close();
+        var err = null;
+        try {
+            jb.removeCollection("c1", true);
+        } catch (e) {
+            err = e;
+        }
+        test.ok(err);
+        test.done();
+    });
 };
 
 
index ce372a8..f9ef719 100644 (file)
@@ -52,7 +52,7 @@ module.exports.testSaveLoad = function(test) {
 module.exports.testQuery1 = function(test) {
     test.ok(jb);
     test.ok(jb.isOpen());
-    jb.query("parrots", {}, function(err, cursor, count) {
+    jb.find("parrots", {}, function(err, cursor, count) {
         test.ifError(err);
         test.equal(count, 2);
         test.ok(cursor);
@@ -81,7 +81,7 @@ module.exports.testQuery1 = function(test) {
 module.exports.testQuery2 = function(test) {
     test.ok(jb);
     test.ok(jb.isOpen());
-    jb.query("parrots",
+    jb.find("parrots",
             {name : /(grenny|bounty)/ig},
             {$orderby : {name : 1}},
             function(err, cursor, count) {
@@ -110,11 +110,11 @@ module.exports.testQuery2 = function(test) {
 };
 
 
-//Test with OR
+//Test with OR, cursor.reset
 module.exports.testQuery3 = function(test) {
     test.ok(jb);
     test.ok(jb.isOpen());
-    jb.query("parrots",
+    jb.find("parrots",
             {}, //main query selector
             [
                 //OR joined conditions
@@ -142,9 +142,111 @@ module.exports.testQuery3 = function(test) {
                     test.equal(rv["likes"].join(","), "green color,night,toys");
                     test.equal(cursor.field("likes").join(","), "green color,night,toys");
                 }
+
+                //test cursor reset
+                cursor.reset();
+                for (c = 0; cursor.next(); ++c);
+                test.equal(c, 2);
+
+                //explicit cursor close
+                cursor.close();
+                test.done();
+            });
+};
+
+module.exports.testCircular = function(test) {
+    test.ok(jb);
+    test.ok(jb.isOpen());
+
+    //Circular query object
+    var cirQuery = {};
+    cirQuery.cq = cirQuery;
+    var err = null;
+    try {
+        jb.find("parrots", cirQuery, function(err, cursor, count) {
+        });
+    } catch (e) {
+        err = e;
+    }
+    test.ok(err);
+    test.equal(err.message, "Circular object reference");
+
+    err = null;
+    try {
+        jb.save("parrots", [cirQuery]);
+    } catch (e) {
+        err = e;
+    }
+    test.ok(err);
+    test.equal(err.message, "Circular object reference");
+    test.done();
+};
+
+
+module.exports.testSaveLoadBuffer = function(test) {
+    test.ok(jb);
+    test.ok(jb.isOpen());
+
+    var sally = {
+        "name" : "Sally",
+        "mood" : "Angry",
+        "secret" : new Buffer("Something binary secrect", "utf8")
+    };
+    var molly = {
+        "name" : "Molly",
+        "mood" : "Very angry",
+        "secret" : null
+    };
+
+    jb.save("birds", sally, function(err, oids) {
+        test.ifError(err);
+        test.ok(oids);
+        test.ok(oids.length === 1);
+        test.ok(sally["_id"]);
+        var sallyOid = sally["_id"];
+        jb.load("birds", sallyOid, function(err, obj) {
+            test.ifError(err);
+            test.ok(obj["secret"] instanceof Buffer);
+            test.equal(obj["secret"], "Something binary secrect");
+            jb.save("birds", [sally, molly], function(err, oids) {
+                test.ifError(err);
+                test.ok(oids);
+                test.ok(oids.indexOf(sallyOid) !== -1);
+                test.done();
+            });
+        });
+    });
+};
+
+module.exports.testRemove = function(test) {
+    test.ok(jb);
+    test.ok(jb.isOpen());
+    jb.findOne("birds", {"name" : "Molly"}, function(err, obj) {
+        test.ifError(err);
+        test.ok(obj["_id"]);
+        test.equal(obj["mood"], "Very angry");
+        //Bye bye Molly!
+        jb.remove("birds", obj["_id"], function(err) {
+            test.ifError(err);
+            jb.findOne("birds", {"name" : "Molly"}, function(err, obj) {
+                test.ifError(err);
+                test.ok(obj === null);
                 test.done();
             });
+        });
+    });
+};
 
+module.exports.testRemoveColls = function(test) {
+    jb.removeCollection("birds", function(err) {
+        test.ifError(err);
+        jb.find("birds", {}, function(err, cursor, count) { //Query on not existing collection
+            test.ifError(err);
+            test.equal(count, 0);
+            test.ok(!cursor.next());
+            test.done();
+        });
+    });
 };
 
 module.exports.testClose = function(test) {