)
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()
}
~~~~~~
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
}
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()
}
}
+// 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()
}
}
+// 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 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))
}
}
-// EJDB_EXPORT bool ejdbtranbegin(EJCOLL *coll);
+// Begin transaction for EJDB collection.
func (coll *EjColl) BeginTransaction() *EjdbError {
res := C.ejdbtranbegin(coll.ptr)
if res {
}
}
-//EJDB_EXPORT bool ejdbtrancommit(EJCOLL *coll);
+// Commit transaction for EJDB collection.
func (coll *EjColl) CommitTransaction() *EjdbError {
res := C.ejdbtrancommit(coll.ptr)
if res {
}
}
-// EJDB_EXPORT bool ejdbtranabort(EJCOLL *coll);
+// Abort transaction for EJDB collection.
func (coll *EjColl) AbortTransaction() *EjdbError {
res := C.ejdbtranabort(coll.ptr)
if res {
}
}
-// 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 {
"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
}
}
}
+// 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)
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 {
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))
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)
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))
}
}
+// 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))
}
}
-// 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 {
}
}
-// 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()
}
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()
}
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)
}
}
-// 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))
}
}
+// 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
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()
}
}
+// 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)
}