From 2f457d1db4d12dfff91dd9b1f8775f53c530ec5f Mon Sep 17 00:00:00 2001 From: adam Date: Mon, 5 Nov 2012 19:36:20 +0700 Subject: [PATCH] #1 --- node/ejdb.js | 94 ++++++++++++++++++++++++++++++++++++++------ node/ejdb_native.cc | 86 ++++++++++++++++++++++++++++------------ node/tests/t1.js | 29 ++++++++------ node/tests/t2.js | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 268 insertions(+), 51 deletions(-) diff --git a/node/ejdb.js b/node/ejdb.js index 72d61ab..3bc84ab 100644 --- a/node/ejdb.js +++ b/node/ejdb.js @@ -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; diff --git a/node/ejdb_native.cc b/node/ejdb_native.cc index 118cc15..9744ff8 100644 --- a/node/ejdb_native.cc +++ b/node/ejdb_native.cc @@ -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::Cast(pv)), + Buffer::Length(Handle::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::Cast(pv)), - Buffer::Length(Handle::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 bsons; //bsons to save|query std::vector 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 EJBTask; typedef EIOCmdTask BSONCmdTask; typedef EIOCmdTask BSONQCmdTask; + typedef EIOCmdTask RMCollCmdTask; static Persistent 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 argv[1]; + if (task->cmd_ret != 0) { + argv[0] = Exception::Error(String::New(task->cmd_ret_msg.c_str())); + } else { + argv[0] = Local::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 &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()); } diff --git a/node/tests/t1.js b/node/tests/t1.js index d2fb056..9d58cd4 100644 --- a/node/tests/t1.js +++ b/node/tests/t1.js @@ -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(); + }); }; diff --git a/node/tests/t2.js b/node/tests/t2.js index ce372a8..f9ef719 100644 --- a/node/tests/t2.js +++ b/node/tests/t2.js @@ -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) { -- 2.7.4