From ad12fc1e197d963242802d33515055f2d2ba290f Mon Sep 17 00:00:00 2001 From: adam Date: Wed, 17 Jul 2013 18:43:13 +0700 Subject: [PATCH] #59 #72 --- README.md | 175 +++++++++++++++++++++++++++++++++++++++++++++++++++------- tcejdb/ejdb.c | 79 ++++++++++++++++++++++++++ tcejdb/ejdb.h | 49 ++++++++++++++-- 3 files changed, 279 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index b9a6550..d0e9637 100644 --- a/README.md +++ b/README.md @@ -30,16 +30,13 @@ Features * LGPL license allows you to embed this library into proprietary software. * [EJDB and TokyoCabinet API ported to Windows](https://github.com/Softmotions/ejdb/blob/master/tcejdb/WIN32.md) * MongoDB-like queries and overall philosophy. +* [Collection joins](https://github.com/Softmotions/ejdb/wiki/Collection-joins) * Collection level write locking. * Collection level transactions. -* String token matching queries: ```$stror``` ```$strand``` -* [Node.js](http://nodejs.org) binding -* [Collection joins](https://github.com/Softmotions/ejdb/wiki/Collection-joins) -* Python/Lua/Java/Ruby/.Net/Go bindings +* Node.js/Python/Lua/Java/Ruby/.Net/Go bindings * [Adobe Air Native Extension (ANE) for EJDB] (https://github.com/thejustinwalsh/airejdb) * [Pike language binding] (https://github.com/hww3/pike_modules-ejdb) - Documentation ================================ @@ -67,7 +64,7 @@ Community * **We use [EJDB Google group](http://groups.google.com/group/ejdb) as our mailing list.** * [Projects using EJDB](https://github.com/Softmotions/ejdb/wiki/Projects-using-EJDB) -NodeJS binding +EJDB NodeJS ================================= One snippet intro @@ -121,9 +118,9 @@ jb.save("parrots", [parrot1, parrot2], function(err, oids) { }); }); ``` -**[EJDB NodeJS binding page](https://github.com/Softmotions/ejdb/blob/master/node/README.md)** +**[EJDB NodeJS binding page](https://github.com/Softmotions/ejdb/blob/master/node)** -EJDB Python binding +EJDB Python ================================== One snippet intro @@ -164,9 +161,9 @@ with ejdb.find("parrots2", {"likes" : "toys"}, ejdb.close() ``` -**[EJDB Python 2.7/3.x binding page](https://github.com/Softmotions/ejdb/blob/master/pyejdb/README.md)** +**[EJDB Python 2.7/3.x binding page](https://github.com/Softmotions/ejdb/blob/master/pyejdb)** -EJDB Lua binding +EJDB Lua ================================== One snippet intro @@ -233,8 +230,155 @@ end db:close() ``` -* **[Lua binding](https://github.com/Softmotions/ejdb/blob/master/luaejdb/README.md)** +**[EJDB Lua binding page](https://github.com/Softmotions/ejdb/blob/master/luaejdb)** + +EJDB Go binding +================================== + +One snippet intro +----------------------------------- + +```go +package ejdbtutorial + +import ( + "fmt" + "github.com/mkilling/goejdb" + "labix.org/v2/mgo/bson" + "os" +) + +func main() { + // Create a new database file and open it + jb, err := goejdb.Open("addressbook", JBOWRITER | JBOCREAT | JBOTRUNC) + if err != nil { + os.Exit(1) + } + // Get or create collection 'contacts' + coll, _ := jb.CreateColl("contacts", nil) + + // Insert one record: + // JSON: {'name' : 'Bruce', 'phone' : '333-222-333', 'age' : 58} + rec := map[string]interface{} {"name" : "Bruce", "phone" : "333-222-333", "age" : 58} + bsrec, _ := bson.Marshal(rec) + coll.SaveBson(bsrec) + fmt.Printf("\nSaved Bruce") + + // Now execute query + res, _ := coll.Find(`{"name" : {"$begin" : "Bru"}}`) // Name starts with 'Bru' string + fmt.Printf("\n\nRecords found: %d\n", len(res)) + + // Now print the result set records + for _, bs := range res { + var m map[string]interface{} + bson.Unmarshal(bs, &m) + fmt.Println(m) + } + + // Close database + jb.Close() +} +``` +**[EJDB Go binding page](https://github.com/mkilling/goejdb)** + + +EJDB Ruby binding +================================== + +One snippet intro +--------------------------------- + +```Ruby +require "rbejdb" + +#Open zoo DB +jb = EJDB.open("zoo", EJDB::DEFAULT_OPEN_MODE | EJDB::JBOTRUNC) + +parrot1 = { + "name" => "Grenny", + "type" => "African Grey", + "male" => true, + "age" => 1, + "birthdate" => Time.now, + "likes" => ["green color", "night", "toys"], + "extra1" => nil +} +parrot2 = { + "name" => "Bounty", + "type" => "Cockatoo", + "male" => false, + "age" => 15, + "birthdate" => Time.now, + "likes" => ["sugar cane"], + "extra1" => nil +} + +jb.save("parrots", parrot1, parrot2) +puts "Grenny OID: #{parrot1["_id"]}" +puts "Bounty OID: #{parrot2["_id"]}" + +results = jb.find("parrots", {"likes" => "toys"}, {"$orderby" => {"name" => 1}}) + +puts "Found #{results.count} parrots" + +results.each { |res| + puts "#{res['name']} likes toys!" +} + +results.close #It's not mandatory to close cursor explicitly +jb.close #Close the database + +``` +**[EJDB Ruby binding page](https://github.com/Softmotions/ejdb/blob/master/rbejdb/)** + + +EJDB Adobe AIR binding +================================== + +One snippet intro +--------------------------------- + +```as3 +// Open the zoo DB +var db:EJDBDatabase = EJDB.open("zoo", EJDB.DEFAULT_OPEN_MODE | EJDB.JBOTRUNC) as EJDBDatabase; + +var parrot1:Object = { + "name" : "Grenny", + "type" : "African Grey", + "male" : true, + "age" : 1, + "birthdate" : new Date(), + "likes" : ["green color", "night", "toys"], + "extra1" : null +}; +var parrot2:Object = { + "name" : "Bounty", + "type" : "Cockatoo", + "male" : false, + "age" : 15, + "birthdate" : new Date(), + "likes" : ["sugar cane"] +}; + +var oids:Array = db.save("parrots", [parrot1, parrot2]); +trace("Grenny OID: " + parrot1._id); +trace("Bounty OID: " + parrot2._id); + +var cursor:EJDBCursor = db.find("parrots", + {"likes" : "toys"}, + [], + {"$orderby" : {"name" : 1}} +); +trace("Found " + cursor.length + " parrots"); +while (cursor.next()) { + trace(cursor.field("name") + " likes toys!"); +} + +cursor.close(); // It IS mandatory to close cursor explicitly to free up resources +db.close(); // Close the database +``` +**[Adobe Air Native Extension (ANE) for EJDB] (https://github.com/thejustinwalsh/airejdb)** EJDB C Library ================================== @@ -308,7 +452,6 @@ int main() { You can save this code in `csnippet.c` And build: - ```sh gcc -std=c99 -Wall -pedantic -c -o csnippet.o csnippet.c gcc -o csnippet csnippet.o -ltcejdb @@ -330,7 +473,7 @@ Manual installation ### Build and install -``` +```sh cd ./tcejdb ./configure --prefix= && make && make check make install @@ -477,9 +620,3 @@ Limitations TODO ------------------------------------ * Collect collection index statistic - -Related software ------------------------------------- -[Connect session store backed by EJDB database](https://github.com/Softmotions/connect-session-ejdb) - - diff --git a/tcejdb/ejdb.c b/tcejdb/ejdb.c index 411fba1..03782ca 100644 --- a/tcejdb/ejdb.c +++ b/tcejdb/ejdb.c @@ -170,6 +170,7 @@ const char* ejdberrmsg(int ecode) { case JBEEJSONPARSE: return "JSON parsing failed"; case JBEEI: return "data export/import failed"; case JBETOOBIGBSON: return "bson size exceeds the maximum allowed size limit"; + case JBEINVALIDCMD: return "invalid ejdb command specified"; default: return tcerrmsg(ecode); } } @@ -831,7 +832,85 @@ finish: tclistdel(bspaths); } return !err; +} + +bson* ejdbcommand(EJDB *jb, bson *cmd) { + bson *ret = bson_create(); + int ecode = 0; + const char *err = NULL; + TCXSTR *xlog = NULL; + TCLIST *cnames = NULL; + bool rv = true; + bson_init(ret); + bson_type bt; + bson_iterator it; + bson_iterator_init(&it, cmd); + + while ((bt = bson_iterator_next(&it)) != BSON_EOO) { + const char *key = bson_iterator_key(&it); + if (!strcmp("ejdbexport", key) || !strcmp("ejdbimport", key)) { + xlog = tcxstrnew(); + char *path = NULL; + int flags = 0; + bson_iterator sit; + bson_iterator_subiterator(&it, &sit); + if (bson_find_fieldpath_value("path", &sit) == BSON_STRING) { + path = strdup(bson_iterator_string(&sit)); + } + bson_iterator_subiterator(&it, &sit); + if (bson_find_fieldpath_value("mode", &sit) == BSON_INT) { + flags = bson_iterator_int(&sit); + } + bson_iterator_subiterator(&it, &sit); + if (bson_find_fieldpath_value("cnames", &sit) == BSON_ARRAY) { + bson_iterator ait; + bson_iterator_subiterator(&sit, &ait); + while ((bt = bson_iterator_next(&ait)) != BSON_EOO) { + if (bt == BSON_STRING) { + if (cnames == NULL) { + cnames = tclistnew(); + } + const char *sv = bson_iterator_string(&ait); + TCLISTPUSH(cnames, sv, strlen(sv)); + } + } + } + if (path == NULL) { + err = "Missing required 'path' field"; + ecode = JBEINVALIDCMD; + goto finish; + } + if (!strcmp("ejdbexport", key)) { + rv = ejdbexport(jb, path, cnames, flags, xlog); + } else { //ejdbimport + rv = ejdbimport(jb, path, cnames, flags, xlog); + } + if (!rv) { + ecode = ejdbecode(jb); + err = ejdberrmsg(ecode); + } + TCFREE(path); + } else { + err = "Unknown command"; + ecode = JBEINVALIDCMD; + goto finish; + } + } +finish: + if (err) { + bson_append_string(ret, "error", err); + bson_append_int(ret, "errorCode", ecode); + } + if (xlog) { + bson_append_string(ret, "log", TCXSTRPTR(xlog)); + tcxstrdel(xlog); + } + if (cnames) { + tclistdel(cnames); + } + bson_finish(ret); + return ret; } /************************************************************************************************* diff --git a/tcejdb/ejdb.h b/tcejdb/ejdb.h index b9b135b..69d49c4 100644 --- a/tcejdb/ejdb.h +++ b/tcejdb/ejdb.h @@ -66,7 +66,8 @@ enum { /** Error codes */ JBEMAXNUMCOLS = 9014, /**< Exceeded the maximum number of collections per database */ JBEEI = 9015, /**< EJDB export/import error */ JBEEJSONPARSE = 9016, /**< JSON parsing failed */ - JBETOOBIGBSON = 9017 /**< BSON size is too big */ + JBETOOBIGBSON = 9017, /**< BSON size is too big */ + JBEINVALIDCMD = 9018 /**< Invalid ejdb command specified */ }; enum { /** Database open modes */ @@ -507,7 +508,6 @@ EJDB_EXPORT bool ejdbtranstatus(EJCOLL *jcoll, bool *txactive); /** Gets description of EJDB database and its collections. */ EJDB_EXPORT bson* ejdbmeta(EJDB *jb); - /** Export/Import settings used in `ejdbexport()` and `ejdbimport()` functions. */ enum { JBJSONEXPORT = 1, //If set json collection data will be exported as JSON files instead of BSON. @@ -554,9 +554,48 @@ EJDB_EXPORT bool ejdbexport(EJDB *jb, const char *path, TCLIST *cnames, int flag */ EJDB_EXPORT bool ejdbimport(EJDB *jb, const char *path, TCLIST *cnames, int flags, TCXSTR *log); - -EJDB_EXPORT bool ejdbcommand(EJDB *jb) - +/** + * Execute ejdb database command. + * + * Supported commands: + * + * EJDBEXPORT: + * Exports database collections data. See ejdbexport() method. + * + * "ejdbexport" : { + * "path" : string, //Exports database collections data + * "cnames" : [string array]|null, //List of collection names to export + * "mode" : int|null //Values: null|`JBJSONEXPORT` See ejdbexport() method + * } + * + * Command response: + * { + * "log" : string, //Diagnostic log about executing this command + * "error" : string|null, //ejdb error message + * "errorCode" : int|0, //ejdb error code + * } + * + * EJDBIMPORT: + * Imports previously exported collections data into ejdb. + * + * "ejdbexport" : { + * "path" : string //The directory path in which data resides + * "cnames" : [string array]|null, //List of collection names to import + * "mode" : int|null //Values: null|`JBIMPORTUPDATE`|`JBIMPORTREPLACE` See ejdbimport() method + * } + * + * Command response: + * { + * "log" : string, //Diagnostic log about executing this command + * "error" : string|null, //ejdb error message + * "errorCode" : int|0, //ejdb error code + * } + * + * @param jb EJDB database handle. + * @param cmd BSON command spec. + * @return Allocated BSON command response object. Caller shoud call `bson_del()` on it. + */ +EJDB_EXPORT bson* ejdbcommand(EJDB *jb, bson *cmd); EJDB_EXTERN_C_END -- 2.7.4