From: Marvin Killing Date: Sun, 14 Jul 2013 16:49:14 +0000 (+0200) Subject: Add README pointing to go bindings repo X-Git-Tag: v1.2.12~276^2~2^2 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=f1de5ffab6363c881e265d474f9f860c6e734e17;p=platform%2Fupstream%2Fejdb.git Add README pointing to go bindings repo Instead of adding the code for the go bindings to this repository it is favourable to put them in their own dedicated repo. This is more compatible with golang's dependency resolution mechanism. I think that adding a README pointing to where the go bindings can be found is a good way to keep an overview of all available bindings in the main repo. --- diff --git a/goejdb/README b/goejdb/README deleted file mode 100644 index bf0cb26..0000000 --- a/goejdb/README +++ /dev/null @@ -1,205 +0,0 @@ -EJDB Go binding -================================== - -One snippet intro ------------------------------------ - -~~~~~~ -package ejdbtutorial - -import ( - "fmt" - "github.com/mkilling/ejdb/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() -} -~~~~~~ - -You can save this code in `ejdbtutorial.go` And build: - - -```sh -go build ejdbtutorial.go -./ejdbtutorial -``` - -Building & Installation --------------------------------- - -[Installing on Debian/Ubuntu](https://github.com/Softmotions/ejdb/wiki/Debian-Ubuntu-installation) - -Manual installation -------------------------------- - -### Prerequisites -**System libraries:** - -* Google Go -* labix.org/v2/mgo/bson (go get labix.org/v2/mgo/bson) -* tcejdb (see tcejdb/ in this repository) - -### Build and install - -~~~~~~ - go get github.com/mkilling/ejdb/goejdb -~~~~~~ - -Queries ---------------------------------- - -~~~~~~ -/** - * Create query object. - * Sucessfully created queries must be destroyed with ejdbquerydel(). - * - * EJDB queries inspired by MongoDB (mongodb.org) and follows same philosophy. - * - * - Supported queries: - * - Simple matching of String OR Number OR Array value: - * - {'fpath' : 'val', ...} - * - $not Negate operation. - * - {'fpath' : {'$not' : val}} //Field not equal to val - * - {'fpath' : {'$not' : {'$begin' : prefix}}} //Field not begins with val - * - $begin String starts with prefix - * - {'fpath' : {'$begin' : prefix}} - * - $gt, $gte (>, >=) and $lt, $lte for number types: - * - {'fpath' : {'$gt' : number}, ...} - * - $bt Between for number types: - * - {'fpath' : {'$bt' : [num1, num2]}} - * - $in String OR Number OR Array val matches to value in specified array: - * - {'fpath' : {'$in' : [val1, val2, val3]}} - * - $nin - Not IN - * - $strand String tokens OR String array val matches all tokens in specified array: - * - {'fpath' : {'$strand' : [val1, val2, val3]}} - * - $stror String tokens OR String array val matches any token in specified array: - * - {'fpath' : {'$stror' : [val1, val2, val3]}} - * - $exists Field existence matching: - * - {'fpath' : {'$exists' : true|false}} - * - $icase Case insensitive string matching: - * - {'fpath' : {'$icase' : 'val1'}} //icase matching - * Ignore case matching with '$in' operation: - * - {'name' : {'$icase' : {'$in' : ['tHéâtre - театр', 'heLLo WorlD']}}} - * For case insensitive matching you can create special index of type: `JBIDXISTR` - * - $elemMatch The $elemMatch operator matches more than one component within an array element. - * - { array: { $elemMatch: { value1 : 1, value2 : { $gt: 1 } } } } - * Restriction: only one $elemMatch allowed in context of one array field. - * - * - Queries can be used to update records: - * $set Field set operation. - * - {.., '$set' : {'field1' : val1, 'fieldN' : valN}} - * $upsert Atomic upsert. If matching records are found it will be '$set' operation, - * otherwise new record will be inserted with fields specified by argment object. - * - {.., '$upsert' : {'field1' : val1, 'fieldN' : valN}} - * $inc Increment operation. Only number types are supported. - * - {.., '$inc' : {'field1' : number, ..., 'field1' : number} - * $dropall In-place record removal operation. - * - {.., '$dropall' : true} - * $addToSet Atomically adds value to the array only if its not in the array already. - * If containing array is missing it will be created. - * - {.., '$addToSet' : {'fpath' : val1, 'fpathN' : valN, ...}} - * $addToSetAll Batch version if $addToSet - * - {.., '$addToSetAll' : {'fpath' : [array of values to add], ...}} - * $pull Atomically removes all occurrences of value from field, if field is an array. - * - {.., '$pull' : {'fpath' : val1, 'fpathN' : valN, ...}} - * $pullAll Batch version of $pull - * - {.., '$pullAll' : {'fpath' : [array of values to remove], ...}} - * - * NOTE: Negate operations: $not and $nin not using indexes - * so they can be slow in comparison to other matching operations. - * - * NOTE: Only one index can be used in search query operation. - * - * QUERY HINTS (specified by `hints` argument): - * - $max Maximum number in the result set - * - $skip Number of skipped results in the result set - * - $orderby Sorting order of query fields. - * - $fields Set subset of fetched fields - If a field presented in $orderby clause it will be forced to include in resulting records. - * Example: - * hints: { - * "$orderby" : { //ORDER BY field1 ASC, field2 DESC - * "field1" : 1, - * "field2" : -1 - * }, - * "$fields" : { //SELECT ONLY {_id, field1, field2} - * "field1" : 1, - * "field2" : 1 - * } - * } - * - * Many query examples can be found in `testejdb/t2.c` test case. - * - * @param EJDB database handle. - * @param qobj Main BSON query object. - * @param orqobjs Array of additional OR query objects (joined with OR predicate). - * @param orqobjsnum Number of OR query objects. - * @param hints BSON object with query hints. - * @return On success return query handle. On error returns NULL. - */ -EJDB_EXPORT EJQ* ejdbcreatequery(EJDB *jb, bson *qobj, bson *orqobjs, int orqobjsnum, bson *hints); -~~~~~~ - -Basic EJDB architecture ------------------------------------- -**EJDB database files structure** - -~~~~~~ -. -├── -├── _ -├── ... -├── _ -└── __. -~~~~~~ - -Where - -* `````` - name of database. It is metadata DB. -* `````` - name of collection. Collection database. -* `````` - JSON field path used in index -* `````` - Collection index extension: - * ```.lex``` String index - * ```.dec``` Number index - * ```.tok``` Array index - -Limitations ------------------------------------- -* One ejdb database can handle up to 1024 collections. -* Indexes for objects in nested arrays currently not supported (#37) - -Related software ------------------------------------- -[Connect session store backed by EJDB database](https://github.com/Softmotions/connect-session-ejdb) - - diff --git a/goejdb/README.md b/goejdb/README.md new file mode 100644 index 0000000..d4c0190 --- /dev/null +++ b/goejdb/README.md @@ -0,0 +1,16 @@ +EJDB Go binding +================================== + +The EJDB Go bindings can be found at [https://github.com/mkilling/goejdb](https://github.com/mkilling/goejdb) + +Installation +------------------------------- + +### Prerequisites + +* Google Go +* installed tcejdb (see ../tcejdb/ in this repository) + +### Install + + go get github.com/mkilling/goejdb \ No newline at end of file diff --git a/goejdb/src/github.com/mkilling/ejdb/goejdb/ejcoll.go b/goejdb/src/github.com/mkilling/ejdb/goejdb/ejcoll.go deleted file mode 100644 index 85ecdba..0000000 --- a/goejdb/src/github.com/mkilling/ejdb/goejdb/ejcoll.go +++ /dev/null @@ -1,215 +0,0 @@ -package goejdb - -// #cgo LDFLAGS: -ltcejdb -// #include "../../../../../../tcejdb/ejdb.h" -import "C" - -import "unsafe" - -// Index modes, index types. -const ( - // Drop index. - JBIDXDROP = C.JBIDXDROP - // Drop index for all types. - JBIDXDROPALL = C.JBIDXDROPALL - // Optimize indexes. - JBIDXOP = C.JBIDXOP - // Rebuild index. - JBIDXREBLD = C.JBIDXREBLD - // Number index. - JBIDXNUM = C.JBIDXNUM - // String index.*/ - JBIDXSTR = C.JBIDXSTR - // Array token index. - JBIDXARR = C.JBIDXARR - // Case insensitive string index - JBIDXISTR = C.JBIDXISTR -) - -// An EJDB collection -type EjColl struct { - ptr *[0]byte - ejdb *Ejdb -} - -// EJDB collection tuning options -type EjCollOpts struct { - // Large collection. It can be larger than 2GB. Default false - large bool - // Collection records will be compressed with DEFLATE compression. Default: false - compressed bool - // Expected records number in the collection. Default: 128K - records int - // Maximum number of cached records. Default: 0 - cachedrecords int -} - -func (coll *EjColl) save_c_bson(c_bson *C.bson) (string, *EjdbError) { - var c_oid C.bson_oid_t - C.ejdbsavebson(coll.ptr, c_bson, &c_oid) - return bson_oid_to_string(&c_oid), coll.ejdb.check_error() -} - -func (coll *EjColl) SaveBson(bsdata []byte) (string, *EjdbError) { - c_bson := bson_from_byte_slice(bsdata) - defer C.bson_destroy(c_bson) - return coll.save_c_bson(c_bson) -} - -func (coll *EjColl) SaveJson(j string) (string, *EjdbError) { - c_bson := bson_from_json(j) - defer C.bson_destroy(c_bson) - return coll.save_c_bson(c_bson) -} - -// EJDB_EXPORT bool ejdbrmbson(EJCOLL *coll, bson_oid_t *oid); -func (coll *EjColl) RmBson(oid string) bool { - c_oid := bson_oid_from_string(&oid) - ret := C.ejdbrmbson(coll.ptr, c_oid) - coll.ejdb.check_error() - return bool(ret) -} - -// EJDB_EXPORT bson* ejdbloadbson(EJCOLL *coll, const bson_oid_t *oid); -func (coll *EjColl) LoadBson(oid string) []byte { - c_oid := bson_oid_from_string(&oid) - bson := C.ejdbloadbson(coll.ptr, c_oid) - defer C.bson_del(bson) - coll.ejdb.check_error() - - return bson_to_byte_slice(bson) -} - -// Execute a query specified by JSON strings query, queries and return the results as a slice of BSON objects -// See the documentation of EjQuery for a description of the query format. -func (coll *EjColl) Find(query string, queries ...string) ([][]byte, *EjdbError) { - q, err := coll.ejdb.CreateQuery(query, queries...) - defer q.Del() - if err != nil { - return nil, err - } else { - return q.Execute(coll) - } -} - -// Execute a query specified by JSON strings query, queries and return only the first result as a BSON object -// See the documentation of EjQuery for a description of the query format. -func (coll *EjColl) FindOne(query string, queries ...string) (*[]byte, *EjdbError) { - q, err := coll.ejdb.CreateQuery(query, queries...) - defer q.Del() - if err != nil { - return nil, err - } else { - return q.ExecuteOne(coll) - } -} - -// Execute a query specified by JSON strings query, queries and return the number of results, not the results themselves. -// See the documentation of EjQuery for a description of the query format. -func (coll *EjColl) Count(query string, queries ...string) (int, *EjdbError) { - q, err := coll.ejdb.CreateQuery(query, queries...) - if err != nil { - return 0, err - } - defer q.Del() - return q.Count(coll) -} - -// EJDB_EXPORT uint32_t ejdbupdate(EJCOLL *jcoll, bson *qobj, bson *orqobjs, int orqobjsnum, bson *hints, TCXSTR *log); -func (coll *EjColl) Update(query string, queries ...string) (int, *EjdbError) { - query_bson := bson_from_json(query) - defer C.bson_destroy(query_bson) - - orqueries := C.malloc((C.size_t)(unsafe.Sizeof(new(C.bson))) * C.size_t(len(queries))) - defer C.free(orqueries) - ptr_orqueries := (*[maxslice]*C.bson)(orqueries) - for i, q := range queries { - (*ptr_orqueries)[i] = bson_from_json(q) - defer C.bson_destroy((*ptr_orqueries)[i]) - } - - count := C.ejdbupdate(coll.ptr, query_bson, (*C.bson)(orqueries), C.int(len(queries)), nil, nil) - return int(count), coll.ejdb.check_error() -} - -// Set index for JSON field in EJDB collection. -// -// - Available index types: -// - `JBIDXSTR` String index for JSON string values. -// - `JBIDXISTR` Case insensitive string index for JSON string values. -// - `JBIDXNUM` Index for JSON number values. -// - `JBIDXARR` Token index for JSON arrays and string values. -// -// - One JSON field can have several indexes for different types. -// -// - Available index operations: -// - `JBIDXDROP` Drop index of specified type. -// - 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. (Optimize the B+ tree index file) -// -// Examples: -// - Set index for JSON path `addressbook.number` for strings and numbers: -// `ccoll.SetIndex("album.number", JBIDXSTR | JBIDXNUM)` -// - Set index for array: -// `ccoll.SetIndex("album.tags", JBIDXARR)` -// - Rebuild previous index: -// `ccoll.SetIndex("album.tags", JBIDXARR | JBIDXREBLD)` -func (coll *EjColl) SetIndex(ipath string, flags int) *EjdbError { - c_ipath := C.CString(ipath) - defer C.free(unsafe.Pointer(c_ipath)) - res := C.ejdbsetindex(coll.ptr, c_ipath, C.int(flags)) - if res { - return nil - } else { - return coll.ejdb.check_error() - } -} - -// Begin transaction for EJDB collection. -func (coll *EjColl) BeginTransaction() *EjdbError { - res := C.ejdbtranbegin(coll.ptr) - if res { - return nil - } else { - return coll.ejdb.check_error() - } -} - -// Commit transaction for EJDB collection. -func (coll *EjColl) CommitTransaction() *EjdbError { - res := C.ejdbtrancommit(coll.ptr) - if res { - return nil - } else { - return coll.ejdb.check_error() - } -} - -// Abort transaction for EJDB collection. -func (coll *EjColl) AbortTransaction() *EjdbError { - res := C.ejdbtranabort(coll.ptr) - if res { - return nil - } else { - return coll.ejdb.check_error() - } -} - -// Get current transaction status. Return true if a transaction is active, false otherwise. -func (coll *EjColl) IsTransactionActive() bool { - var ret C.bool - C.ejdbtranstatus(coll.ptr, &ret) - return bool(ret) -} - -// Synchronize content of a EJDB collection database with the file on device. On success return true. -func (coll *EjColl) Sync() (bool, *EjdbError) { - ret := C.ejdbsyncoll(coll.ptr) - if ret { - return bool(ret), nil - } else { - return bool(ret), coll.ejdb.check_error() - } -} diff --git a/goejdb/src/github.com/mkilling/ejdb/goejdb/ejdb.go b/goejdb/src/github.com/mkilling/ejdb/goejdb/ejdb.go deleted file mode 100644 index c9f5dcc..0000000 --- a/goejdb/src/github.com/mkilling/ejdb/goejdb/ejdb.go +++ /dev/null @@ -1,235 +0,0 @@ -package goejdb - -// #cgo LDFLAGS: -ltcejdb -// #include "../../../../../../tcejdb/ejdb.h" -import "C" - -import ( - "errors" - "fmt" - "unsafe" -) - -// Database open modes -const ( - // Open as a reader. - JBOREADER = C.JBOREADER - // Open as a writer. - JBOWRITER = C.JBOWRITER - // Create if db file not exists. - JBOCREAT = C.JBOCREAT - // Truncate db on open. - JBOTRUNC = C.JBOTRUNC - // Open without locking. - JBONOLCK = C.JBONOLCK - // Lock without blocking. - JBOLCKNB = C.JBOLCKNB - // Synchronize every transaction. - JBOTSYNC = C.JBOTSYNC -) - -// Error codes -const ( - // Invalid collection name. - JBEINVALIDCOLNAME = C.JBEINVALIDCOLNAME - // Invalid bson object. - JBEINVALIDBSON = C.JBEINVALIDBSON - // Invalid bson object id. - JBEINVALIDBSONPK = C.JBEINVALIDBSONPK - // Invalid query control field starting with '$'. - JBEQINVALIDQCONTROL = C.JBEQINVALIDQCONTROL - // $strand, $stror, $in, $nin, $bt keys requires not empty array value. - JBEQINOPNOTARRAY = C.JBEQINOPNOTARRAY - // Inconsistent database metadata. - JBEMETANVALID = C.JBEMETANVALID - // Invalid field path value. - JBEFPATHINVALID = C.JBEFPATHINVALID - // Invalid query regexp value. - JBEQINVALIDQRX = C.JBEQINVALIDQRX - // Result set sorting error. - JBEQRSSORTING = C.JBEQRSSORTING - // Query generic error. - JBEQERROR = C.JBEQERROR - // Updating failed. - JBEQUPDFAILED = C.JBEQUPDFAILED - // Only one $elemMatch allowed in the fieldpath. - JBEQONEEMATCH = C.JBEQONEEMATCH - // $fields hint cannot mix include and exclude fields - JBEQINCEXCL = C.JBEQINCEXCL - // action key in $do block can only be one of: $join - JBEQACTKEY = C.JBEQACTKEY - // Exceeded the maximum number of collections per database - JBEMAXNUMCOLS = C.JBEMAXNUMCOLS -) - -const maxslice = 1<<31 - 1 - -// An EJDB database -type Ejdb struct { - ptr *[0]byte -} - -type EjdbError struct { - // Error code returned by EJDB - ErrorCode int - error -} - -func new_ejdb() *Ejdb { - ejdb := new(Ejdb) - ejdb.ptr = C.ejdbnew() - if ejdb.ptr == nil { - return nil - } else { - return ejdb - } -} - -// Returns EJDB library version string. Eg: "1.1.13" -func Version() string { - cs := C.ejdbversion() - return C.GoString(cs) -} - -// Return true if passed `oid` string cat be converted to valid 12 bit BSON object identifier (OID). -func IsValidOidStr(oid string) bool { - c_oid := C.CString(oid) - res := C.ejdbisvalidoidstr(c_oid) - C.free(unsafe.Pointer(c_oid)) - - return bool(res) -} - -// Returns a new open EJDB database. -// path is the path to the database file. -// options specify the open mode bitmask flags. -func Open(path string, options int) (*Ejdb, *EjdbError) { - ejdb := new_ejdb() - if ejdb != nil { - c_path := C.CString(path) - defer C.free(unsafe.Pointer(c_path)) - C.ejdbopen(ejdb.ptr, c_path, C.int(options)) - } - - return ejdb, ejdb.check_error() -} - -func (ejdb *Ejdb) check_error() *EjdbError { - ecode := C.ejdbecode(ejdb.ptr) - if ecode == 0 { - return nil - } - c_msg := C.ejdberrmsg(ecode) - msg := C.GoString(c_msg) - return &EjdbError{int(ecode), errors.New(fmt.Sprintf("EJDB error: %v", msg))} -} - -// Return true if database is in open state, false otherwise -func (ejdb *Ejdb) IsOpen() bool { - ret := C.ejdbisopen(ejdb.ptr) - return bool(ret) -} - -// Delete database object. If the database is not closed, it is closed implicitly. -// Note that the deleted object and its derivatives can not be used anymore -func (ejdb *Ejdb) Del() { - C.ejdbdel(ejdb.ptr) -} - -// Close a table database object. If a writer opens a database but does not close it appropriately, the database will be broken. -// If successful return true, otherwise return false. -func (ejdb *Ejdb) Close() *EjdbError { - C.ejdbclose(ejdb.ptr) - return ejdb.check_error() -} - -// Retrieve collection handle for collection specified `collname`. -// If collection with specified name does't exists it will return nil. -func (ejdb *Ejdb) GetColl(colname string) (*EjColl, *EjdbError) { - c_colname := C.CString(colname) - defer C.free(unsafe.Pointer(c_colname)) - - ejcoll := new(EjColl) - ejcoll.ejdb = ejdb - ejcoll.ptr = C.ejdbgetcoll(ejdb.ptr, c_colname) - - return ejcoll, ejdb.check_error() -} - -// Return a slice containing shallow copies of all collection handles (EjColl) currently open. -func (ejdb *Ejdb) GetColls() ([]*EjColl, *EjdbError) { - ret := make([]*EjColl, 0) - lst := C.ejdbgetcolls(ejdb.ptr) - if lst == nil { - return ret, ejdb.check_error() - } - - for i := int(lst.start); i < int(lst.start)+int(lst.num); i++ { - ptr := uintptr(unsafe.Pointer(lst.array)) + unsafe.Sizeof(C.TCLISTDATUM{})*uintptr(i) - datum := (*C.TCLISTDATUM)(unsafe.Pointer(ptr)) - datum_ptr := unsafe.Pointer(datum.ptr) - ret = append(ret, &EjColl{(*[0]byte)(datum_ptr), ejdb}) - } - return ret, nil -} - -// Same as GetColl() but automatically creates new collection if it doesn't exists. -func (ejdb *Ejdb) CreateColl(colname string, opts *EjCollOpts) (*EjColl, *EjdbError) { - c_colname := C.CString(colname) - defer C.free(unsafe.Pointer(c_colname)) - - ret := new(EjColl) - ret.ejdb = ejdb - - if opts != nil { - var c_opts C.EJCOLLOPTS - c_opts.large = C._Bool(opts.large) - c_opts.compressed = C._Bool(opts.large) - c_opts.records = C.int64_t(opts.records) - c_opts.cachedrecords = C.int(opts.cachedrecords) - ret.ptr = C.ejdbcreatecoll(ejdb.ptr, c_colname, &c_opts) - } else { - ret.ptr = C.ejdbcreatecoll(ejdb.ptr, c_colname, nil) - } - - if ret.ptr != nil { - return ret, nil - } else { - return nil, ejdb.check_error() - } -} - -// Removes collections specified by `colname`. -// If `unlinkfile` is true the collection db file and all of its index files will be removed. -// If removal was successful return true, otherwise return false. -func (ejdb *Ejdb) RmColl(colname string, unlinkfile bool) (bool, *EjdbError) { - c_colname := C.CString(colname) - defer C.free(unsafe.Pointer(c_colname)) - res := C.ejdbrmcoll(ejdb.ptr, c_colname, C._Bool(unlinkfile)) - if res { - return bool(res), nil - } else { - return bool(res), ejdb.check_error() - } -} - -// Synchronize entire EJDB database and all of its collections with storage. -func (ejdb *Ejdb) Sync() (bool, *EjdbError) { - ret := C.ejdbsyncdb(ejdb.ptr) - if ret { - return bool(ret), nil - } else { - return bool(ret), ejdb.check_error() - } -} - -// Gets description of EJDB database and its collections as a BSON object. -func (ejdb *Ejdb) Meta() ([]byte, *EjdbError) { - bson := C.ejdbmeta(ejdb.ptr) - err := ejdb.check_error() - if err != nil { - return make([]byte, 0), err - } - defer C.bson_del(bson) - return bson_to_byte_slice(bson), nil -} diff --git a/goejdb/src/github.com/mkilling/ejdb/goejdb/ejdb_test.go b/goejdb/src/github.com/mkilling/ejdb/goejdb/ejdb_test.go deleted file mode 100644 index f3253a4..0000000 --- a/goejdb/src/github.com/mkilling/ejdb/goejdb/ejdb_test.go +++ /dev/null @@ -1,423 +0,0 @@ -package goejdb - -import ( - "fmt" - "labix.org/v2/mgo/bson" - "sync" - "testing" - "os" -) - -type TestType struct { - I int - S string - M map[string]string -} - -func make_test_type() TestType { - return TestType{I: 5, S: "mystring", M: map[string]string{"1": "one", "2": "two"}} -} - -var i int = 0 -var iMu sync.Mutex - -func open() *Ejdb { - iMu.Lock() - i++ - ejdb, _ := Open(fmt.Sprintf("/tmp/my%d.ejdb", i), JBOWRITER|JBOCREAT|JBOTRUNC) - iMu.Unlock() - return ejdb -} - -func TestVersion(t *testing.T) { - Version() -} - -func TestIsValidOidStr(t *testing.T) { - if !IsValidOidStr("4fc62a0f4c114f273c000001") { - t.Errorf("4fc62a0f4c114f273c000001 not recognized as a valid oid str though it should be") - } - - if IsValidOidStr("invalidoid") { - t.Errorf("invalidoid recognized as a valid oid str though it should not be") - } -} - -func TestOpen(t *testing.T) { - ejdb := open() - if ejdb == nil || !ejdb.IsOpen() { - t.Errorf("Opening EJDB failed") - } -} - -func TestDel(t *testing.T) { - ejdb := open() - ejdb.Del() -} - -func TestClose(t *testing.T) { - ejdb := open() - ejdb.Close() - if ejdb.IsOpen() { - t.Errorf("Closing EJDB failed") - } -} - -func TestCreateColl(t *testing.T) { - ejdb := open() - coll, err := ejdb.CreateColl("MyNewColl", nil) - if err != nil { - t.Errorf("CreateColl() failed with %v", err) - } - if coll == nil { - t.Errorf("CreateColl() returned nil") - } -} - -func TestCreateCollWithOptions(t *testing.T) { - ejdb := open() - coll, err := ejdb.CreateColl("MyNewColl", &EjCollOpts{large: true, compressed: true, records: 1280000, cachedrecords: 0}) - if err != nil { - t.Errorf("CreateColl() failed with %v", err) - } - if coll == nil { - t.Errorf("CreateColl() returned nil") - } -} - -func TestGetColl(t *testing.T) { - ejdb := open() - ejdb.CreateColl("MyNewColl", nil) - coll, err := ejdb.GetColl("MyNewColl") - if err != nil { - t.Errorf("GetColl() failed with %v", err) - } - if coll == nil { - t.Errorf("GetColl() returned nil") - } -} - -func TestGetColls(t *testing.T) { - ejdb := open() - ejdb.CreateColl("MyNewColl", nil) - ejdb.CreateColl("MyNewColl2", nil) - - colls, err := ejdb.GetColls() - if err != nil { - t.Errorf("GetColls() failed with %v", err) - } - if len(colls) != 2 { - t.Errorf("expected GetColls() to return 2 collections, got %d", len(colls)) - } -} - -func TestRmColl(t *testing.T) { - ejdb := open() - ejdb.CreateColl("MyNewColl", nil) - ret, err := ejdb.RmColl("MyNewColl", true) - if err != nil { - t.Errorf("RmColl() failed with %v", err) - } - if ret == false { - t.Errorf("RmColl() returned false though collection did exist and it should have returned true") - } -} - -func TestSaveBson(t *testing.T) { - bytes, _ := bson.Marshal(make_test_type()) - - ejdb := open() - coll, _ := ejdb.CreateColl("MyNewColl", nil) - oid, _ := coll.SaveBson(bytes) - if oid == "" { - t.Errorf("SaveBson returned empty string instead of valid BSON OID") - } -} - -func TestLoadBson(t *testing.T) { - bytes, _ := bson.Marshal(make_test_type()) - - ejdb := open() - coll, _ := ejdb.CreateColl("MyNewColl", nil) - oid, _ := coll.SaveBson(bytes) - - loaded := coll.LoadBson(oid) - var out TestType - bson.Unmarshal(loaded, &out) - - if out.I != 5 { - t.Errorf("Unmarshalling int failed: expected 5 but was %v", out.I); - } - if out.S != "mystring" { - t.Errorf("Unmarshalling string failed: expected \"mystring\" but was %v", out.S); - } - if out.M["2"] != "two" { - t.Errorf("Unmarshalling map[string]string failed: expected entry [\"2\"] to be \"two\" but was %v", out.M["2"]); - } -} - -func TestRmBson(t *testing.T) { - bytes, _ := bson.Marshal(make_test_type()) - - ejdb := open() - coll, _ := ejdb.CreateColl("MyNewColl", nil) - oid, _ := coll.SaveBson(bytes) - coll.RmBson(oid) -} - -func TestSyncColl(t *testing.T) { - bytes, _ := bson.Marshal(make_test_type()) - - ejdb := open() - coll, _ := ejdb.CreateColl("MyNewColl", nil) - coll.SaveBson(bytes) - ret, err := coll.Sync() - if err != nil { - t.Errorf("Collection.Sync() failed with %v", err) - } - if ret == false { - t.Errorf("Collection.Sync() should have returned true but returned false") - } -} - -func TestSyncEjdb(t *testing.T) { - ejdb := open() - ejdb.CreateColl("MyNewColl", nil) - ret, err := ejdb.Sync() - if err != nil { - t.Errorf("Ejdb.Sync() failed with %v", err) - } - if ret == false { - t.Errorf("Ejdb.Sync() should have returned true but returned false") - } -} - -func TestEjdbMeta(t *testing.T) { - ejdb := open() - var m map[string]interface{} - bs, _ := ejdb.Meta() - bson.Unmarshal(bs, &m) - if m["file"] == nil { - t.Errorf("Metadata seems to be invalid, does not have an entry for [\"file\"]") - } -} - -func TestFind(t *testing.T) { - bytes, _ := bson.Marshal(make_test_type()) - - ejdb := open() - coll, _ := ejdb.CreateColl("MyNewColl", nil) - coll.SaveBson(bytes) - - res1, err := coll.Find(`{"s" : "mystring"}`) - if err != nil { - t.Errorf("Find() failed with %v", err) - } - if len(res1) != 1 { - fmt.Println(res1) - t.Errorf("Find() did not find the right amount of entries. Expected 1 but got %v", len(res1)) - } -} - -func TestFindShouldReturnEmptySliceOnNoResults(t *testing.T) { - ejdb := open() - coll, _ := ejdb.CreateColl("MyNewColl", nil) - - res1, err := coll.Find(`{"s" : "mystring"}`) - if err != nil { - t.Errorf("Find() failed with %v", err) - } - if len(res1) != 0 { - fmt.Println(res1) - t.Errorf("Find() did not find the right amount of entries. Expected 0 but got %v", len(res1)) - } -} - -func TestCount(t *testing.T) { - bytes, _ := bson.Marshal(make_test_type()) - - ejdb := open() - coll, _ := ejdb.CreateColl("MyNewColl", nil) - coll.SaveBson(bytes) - coll.SaveBson(bytes) - - ct, err := coll.Count(`{"s" : "mystring"}`) - if err != nil { - t.Errorf("Count() failed with %v", err) - } - if ct != 2 { - t.Errorf("Count() did not return the right result. Expected 2 but got %v", ct) - } -} - -func TestFindOneShouldReturnResult(t *testing.T) { - bytes, _ := bson.Marshal(make_test_type()) - - ejdb := open() - coll, _ := ejdb.CreateColl("MyNewColl", nil) - coll.SaveBson(bytes) - coll.SaveBson(bytes) - - ret, err := coll.FindOne(`{"s" : "mystring"}`) - if err != nil { - t.Errorf("FindOne() failed with %v", err) - } - if ret == nil { - t.Fatalf("FindOne() failed. Returned nil though there should be a result.") - } - var tt TestType - bson.Unmarshal(*ret, &tt) - if tt.S != "mystring" { - t.Errorf("FindOne() failed. Expected result to have entry [\"s\"] set to \"mystring\" but was \"%s\".", tt.S) - } -} - -func TestFindOneShouldReturnNilOnNoResult(t *testing.T) { - ejdb := open() - coll, _ := ejdb.CreateColl("MyNewColl", nil) - - ret, err := coll.FindOne(`{"s" : "mystring"}`) - if err != nil { - t.Errorf("FindOne() failed with %v", err) - } - if ret != nil { - t.Fatalf("FindOne() failed. Returned non-nil result though it should have found nothing and returned nil.") - } -} - -func TestUpdate(t *testing.T) { - bytes, _ := bson.Marshal(make_test_type()) - - ejdb := open() - coll, _ := ejdb.CreateColl("MyNewColl", nil) - coll.SaveBson(bytes) - coll.SaveBson(bytes) - - ct, err := coll.Update(`{"$inc": {"i": 5}}`) - if err != nil { - t.Errorf("Update() failed with %v", err) - } - if ct != 2 { - t.Errorf("Update() returned the wrong count. Expected 2 but got %d", ct) - } - - // check the values after updating - res, _ := coll.Find(`{}`) - for _, r := range res { - var tt TestType - bson.Unmarshal(r, &tt) - if tt.I != 10 { - t.Errorf("Update() failed. Expected all \"I\" entries to have been updated to 10 but this one was %v", tt.I) - } - } -} - -func TestSetIndex(t *testing.T) { - ejdb := open() - coll, _ := ejdb.CreateColl("MyNewColl", nil) - for i := 0; i < 10; i++ { - coll.SaveJson(fmt.Sprintf(`{"i": %d, "s": "number%d"`, i)) - } - - err1 := coll.SetIndex("i", JBIDXNUM) - if err1 != nil { - t.Errorf("SetIndex() with JBIDXNUM failed with %v", err1) - } - err2 := coll.SetIndex("s", JBIDXSTR) - if err2 != nil { - t.Errorf("SetIndex() with JBIDXSTR failed with %v", err2) - } -} - -func TestTransactions(t *testing.T) { - bytes, _ := bson.Marshal(make_test_type()) - - ejdb := open() - coll, _ := ejdb.CreateColl("MyNewColl", nil) - - coll.BeginTransaction() - coll.SaveBson(bytes) - coll.SaveBson(bytes) - coll.AbortTransaction() - - ct, _ := coll.Count(`{}`) - if ct != 0 { - t.Errorf("transaction not aborted successfully") - } - - if coll.IsTransactionActive() == true { - t.Errorf("IsTransactionActive() returned true though it should be false") - } - - coll.BeginTransaction() - coll.SaveBson(bytes) - coll.SaveBson(bytes) - - if coll.IsTransactionActive() == false { - t.Errorf("IsTransactionActive() returned false though it should be true") - } - - coll.CommitTransaction() - - ct2, _ := coll.Count(`{}`) - if ct2 != 2 { - t.Errorf("transaction not committed successfully") - } -} - -func TestOneSnippetIntroFromReadme(t *testing.T) { - // Create a new database file and open it - jb, err := Open("/tmp/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() -} - -// long-running benchmarks to identify possible memory leaks - -func BenchmarkSavingAndCounting(b *testing.B) { - ejdb, _ := Open("/tmp/mybench.ejdb", JBOWRITER|JBOCREAT|JBOTRUNC|JBOTSYNC) - coll, _ := ejdb.CreateColl("BenchmarkCollection", nil) - bytes, _ := bson.Marshal(make_test_type()) - - for i := 0; i < 100000; i++ { - coll.SaveBson(bytes) - } - - coll.Count(`{"s" : "mystring"}`) - - ejdb.Close() -} - -func BenchmarkCounting(b *testing.B) { - ejdb, _ := Open("/tmp/mybench2.ejdb", JBOWRITER|JBOCREAT|JBOTRUNC|JBOTSYNC) - coll, _ := ejdb.CreateColl("BenchmarkCollection", nil) - bytes, _ := bson.Marshal(make_test_type()) - coll.SaveBson(bytes) - coll.Count(`{"s" : "mystring"}`) - ejdb.Close() -} diff --git a/goejdb/src/github.com/mkilling/ejdb/goejdb/ejquery.go b/goejdb/src/github.com/mkilling/ejdb/goejdb/ejquery.go deleted file mode 100644 index adce729..0000000 --- a/goejdb/src/github.com/mkilling/ejdb/goejdb/ejquery.go +++ /dev/null @@ -1,187 +0,0 @@ -package goejdb - -// #cgo LDFLAGS: -ltcejdb -// #include "../../../../../../tcejdb/ejdb.h" -import "C" - -import "unsafe" - -// Query search mode flags in ejdbqryexecute() -const ( - // Query only count(*) - jbqrycount = C.JBQRYCOUNT - // Fetch first record only - jbqryfindone = C.JBQRYFINDONE -) - -// An EJDB query -type EjQuery struct { - ptr *[0]byte - ejdb *Ejdb -} - -// Create query object. -// Sucessfully created queries must be destroyed with Query.Del(). -// -// EJDB queries inspired by MongoDB (mongodb.org) and follows same philosophy. -// -// - Supported queries: -// - Simple matching of String OR Number OR Array value: -// - {'fpath' : 'val', ...} -// - $not Negate operation. -// - {'fpath' : {'$not' : val}} //Field not equal to val -// - {'fpath' : {'$not' : {'$begin' : prefix}}} //Field not begins with val -// - $begin String starts with prefix -// - {'fpath' : {'$begin' : prefix}} -// - $gt, $gte (>, >=) and $lt, $lte for number types: -// - {'fpath' : {'$gt' : number}, ...} -// - $bt Between for number types: -// - {'fpath' : {'$bt' : [num1, num2]}} -// - $in String OR Number OR Array val matches to value in specified array: -// - {'fpath' : {'$in' : [val1, val2, val3]}} -// - $nin - Not IN -// - $strand String tokens OR String array val matches all tokens in specified array: -// - {'fpath' : {'$strand' : [val1, val2, val3]}} -// - $stror String tokens OR String array val matches any token in specified array: -// - {'fpath' : {'$stror' : [val1, val2, val3]}} -// - $exists Field existence matching: -// - {'fpath' : {'$exists' : true|false}} -// - $icase Case insensitive string matching: -// - {'fpath' : {'$icase' : 'val1'}} //icase matching -// Ignore case matching with '$in' operation: -// - {'name' : {'$icase' : {'$in' : ['théâtre - театр', 'hello world']}}} -// For case insensitive matching you can create special index of type: `JBIDXISTR` -// - $elemMatch The $elemMatch operator matches more than one component within an array element. -// - { array: { $elemMatch: { value1 : 1, value2 : { $gt: 1 } } } } -// Restriction: only one $elemMatch allowed in context of one array field. -// -// - Queries can be used to update records: -// -// $set Field set operation. -// - {.., '$set' : {'field1' : val1, 'fieldN' : valN}} -// $upsert Atomic upsert. If matching records are found it will be '$set' operation, -// otherwise new record will be inserted -// with fields specified by argment object. -// - {.., '$upsert' : {'field1' : val1, 'fieldN' : valN}} -// $inc Increment operation. Only number types are supported. -// - {.., '$inc' : {'field1' : number, ..., 'field1' : number} -// $dropall In-place record removal operation. -// - {.., '$dropall' : true} -// $addToSet Atomically adds value to the array only if its not in the array already. -// If containing array is missing it will be created. -// - {.., '$addToSet' : {'fpath' : val1, 'fpathN' : valN, ...}} -// $addToSetAll Batch version if $addToSet -// - {.., '$addToSetAll' : {'fpath' : [array of values to add], ...}} -// $pull Atomically removes all occurrences of value from field, if field is an array. -// - {.., '$pull' : {'fpath' : val1, 'fpathN' : valN, ...}} -// $pullAll Batch version of $pull -// - {.., '$pullAll' : {'fpath' : [array of values to remove], ...}} -// -// - Collection joins supported in the following form: -// -// {..., $do : {fpath : {$join : 'collectionname'}} } -// Where 'fpath' value points to object's OIDs from 'collectionname'. Its value -// can be OID, string representation of OID or array of this pointers. -// -// NOTE: Negate operations: $not and $nin not using indexes -// so they can be slow in comparison to other matching operations. -// -// NOTE: Only one index can be used in search query operation. -func (ejdb *Ejdb) CreateQuery(query string, queries ...string) (*EjQuery, *EjdbError) { - query_bson := bson_from_json(query) - defer C.bson_destroy(query_bson) - - orqueries_count := len(queries) - orqueries := C.malloc((C.size_t)(unsafe.Sizeof(new(C.bson))) * C.size_t(orqueries_count)) - defer C.free(orqueries) - ptr_orqueries := (*[maxslice]*C.bson)(orqueries) - for i, q := range queries { - (*ptr_orqueries)[i] = bson_from_json(q) - defer C.bson_destroy((*ptr_orqueries)[i]) - } - - q := C.ejdbcreatequery(ejdb.ptr, query_bson, (*C.bson)(orqueries), C.int(len(queries)), nil) - if q == nil { - return nil, ejdb.check_error() - } else { - return &EjQuery{ptr: q, ejdb: ejdb}, nil - } -} - -// Set query hints. `hints` is a JSON string -// - $max Maximum number in the result set -// - $skip Number of skipped results in the result set -// - $orderby Sorting order of query fields. -// - $fields Set subset of fetched fields -// If a field presented in $orderby clause it will be forced to include in resulting records. -// Example: -// hints: { -// "$orderby" : { //ORDER BY field1 ASC, field2 DESC -// "field1" : 1, -// "field2" : -1 -// }, -// "$fields" : { //SELECT ONLY {_id, field1, field2} -// "field1" : 1, -// "field2" : 1 -// } -// } -func (q *EjQuery) SetHints(hints string) *EjdbError { - bsdata := bson_from_json(hints).data - ret := C.ejdbqueryhints(q.ejdb.ptr, q.ptr, unsafe.Pointer(bsdata)) - if ret == nil { - return q.ejdb.check_error() - } else { - return nil - } -} - -// Execute the query and return all results as a slice of BSON objects -func (q *EjQuery) Execute(coll *EjColl) ([][]byte, *EjdbError) { - // execute query - var count C.uint32_t - res := C.ejdbqryexecute(coll.ptr, q.ptr, &count, 0, nil) - defer C.ejdbqresultdispose(res) - err := coll.ejdb.check_error() - - // return results - ret := make([][]byte, 0) - for i := 0; i < int(count); i++ { - var size C.int - bson_blob := C.ejdbqresultbsondata(res, C.int(i), &size) - ret = append(ret, ((*[maxslice]byte)(bson_blob))[:int(size)]) - } - - return ret, err -} - -// Execute the query and return only the first result as a BSON object -func (q *EjQuery) ExecuteOne(coll *EjColl) (*[]byte, *EjdbError) { - // execute query - var count C.uint32_t - res := C.ejdbqryexecute(coll.ptr, q.ptr, &count, jbqryfindone, nil) - defer C.ejdbqresultdispose(res) - err := coll.ejdb.check_error() - - // return results - if count == 0 { - return nil, err - } else { - var size C.int - bson_blob := C.ejdbqresultbsondata(res, 0, &size) - ret := ((*[maxslice]byte)(bson_blob))[:int(size)] - return &ret, err - } -} - -// Execute the query and only return the number of results it returned, not the results themselves -func (q *EjQuery) Count(coll *EjColl) (int, *EjdbError) { - var count C.uint32_t - C.ejdbqryexecute(coll.ptr, q.ptr, &count, jbqrycount, nil) - err := coll.ejdb.check_error() - return int(count), err -} - -// Delete the query. This must be called in order to not leak memory. -func (q *EjQuery) Del() { - C.ejdbquerydel(q.ptr) -} diff --git a/goejdb/src/github.com/mkilling/ejdb/goejdb/util.go b/goejdb/src/github.com/mkilling/ejdb/goejdb/util.go deleted file mode 100644 index 64d380d..0000000 --- a/goejdb/src/github.com/mkilling/ejdb/goejdb/util.go +++ /dev/null @@ -1,67 +0,0 @@ -package goejdb - -// #cgo LDFLAGS: -ltcejdb -// #include "../../../../../../tcejdb/ejdb.h" -import "C" - -import ( - "encoding/json" - "labix.org/v2/mgo/bson" - "unsafe" -) - -func bson_oid_from_string(oid *string) *C.bson_oid_t { - if oid == nil { - return nil - } - - c_oid := C.CString(*oid) - defer C.free(unsafe.Pointer(c_oid)) - - ret := new(C.bson_oid_t) - C.bson_oid_from_string(ret, c_oid) - return ret -} - -func bson_oid_to_string(oid *C.bson_oid_t) string { - var c_str [25]C.char - char_ptr := (*C.char)(unsafe.Pointer(&c_str)) - C.bson_oid_to_string(oid, char_ptr) - return C.GoString(char_ptr) -} - -func bson_to_byte_slice(bson *C.bson) []byte { - size := int(C.bson_size(bson)) - data := C.bson_data(bson) - ptr_data := (*[maxslice]byte)(unsafe.Pointer(data)) - return (*ptr_data)[:size] -} - -func bson_from_byte_slice(bsdata []byte) *C.bson { - c_bson := new(C.bson) - //C.bson_init(c_bson) - - buff := C.malloc(C.size_t(len(bsdata))) - ptr_buff := (*[maxslice]byte)(unsafe.Pointer(buff)) - for i := 0; i < len(bsdata); i++ { - (*ptr_buff)[i] = bsdata[i] - } - - C.bson_init_finished_data(c_bson, (*C.char)(buff)) - return c_bson -} - -func bson_from_json(j string) *C.bson { - var m map[string]interface{} - json.Unmarshal(([]byte)(j), &m) - bytes, _ := bson.Marshal(&m) - return bson_from_byte_slice(bytes) -} - -func bson_destroy(bson *C.bson) { - C.bson_destroy(bson) -} - -func bson_del(bson *C.bson) { - C.bson_del(bson) -}