From a41afdc4cb7fc02b91e12fae531cadc250f25039 Mon Sep 17 00:00:00 2001 From: adam Date: Tue, 6 Nov 2012 18:53:41 +0700 Subject: [PATCH] binding of ejdb index api --- .idea/jsLibraryMappings.xml | 7 +-- Changelog | 4 ++ node/ejdb.js | 129 ++++++++++++++++++++++++++++++++++++++++++++ node/ejdb_native.cc | 102 ++++++++++++++++++++++++++++++++--- node/tests/t2.js | 20 +++++++ tcejdb/ChangeLog | 3 -- tcejdb/ejdb.h | 2 +- 7 files changed, 249 insertions(+), 18 deletions(-) create mode 100644 Changelog diff --git a/.idea/jsLibraryMappings.xml b/.idea/jsLibraryMappings.xml index 14019d8..f6e673a 100644 --- a/.idea/jsLibraryMappings.xml +++ b/.idea/jsLibraryMappings.xml @@ -1,8 +1,3 @@ - - - - - - + diff --git a/Changelog b/Changelog new file mode 100644 index 0000000..22bd275 --- /dev/null +++ b/Changelog @@ -0,0 +1,4 @@ + +2012-10-27 Anton Adamansky. + - Initial release based on Tokyo Cabinet v1.4.48 + - Release 1.0.0 diff --git a/node/ejdb.js b/node/ejdb.js index acee7e9..21389e5 100644 --- a/node/ejdb.js +++ b/node/ejdb.js @@ -385,5 +385,134 @@ EJDB.prototype.count = function(cname, qobj, orarr, hints, cb) { }); }; + +const simpleErrCb = function(err) { + if (err) { + console.error(err); + } +}; + +/** + * DROP indexes of all types for JSON field path + * @param {String} cname Name of collection + * @param {String} path JSON field path + * @param {Function} [cb] Optional callback function. Callback args: (error) + */ +EJDB.prototype.dropIndexes = function(cname, path, cb) { + if (typeof cb !== "function") cb = simpleErrCb; + return this._impl.setIndex(cname, path, ejdblib.JBIDXDROPALL, cb); +}; + +/** + * OPTIMIZE indexes of all types for JSON field path. + * B+ tree index file optimization. + * @param {String} cname Name of collection + * @param {String} path JSON field path + * @param {Function} [cb] Optional callback function. Callback args: (error) + */ +EJDB.prototype.optimizeIndexes = function(cname, path, cb) { + if (typeof cb !== "function") cb = simpleErrCb; + return this._impl.setIndex(cname, path, ejdblib.JBIDXOP, cb); +}; + +/** + * Ensure index presence of String type for JSON field path + * @param {String} cname Name of collection + * @param {String} path JSON field path + * @param {Function} [cb] Optional callback function. Callback args: (error) + */ +EJDB.prototype.ensureStringIndex = function(cname, path, cb) { + if (typeof cb !== "function") cb = simpleErrCb; + return this._impl.setIndex(cname, path, ejdblib.JBIDXSTR, cb); +}; + +/** + * Rebuild index of String type for JSON field path + * @param {String} cname Name of collection + * @param {String} path JSON field path + * @param {Function} [cb] Optional callback function. Callback args: (error) + */ +EJDB.prototype.rebuildStringIndex = function(cname, path, cb) { + if (typeof cb !== "function") cb = simpleErrCb; + return this._impl.setIndex(cname, path, ejdblib.JBIDXSTR | ejdblib.JBIDXREBLD, cb); +}; + +/** + * Drop index of String type for JSON field path + * @param {String} cname Name of collection + * @param {String} path JSON field path + * @param {Function} [cb] Optional callback function. Callback args: (error) + */ +EJDB.prototype.dropStringIndex = function(cname, path, cb) { + if (typeof cb !== "function") cb = simpleErrCb; + return this._impl.setIndex(cname, path, ejdblib.JBIDXSTR | ejdblib.JBIDXDROP, cb); +}; + +/** + * Ensure index presence of Number type for JSON field path + * @param {String} cname Name of collection + * @param {String} path JSON field path + * @param {Function} [cb] Optional callback function. Callback args: (error) + */ +EJDB.prototype.ensureNumberIndex = function(cname, path, cb) { + if (typeof cb !== "function") cb = simpleErrCb; + return this._impl.setIndex(cname, path, ejdblib.JBIDXNUM, cb); +}; + +/** + * Rebuild index of Number type for JSON field path + * @param {String} cname Name of collection + * @param {String} path JSON field path + * @param {Function} [cb] Optional callback function. Callback args: (error) + */ +EJDB.prototype.rebuildNumberIndex = function(cname, path, cb) { + if (typeof cb !== "function") cb = simpleErrCb; + return this._impl.setIndex(cname, path, ejdblib.JBIDXNUM | ejdblib.JBIDXREBLD, cb); +}; + +/** + * Drop index of Number type for JSON field path + * @param {String} cname Name of collection + * @param {String} path JSON field path + * @param {Function} [cb] Optional callback function. Callback args: (error) + */ +EJDB.prototype.dropNumberIndex = function(cname, path, cb) { + if (typeof cb !== "function") cb = simpleErrCb; + return this._impl.setIndex(cname, path, ejdblib.JBIDXNUM | ejdblib.JBIDXDROP, cb); +}; + +/** + * Ensure index presence of Array type for JSON field path + * @param {String} cname Name of collection + * @param {String} path JSON field path + * @param {Function} [cb] Optional callback function. Callback args: (error) + */ +EJDB.prototype.ensureArrayIndex = function(cname, path, cb) { + if (typeof cb !== "function") cb = simpleErrCb; + return this._impl.setIndex(cname, path, ejdblib.JBIDXARR, cb); +}; + +/** + * Rebuild index of Array type for JSON field path + * @param {String} cname Name of collection + * @param {String} path JSON field path + * @param {Function} [cb] Optional callback function. Callback args: (error) + */ +EJDB.prototype.rebuildArrayIndex = function(cname, path, cb) { + if (typeof cb !== "function") cb = simpleErrCb; + return this._impl.setIndex(cname, path, ejdblib.JBIDXARR | ejdblib.JBIDXREBLD, cb); +}; + +/** + * Drop index of Array type for JSON field path + * @param {String} cname Name of collection + * @param {String} path JSON field path + * @param {Function} [cb] Optional callback function. Callback args: (error) + */ +EJDB.prototype.dropArrayIndex = function(cname, path, cb) { + if (typeof cb !== "function") cb = simpleErrCb; + return this._impl.setIndex(cname, path, ejdblib.JBIDXARR | ejdblib.JBIDXDROP, cb); +}; + module.exports = EJDB; diff --git a/node/ejdb_native.cc b/node/ejdb_native.cc index 9744ff8..ceb57e6 100644 --- a/node/ejdb_native.cc +++ b/node/ejdb_native.cc @@ -41,6 +41,7 @@ namespace ejdb { static Persistent sym_compressed; static Persistent sym_records; static Persistent sym_cachedrecords; + static Persistent sym_explain; /////////////////////////////////////////////////////////////////////////// @@ -427,7 +428,8 @@ namespace ejdb { cmdLoad = 2, //Load BSON by oid cmdRemove = 3, //Remove BSON by oid cmdQuery = 4, //Query collection - cmdRemoveColl = 5 //Remove collection + cmdRemoveColl = 5, //Remove collection + cmdSetIndex = 6 //Set index }; struct BSONCmdData { //Any bson related cmd data @@ -453,15 +455,20 @@ namespace ejdb { TCLIST *res; int qflags; uint32_t count; + TCXSTR *log; - BSONQCmdData(const char* _cname, int _qflags) : - BSONCmdData::BSONCmdData(_cname), res(NULL), qflags(_qflags), count(0) { + BSONQCmdData(const char *_cname, int _qflags) : + BSONCmdData::BSONCmdData(_cname), res(NULL), qflags(_qflags), count(0), log(NULL) { } virtual ~BSONQCmdData() { if (res) { tclistdel(res); } + if (log) { + tcxstrdel(log); + } + } }; @@ -473,10 +480,21 @@ namespace ejdb { } }; + struct SetIndexCmdData { //set index + std::string cname; //Name of collection + std::string ipath; //JSON field path for index + int flags; //set index op flags + + SetIndexCmdData(const char *_cname, const char *_ipath, int _flags) : + cname(_cname), ipath(_ipath), flags(_flags) { + } + }; + typedef EIOCmdTask EJBTask; typedef EIOCmdTask BSONCmdTask; typedef EIOCmdTask BSONQCmdTask; typedef EIOCmdTask RMCollCmdTask; + typedef EIOCmdTask SetIndexCmdTask; static Persistent constructor_template; @@ -632,12 +650,35 @@ namespace ejdb { } cmdata->bsons.push_back(bs); } + + if (len > 1 && qarr->Get(len - 1)->IsObject()) { + Local hints = Local::Cast(qarr->Get(len - 1)); + if (hints->Get(sym_explain)->BooleanValue()) { + cmdata->log = tcxstrnew(); + } + } + NodeEJDB *njb = ObjectWrap::Unwrap< NodeEJDB > (args.This()); BSONQCmdTask *task = new BSONQCmdTask(cb, njb, cmdQuery, cmdata, BSONQCmdTask::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()); } + static Handle s_set_index(const Arguments& args) { + HandleScope scope; + REQ_ARGS(4); + REQ_STR_ARG(0, cname) + REQ_STR_ARG(1, ipath) + REQ_INT32_ARG(2, flags); + REQ_FUN_ARG(3, cb); + + NodeEJDB *njb = ObjectWrap::Unwrap< NodeEJDB > (args.This()); + SetIndexCmdData *cmdata = new SetIndexCmdData(*cname, *ipath, flags); + SetIndexCmdTask *task = new SetIndexCmdTask(cb, njb, cmdSetIndex, cmdata, SetIndexCmdTask::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()); + } + static Handle s_ecode(const Arguments& args) { HandleScope scope; NodeEJDB *njb = ObjectWrap::Unwrap< NodeEJDB > (args.This()); @@ -712,6 +753,9 @@ namespace ejdb { case cmdRemoveColl: rm_collection((RMCollCmdTask*) task); break; + case cmdSetIndex: + set_index((SetIndexCmdTask*) task); + break; } } @@ -733,7 +777,43 @@ namespace ejdb { case cmdRemoveColl: rm_collection_after((RMCollCmdTask*) task); break; + case cmdSetIndex: + set_index_after((SetIndexCmdTask*) task); + break; + } + } + + void set_index(SetIndexCmdTask *task) { + if (!_check_state((EJBTask*) task)) { + return; + } + SetIndexCmdData *cmdata = task->cmd_data; + assert(cmdata); + 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(); + return; + } + if (!ejdbsetindex(coll, cmdata->ipath.c_str(), cmdata->flags)) { + task->cmd_ret = CMD_RET_ERROR; + task->cmd_ret_msg = _jb_error_msg(); + } + } + + void set_index_after(SetIndexCmdTask *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); } } @@ -927,7 +1007,7 @@ namespace ejdb { task->cmd_ret_msg = _jb_error_msg(); goto finish; } - res = ejdbqrysearch(coll, q, &cmdata->count, cmdata->qflags, NULL); + res = ejdbqrysearch(coll, q, &cmdata->count, cmdata->qflags, cmdata->log); if (ejdbecode(m_jb) != TCESUCCESS) { if (res) { tclistdel(res); @@ -993,6 +1073,7 @@ finish: sym_compressed = NODE_PSYMBOL("compressed"); sym_records = NODE_PSYMBOL("records"); sym_cachedrecords = NODE_PSYMBOL("cachedrecords"); + sym_explain = NODE_PSYMBOL("$explain"); Local t = FunctionTemplate::New(s_new_object); @@ -1030,6 +1111,7 @@ finish: NODE_SET_PROTOTYPE_METHOD(constructor_template, "ensureCollection", s_ensure_collection); NODE_SET_PROTOTYPE_METHOD(constructor_template, "removeCollection", s_rm_collection); NODE_SET_PROTOTYPE_METHOD(constructor_template, "isOpen", s_is_open); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "setIndex", s_set_index); //Symbols target->Set(String::NewSymbol("NodeEJDB"), constructor_template->GetFunction()); @@ -1259,7 +1341,10 @@ finish: void NodeEJDB::query_after(BSONQCmdTask *task) { HandleScope scope; - Local argv[3]; + BSONQCmdData *cmdata = task->cmd_data; + assert(cmdata); + + Local argv[4]; if (task->cmd_ret != 0) { argv[0] = Exception::Error(String::New(task->cmd_ret_msg.c_str())); TryCatch try_catch; @@ -1269,8 +1354,6 @@ finish: } return; } - BSONQCmdData *cmdata = task->cmd_data; - assert(cmdata); TCLIST *res = cmdata->res; cmdata->res = NULL; //res will be freed by NodeEJDBCursor instead of ~BSONQCmdData() argv[0] = Local::New(Null()); @@ -1280,8 +1363,11 @@ finish: Local cursor(NodeEJDBCursor::constructor_template->GetFunction()->NewInstance(2, cursorArgv)); argv[1] = Local::New(cursor); argv[2] = Integer::New(cmdata->count); + if (cmdata->log) { + argv[3] = String::New((const char*) tcxstrptr(cmdata->log)); + } TryCatch try_catch; - task->cb->Call(Context::GetCurrent()->Global(), 3, argv); + task->cb->Call(Context::GetCurrent()->Global(), (cmdata->log) ? 4 : 3, argv); if (try_catch.HasCaught()) { FatalException(try_catch); } diff --git a/node/tests/t2.js b/node/tests/t2.js index f9ef719..b4f554f 100644 --- a/node/tests/t2.js +++ b/node/tests/t2.js @@ -218,6 +218,26 @@ module.exports.testSaveLoadBuffer = function(test) { }); }; +module.exports.testUseStringIndex = function(test) { + test.ok(jb); + test.ok(jb.isOpen()); + jb.find("birds", {"name" : "Molly"}, {"$explain" : true}, function(err, cursor, count, log) { + test.ifError(err); + test.ok(cursor); + test.ok(count == 1); + test.ok(log); + test.ok(log.indexOf("RUN FULLSCAN") !== -1); + //Now set the name string index + jb.ensureStringIndex("birds", "name", function(err) { + test.ifError(err); + jb.find("birds", {"name" : "Molly"}, {"$explain" : true}, function(err, cursor, count, log) { + test.ok(log.indexOf("MAIN IDX: 'sname'") !== -1); + test.done(); + }); + }); + }); +}; + module.exports.testRemove = function(test) { test.ok(jb); test.ok(jb.isOpen()); diff --git a/tcejdb/ChangeLog b/tcejdb/ChangeLog index ae20b39..e69de29 100644 --- a/tcejdb/ChangeLog +++ b/tcejdb/ChangeLog @@ -1,3 +0,0 @@ -2012-10-27 Softmotions. - - Initial release based on Tokyo Cabinet v1.4.48 - - Release 1.0.0 diff --git a/tcejdb/ejdb.h b/tcejdb/ejdb.h index c53cf8a..56d44d6 100644 --- a/tcejdb/ejdb.h +++ b/tcejdb/ejdb.h @@ -267,7 +267,7 @@ EJDB_EXPORT void ejdbquerydel(EJQ* q); * - Eg: flag = JBIDXDROP | JBIDXNUM (Drop number index) * - `JBIDXDROPALL` Drop index for all types. * - `JBIDXREBLD` Rebuild index of specified type. - * - `JBIDXOP` Optimize index of specified type. + * - `JBIDXOP` Optimize index of specified type. (Optimize the B+ tree index file) * * Examples: * - Set index for JSON path `addressbook.number` for strings and numbers: -- 2.7.4