From: Marvin Killing Date: Sat, 13 Jul 2013 13:08:20 +0000 (+0200) Subject: Refine documentation X-Git-Tag: v1.2.12~276^2~4^2~1 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=c5c33d75f48fc82fad061feb9286fec42a326fa7;p=platform%2Fupstream%2Fejdb.git Refine documentation --- diff --git a/goejdb/README b/goejdb/README index 838ac9b..6c37f76 100644 --- a/goejdb/README +++ b/goejdb/README @@ -15,32 +15,33 @@ import ( ) 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' + // Get or create collection 'contacts' coll, _ := jb.CreateColl("contacts", nil) - //Insert one record: - //JSON: {'name' : 'Bruce', 'phone' : '333-222-333', 'age' : 58} + // 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 + // 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 + // Now print the result set records for _, bs := range res { var m map[string]interface{} bson.Unmarshal(bs, &m) fmt.Println(m) } - //Close database + // Close database jb.Close() } ~~~~~~ diff --git a/goejdb/src/github.com/mkilling/ejdb/goejdb/ejcoll.go b/goejdb/src/github.com/mkilling/ejdb/goejdb/ejcoll.go index af0a0bd..85ecdba 100644 --- a/goejdb/src/github.com/mkilling/ejdb/goejdb/ejcoll.go +++ b/goejdb/src/github.com/mkilling/ejdb/goejdb/ejcoll.go @@ -6,26 +6,41 @@ 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 } @@ -65,7 +80,8 @@ func (coll *EjColl) LoadBson(oid string) []byte { return bson_to_byte_slice(bson) } -// EJDB_EXPORT EJQRESULT ejdbqryexecute(EJCOLL *jcoll, const EJQ *q, uint32_t *count, int qflags, TCXSTR *log); +// 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() @@ -76,6 +92,8 @@ func (coll *EjColl) Find(query string, queries ...string) ([][]byte, *EjdbError) } } +// 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() @@ -86,6 +104,8 @@ func (coll *EjColl) FindOne(query string, queries ...string) (*[]byte, *EjdbErro } } +// 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 { @@ -112,7 +132,30 @@ func (coll *EjColl) Update(query string, queries ...string) (int, *EjdbError) { return int(count), coll.ejdb.check_error() } -// EJDB_EXPORT bool ejdbsetindex(EJCOLL *coll, const char *ipath, int flags); +// 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)) @@ -124,7 +167,7 @@ func (coll *EjColl) SetIndex(ipath string, flags int) *EjdbError { } } -// EJDB_EXPORT bool ejdbtranbegin(EJCOLL *coll); +// Begin transaction for EJDB collection. func (coll *EjColl) BeginTransaction() *EjdbError { res := C.ejdbtranbegin(coll.ptr) if res { @@ -134,7 +177,7 @@ func (coll *EjColl) BeginTransaction() *EjdbError { } } -//EJDB_EXPORT bool ejdbtrancommit(EJCOLL *coll); +// Commit transaction for EJDB collection. func (coll *EjColl) CommitTransaction() *EjdbError { res := C.ejdbtrancommit(coll.ptr) if res { @@ -144,7 +187,7 @@ func (coll *EjColl) CommitTransaction() *EjdbError { } } -// EJDB_EXPORT bool ejdbtranabort(EJCOLL *coll); +// Abort transaction for EJDB collection. func (coll *EjColl) AbortTransaction() *EjdbError { res := C.ejdbtranabort(coll.ptr) if res { @@ -154,16 +197,14 @@ func (coll *EjColl) AbortTransaction() *EjdbError { } } -// EJDB_EXPORT bool ejdbtranstatus(EJCOLL *jcoll, bool *txactive); +// 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) } -// EJDB_EXPORT void ejdbqresultdispose(EJQRESULT qr); - -// EJDB_EXPORT bool ejdbsyncoll(EJCOLL *jcoll); +// 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 { diff --git a/goejdb/src/github.com/mkilling/ejdb/goejdb/ejdb.go b/goejdb/src/github.com/mkilling/ejdb/goejdb/ejdb.go index 74b4310..c9f5dcc 100644 --- a/goejdb/src/github.com/mkilling/ejdb/goejdb/ejdb.go +++ b/goejdb/src/github.com/mkilling/ejdb/goejdb/ejdb.go @@ -10,23 +10,67 @@ import ( "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 } @@ -41,11 +85,13 @@ func new_ejdb() *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) @@ -54,6 +100,9 @@ func IsValidOidStr(oid string) bool { 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 { @@ -75,20 +124,27 @@ func (ejdb *Ejdb) check_error() *EjdbError { 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)) @@ -100,6 +156,7 @@ func (ejdb *Ejdb) GetColl(colname string) (*EjColl, *EjdbError) { 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) @@ -116,6 +173,7 @@ func (ejdb *Ejdb) GetColls() ([]*EjColl, *EjdbError) { 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)) @@ -141,6 +199,9 @@ func (ejdb *Ejdb) CreateColl(colname string, opts *EjCollOpts) (*EjColl, *EjdbEr } } +// 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)) @@ -152,7 +213,7 @@ func (ejdb *Ejdb) RmColl(colname string, unlinkfile bool) (bool, *EjdbError) { } } -// EJDB_EXPORT bool ejdbsyncdb(EJDB *jb); +// Synchronize entire EJDB database and all of its collections with storage. func (ejdb *Ejdb) Sync() (bool, *EjdbError) { ret := C.ejdbsyncdb(ejdb.ptr) if ret { @@ -162,7 +223,7 @@ func (ejdb *Ejdb) Sync() (bool, *EjdbError) { } } -// EJDB_EXPORT bson* ejdbmeta(EJDB *jb); +// 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() diff --git a/goejdb/src/github.com/mkilling/ejdb/goejdb/ejdb_test.go b/goejdb/src/github.com/mkilling/ejdb/goejdb/ejdb_test.go index f0544be..f3253a4 100644 --- a/goejdb/src/github.com/mkilling/ejdb/goejdb/ejdb_test.go +++ b/goejdb/src/github.com/mkilling/ejdb/goejdb/ejdb_test.go @@ -366,32 +366,34 @@ func TestTransactions(t *testing.T) { } func TestOneSnippetIntroFromReadme(t *testing.T) { - jb, err := Open("addressbook", JBOWRITER | JBOCREAT | JBOTRUNC) + // 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' + + // Get or create collection 'contacts' coll, _ := jb.CreateColl("contacts", nil) - //Insert one record: - //JSON: {'name' : 'Bruce', 'phone' : '333-222-333', 'age' : 58} + // 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 + // 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 + // Now print the result set records for _, bs := range res { var m map[string]interface{} bson.Unmarshal(bs, &m) fmt.Println(m) } - //Close database + // Close database jb.Close() } diff --git a/goejdb/src/github.com/mkilling/ejdb/goejdb/ejquery.go b/goejdb/src/github.com/mkilling/ejdb/goejdb/ejquery.go index a5f85ce..adce729 100644 --- a/goejdb/src/github.com/mkilling/ejdb/goejdb/ejquery.go +++ b/goejdb/src/github.com/mkilling/ejdb/goejdb/ejquery.go @@ -6,17 +6,87 @@ import "C" import "unsafe" +// Query search mode flags in ejdbqryexecute() const ( - JBQRYCOUNT = C.JBQRYCOUNT - JBQRYFINDONE = C.JBQRYFINDONE + // Query only count(*) + jbqrycount = C.JBQRYCOUNT + // Fetch first record only + jbqryfindone = C.JBQRYFINDONE ) +// An EJDB query type EjQuery struct { ptr *[0]byte ejdb *Ejdb } -// EJDB_EXPORT EJQ* ejdbcreatequery(EJDB *jb, bson *qobj, bson *orqobjs, int orqobjsnum, bson *hints); +// 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) @@ -38,7 +108,23 @@ func (ejdb *Ejdb) CreateQuery(query string, queries ...string) (*EjQuery, *EjdbE } } -// EJDB_EXPORT EJQ* ejdbqueryhints(EJDB *jb, EJQ *q, const void *hintsbsdata); +// 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)) @@ -49,6 +135,7 @@ func (q *EjQuery) SetHints(hints string) *EjdbError { } } +// 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 @@ -67,10 +154,11 @@ func (q *EjQuery) Execute(coll *EjColl) ([][]byte, *EjdbError) { 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) + res := C.ejdbqryexecute(coll.ptr, q.ptr, &count, jbqryfindone, nil) defer C.ejdbqresultdispose(res) err := coll.ejdb.check_error() @@ -85,14 +173,15 @@ func (q *EjQuery) ExecuteOne(coll *EjColl) (*[]byte, *EjdbError) { } } +// 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) + C.ejdbqryexecute(coll.ptr, q.ptr, &count, jbqrycount, nil) err := coll.ejdb.check_error() return int(count), err } -// EJDB_EXPORT void ejdbquerydel(EJQ *q); +// Delete the query. This must be called in order to not leak memory. func (q *EjQuery) Del() { C.ejdbquerydel(q.ptr) }