* Boston, MA 02111-1307 USA.
*************************************************************************************************/
+/*
+ * Document-class: EJDB
+ * Main EJDB class that contains all database control methods. Instance should be created by EJDB::open
+ */
+
+
#include <tcejdb/ejdb_private.h>
#include <ruby.h>
return !NIL_P(res) ? res : rb_hash_aref(hash, rb_str_new2(opt));
}
-static int raise_ejdb_error(EJDB *ejdb) {
+int raise_ejdb_error(EJDB *ejdb) {
int ecode = ejdbecode(ejdb);
const char *emsg = ejdberrmsg(ecode);
rb_raise(rb_eRuntimeError, "%s", emsg);
}
-static int nil_or_raise_ejdb_error(EJDB *ejdb) {
+int nil_or_raise_ejdb_error(EJDB *ejdb) {
int ecode = ejdbecode(ejdb);
if (ecode != TCESUCCESS && ecode != TCENOREC) {
raise_ejdb_error(ejdb);
}
+/*
+ * call-seq:
+ * EJDB::open(path, mode) -> EJDB
+ *
+ * Open database. Return database instance handle object. <br/>
+ * Default open mode: JBOWRITER | JBOCREAT .
+ *
+ * - +path+ (String) - database main file name
+ * - +mode+ (Number) - bitmask of open modes:
+ *
+ * [JBOREADER] Open as a reader.
+ * [JBOWRITER] Open as a writer.
+ * [JBOCREAT] Create if db file not exists
+ * [JBOTRUNC] Truncate db.
+ *
+ */
VALUE EJDB_open(VALUE clazz, VALUE path, VALUE mode) {
SafeStringValue(path);
Check_Type(mode, T_FIXNUM);
return ejdbWrap;
}
+/*
+ * call-seq:
+ * ejdb.open? -> true|false
+ *
+ * Check if database in opened state.
+ */
VALUE EJDB_is_open(VALUE self) {
EJDB* ejdb = getEJDB(self);
return ejdb && ejdbisopen(ejdb) ? Qtrue : Qfalse;
}
-void EJDB_close(VALUE self) {
- ejdbclose(getEJDB(self));
-}
-
-void EJDB_drop_collection(int argc, VALUE* argv, VALUE self) {
- VALUE collName;
- VALUE prune;
-
- rb_scan_args(argc, argv, "11", &collName, &prune);
- SafeStringValue(collName);
- EJDB* ejdb = getEJDB(self);
- if (!ejdbrmcoll(ejdb, StringValuePtr(collName), RTEST(prune))) {
- raise_ejdb_error(ejdb);
- }
+/*
+ * call-seq:
+ * ejdb.close -> nil
+ *
+ * Close database.<br/>
+ * If database was not opened it does nothing.
+ */
+VALUE EJDB_close(VALUE self) {
+ ejdbclose(getEJDB(self));
+ return Qnil;
}
-void EJDB_ensure_collection(int argc, VALUE* argv, VALUE self) {
+/*
+ * call-seq:
+ * ejdb.ensure_collection(collName, [copts]) -> nil
+ *
+ * Automatically creates new collection if it does't exists. Collection options +copts+ applied only for newly created collection.<br/>
+ * For existing collections +copts+ takes no effect. <br/>
+ *
+ * Collection options (copts): <br/>
+ * [:cachedrecords] Max number of cached records in shared memory segment. Default: 0
+ * [:records] Estimated number of records in this collection. Default: 65535.
+ * [:large] Specifies that the size of the database can be larger than 2GB. Default: false
+ * [:compressed] If true collection records will be compressed with DEFLATE compression. Default: false.
+ */
+VALUE EJDB_ensure_collection(int argc, VALUE* argv, VALUE self) {
VALUE collName;
VALUE copts;
if (!ejdbcreatecoll(ejdb, StringValuePtr(collName), &jcopts)) {
raise_ejdb_error(ejdb);
}
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * ejdb.drop_collection(collName, [prune=false]) -> nil
+ *
+ * Drop collection.
+ * - +collName+ (String) - name of collection
+ * - +prune+ (true|false) - if true the collection data will be erased from disk.
+ */
+VALUE EJDB_drop_collection(int argc, VALUE* argv, VALUE self) {
+ VALUE collName;
+ VALUE prune;
+
+ rb_scan_args(argc, argv, "11", &collName, &prune);
+ SafeStringValue(collName);
+
+ EJDB* ejdb = getEJDB(self);
+ if (!ejdbrmcoll(ejdb, StringValuePtr(collName), RTEST(prune))) {
+ raise_ejdb_error(ejdb);
+ }
+ return Qnil;
}
+/*
+ * call-seq:
+ * ejdb.save(collName, [obj1, …, objN, merge = false]) -> Array or Number or nil
+ *
+ * Save/update specified hashes or Ruby objects in the collection. If collection with +collName+ does not exists it will be created. <br/>
+ * Each persistent object has unique identifier (OID) placed in the _id property. If a saved object does not have _id it will be autogenerated.
+ * To identify and update object it should contains _id property.<br/><br/>
+ *
+ * NOTE: Field names of passed objects may not contain $ and . characters, error condition will be fired in this case.
+ * - +collName+ (String) - name of collection
+ * - +obj+ (Hash or Object) - zero or more objects to save
+ * - +merge+ (Hash or Object) - if true a saved objects will be merged with who's
+ *
+ * <br/>
+ * Returns:
+ * - nil, if no objects provided in arguments
+ * - oid of saved object, as String if one object provided in arguments
+ * - array of oids, in other case
+ */
VALUE EJDB_save(int argc, VALUE *argv, VALUE self) {
if (argc < 1) {
rb_raise(rb_eArgError, "Error calling EJDB.save(): need to specify collection name");
}
}
+/*
+ * call-seq:
+ * ejdb.load(collName, oid) -> Hash or nil
+ *
+ * Loads JSON object identified by OID from the collection.
+ *
+ * - +collName+ (String) - name of collection
+ * - +oid+ (String) - object identifier (OID)
+ *
+ * <br/>
+ * Returns:
+ * - BSON object as hash
+ * - nil, if it is not found
+ */
VALUE EJDB_load(VALUE self, VALUE collName, VALUE rboid) {
SafeStringValue(collName);
+ SafeStringValue(rboid);
EJDB* ejdb = getEJDB(self);
return bs ? bson_to_ruby_ensure_destroy(bs) : nil_or_raise_ejdb_error(ejdb);
}
+/*
+ * call-seq:
+ * ejdb.remove(collName, oid) -> nil
+ *
+ * Removes JSON object from the collection.
+ *
+ * - +collName+ (String) - name of collection
+ * - +oid+ (String) - object identifier (OID)
+ */
+VALUE EJDB_remove(VALUE self, VALUE collName, VALUE rboid) {
+ SafeStringValue(collName);
+ SafeStringValue(rboid);
+
+ EJDB* ejdb = getEJDB(self);
+
+ EJCOLL *coll = ejdbgetcoll(ejdb, StringValuePtr(collName));
+ if (!coll) {
+ raise_ejdb_error(ejdb);
+ }
+
+ bson_oid_t oid = ruby_to_bson_oid(rboid);
+ if (!ejdbrmbson(coll, &oid)) {
+ raise_ejdb_error(ejdb);
+ }
+ return Qnil;
+}
+
void prepare_query_hint(VALUE res, VALUE hints, char* hint) {
VALUE val = get_hash_option(hints, hint);
prepare_query_hint(res, hints, "orderby");
prepare_query_hint(res, hints, "max");
prepare_query_hint(res, hints, "skip");
+ prepare_query_hint(res, hints, "fields");
return res;
}
EJDB_remove_query_internal(rbquery);
}
+
+/*
+ * call-seq:
+ * ejdb.find(collName, [q = {}, orarr = [], hints = {}]) -> nil
+ *
+ * Execute query on collection. EJDB queries inspired by MongoDB (mongodb.org) and follows same philosophy.
+ * Both in query and in +hints+ strings or symbols may be used as keys (f. e. "fpath" and :fpath are equal).
+ * 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 type of string index.
+ * - $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], ...}}
+ *
+ * <br/>
+ * NOTE: It is better to execute update queries with `$onlycount=true` hint flag
+ * or use the special +update+ method to avoid unnecessarily rows fetching.<br/><br/>
+ *
+ * NOTE: Negate operations: $not and $nin not using indexes
+ * so they can be slow in comparison to other matching operations.<br/><br/>
+ *
+ * NOTE: Only one index can be used in search query operation.<br/><br/>
+ *
+ * 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.
+ * [:onlycount] If `true` only count of matching records will be returned without placing records in result set.
+ * [:fields] Set subset of fetched fields
+ * If a field presented in $orderby clause it will be forced to include in resulting records.<br/>
+ * Example:<br/>
+ * hints: {
+ * "$orderby" : { //ORDER BY field1 ASC, field2 DESC
+ * "field1" : 1,
+ * "field2" : -1
+ * },
+ * "$fields" : { //SELECT ONLY {_id, field1, field2}
+ * "field1" : 1,
+ * "field2" : 1
+ * }
+ * }
+ *
+ * Many C API query examples can be found in `tcejdb/testejdb/t2.c` test case.<br/><br/>
+ *
+ * - +collName+ (String) - name of collection
+ * - +q+ (Hash or Object) - query object. In most cases it will be easier to use hash to specify EJDB queries
+ * - +orarr+ (Array) - array of additional OR query objects (joined with OR predicate). If 3rd argument is not array it
+ * will be recognized as +hints+ argument
+ * - +hints+ (Hash or Object) - object with query hints
+ */
VALUE EJDB_find(int argc, VALUE* argv, VALUE self) {
VALUE collName;
VALUE q;
}
-void EJDB_remove(VALUE self, VALUE collName, VALUE rboid) {
- SafeStringValue(collName);
- SafeStringValue(rboid);
-
- EJDB* ejdb = getEJDB(self);
-
- EJCOLL *coll = ejdbgetcoll(ejdb, StringValuePtr(collName));
- if (!coll) {
- raise_ejdb_error(ejdb);
- }
-
- bson_oid_t oid = ruby_to_bson_oid(rboid);
- if (!ejdbrmbson(coll, &oid)) {
- raise_ejdb_error(ejdb);
- }
-}
-
void EJDB_set_index_internal(VALUE self, VALUE collName, VALUE fpath, int flags) {
SafeStringValue(collName);
rb_define_singleton_method(ejdbClass, "new", RUBY_METHOD_FUNC(EJDB_new), 0);
rb_define_const(ejdbClass, "DEFAULT_OPEN_MODE", INT2FIX(DEFAULT_OPEN_MODE));
+ rb_define_const(ejdbClass, "JBOREADER", INT2FIX(JBOREADER));
rb_define_const(ejdbClass, "JBOWRITER", INT2FIX(JBOWRITER));
rb_define_const(ejdbClass, "JBOCREAT", INT2FIX(JBOCREAT));
rb_define_const(ejdbClass, "JBOTSYNC", INT2FIX(JBOTSYNC));
rb_define_const(ejdbClass, "JBOTRUNC", INT2FIX(JBOTRUNC));
rb_define_singleton_method(ejdbClass, "open", RUBY_METHOD_FUNC(EJDB_open), 2);
- rb_define_method(ejdbClass, "is_open?", RUBY_METHOD_FUNC(EJDB_is_open), 0);
+ rb_define_method(ejdbClass, "open?", RUBY_METHOD_FUNC(EJDB_is_open), 0);
rb_define_method(ejdbClass, "close", RUBY_METHOD_FUNC(EJDB_close), 0);
rb_define_method(ejdbClass, "save", RUBY_METHOD_FUNC(EJDB_save), -1);
rb_define_method(ejdbClass, "load", RUBY_METHOD_FUNC(EJDB_load), 2);