From 9fa8fa2fbb0c2423281ac7c2072daba804036aa0 Mon Sep 17 00:00:00 2001 From: adam Date: Wed, 20 Mar 2013 17:30:11 +0700 Subject: [PATCH] Working on lua documentation --- luaejdb/doc/index.html | 1220 +++++++++++++++++++++++++++++++++++++++++++++--- luaejdb/ejdb.lua | 89 ++-- luaejdb/ejdb.luadoc | 280 +++++++++-- luaejdb/test/t1.lua | 9 +- node/ejdb.js | 4 +- 5 files changed, 1470 insertions(+), 132 deletions(-) diff --git a/luaejdb/doc/index.html b/luaejdb/doc/index.html index 0fd9211..fb6e468 100644 --- a/luaejdb/doc/index.html +++ b/luaejdb/doc/index.html @@ -123,12 +123,40 @@ Save/update specified JSON objects in the collection. - DB:find (cname, q) + DB:find (cname, q, flags) Execute query on collection. + DB:findOne (cname, q) + Same as DB:find but retrieves only first matching JSON object. + + + DB:update (cname, q) + Convenient method to execute update queries. + + + DB:count (cname, q) + Convenient count(*) operation. + + + RS:object (i) + Returns result set lua table object at specified position i + + + RS:field (i, name) + Returns field value of lua object at specified position i + + + RS.__len () + Length of result set. + + + Q:F (fname) + Set current field for the next operation during query building. + + Q:Eq (val) - Field eq restriction. + Field equality restriction. Q:ElemMatch (val) @@ -154,6 +182,95 @@ Q:Lte (val) Lesser than or equal (val <= arg) + + Q:Icase (val) + Case insensitive string matching + + + Q:Begin (val) + String starts with prefix + + + Q:In (val) + Field value matched any value of specified in val table. + + + Q:NotIn (val) + Negation of Q:In + + + Q:Bt (n1, n2) + Between for number types + + + Q:StrAnd (val) + String tokens(or string array vals) matches all tokens in specified val array. + + + Q:StrOr (val) + String tokens(or string array vals) matches any token in specified array. + + + Q:Inc (val) + Increment current field. + + + Q:Set (val) + Set fields to values. + + + Q:Upsert (val) + Atomic upsert. + + + Q:AddToSet (val) + Atomically adds val to the array field only if val not in the array already. + + + Q:AddToSetAll (val) + Atomically performs set union with values in val for specified array field. + + + Q:Pull (val) + Atomically removes all occurrences of val from field, if field is an array. + + + Q:PullAll (val) + Atomically performs set substraction of values in val for specified array field. + + + Q:DropAll () + In-place record removal operation. + + + Q:Join (cname, fpath) + Make collection join + for select queries. + + + Q:Or (...) + Add OR joined query restrictions. + + + Q:Skip (val) + Sets number of skipped records in the result set. + + + Q:Max (val) + Sets max number of records in the result set. + + + Q:OrderBy (...) + Set sorting rules for query results. + + + Q:Fields (...) + Sets fields to be included in resulting objects. + + + Q:NotFields (...) + Sets fields to be excluded from resulting objects. +

Tables

@@ -166,6 +283,10 @@ + + + +
DB Database itself.
RSResult set cursor object.

@@ -180,19 +301,21 @@
Opens EJDB database. - w Open as a writer
- r Open as a reader
- c Create db if it not exists
- t Truncate existing db
- s Sycn db after each transaction
- Default open mode: rws

Parameters:

Returns:

@@ -232,7 +355,8 @@

Parameters:

@@ -293,9 +417,11 @@

Parameters:

Returns:

@@ -535,9 +661,11 @@

Parameters:

@@ -554,15 +682,17 @@
- DB:find (cname, q) + DB:find (cname, q, flags)

Execute query on collection.

-

EJDB queries inspired by MongoDB (mongodb.org) and follows same philosophy. - - Supported queries:

-
- Simple matching of String OR Number OR Array value:
+

EJDB queries inspired by MongoDB (mongodb.org) and follows same philosophy.

+ +

Queries and query hints can be constructed by Q query/json builder.

+
- Supported queries:
+- Simple matching of String OR Number OR Array value:
     -   {'fpath' : 'val', ...}
 - $not Negate operation.
     -   {'fpath' : {'$not' : val}} //Field not equal to val
@@ -630,21 +760,22 @@
 
-

NOTE: It is better to execute update queries with $onlycount=true hint flag

+

NOTE: It is better to execute update queries with $onlycount=true hint flag

    or use the special `update()` method to avoid unnecessarily data fetching.
 
-

NOTE: Negate operations: $not and $nin not using indexes

+ +

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. - NOTE: If callback is not provided this function will be synchronous.

-

QUERY HINTS (specified by hints argument):

+

NOTE: Only one index can be used in search query operation.

+ +

NOTE: If callback is not provided this function will be synchronous.

+ +

QUERY HINTS specified by calling Q:Skip Q:Max, Q:OrderBy, Q:Fields:

- $max Maximum number in the result set
 - $skip Number of skipped results in the result set
 - $orderby Sorting order of query fields.
-- $onlycount true|false If `true` only count of matching records will be returned
-                        without placing records in result set.
 - $fields Set subset of fetched fields.
     If field presented in $orderby clause it will be forced to include in resulting records.
     Example:
@@ -700,11 +831,33 @@ end
     

Parameters:

  • cname - {String} Name of collection
  • + string + Name of collection
  • q - {table|Q} JSON query object
  • + Q + JSON query object +
  • flags + string + +

    Query control flags:

    +
    `c`: only count of matching records will be returned without placing records in result set.
    +`l`: return query execution log
    +
    +
+

Returns:

+
    +
  1. + RS + result set, it will be nil if c flag presented in the control flags
  2. +
  3. + number + Count of matched/updated records
  4. +
  5. + optional string + Query execution log if l flag presented in the control flags
  6. +

see also:

@@ -712,25 +865,188 @@ end Q +

Usage:

+
    +
  • db:find("mycoll", Q("foo", "bar")) => {"foo" : "bar"}
  • +
  • db:find("mycoll", Q("foo", "bar"):Max(10)) -- Limit results up to 10 records
  • +
  • db:find("parrots2", Q("likes", "toys"):OrderBy("name asc", "age desc"))
  • +
  • db:find("parrots2", Q():F("likes"):Eq("toys"):OrderBy({ name = 1 }, { age = -1 }))
  • +
- - Q:Eq (val) + + DB:findOne (cname, q)
+ Same as DB:find but retrieves only first matching JSON object. + +

Parameters:

+
    +
  • cname + string + Name of collection
  • +
  • q + Q + JSON query object
  • +
+ +

Returns:

+
    +
  1. + table + Lua table constructed from matched BSON record or nil of record not found
  2. +
  3. + number + Count of matched/updated records
  4. +
  5. + optional string + Query execution log if l flag presented in the control flags
  6. +
+ -

Field eq restriction.

-
{fname : fval}
-
+
+
+ + DB:update (cname, q) +
+
+ Convenient method to execute update queries. +

Parameters:

    -
  • val +
  • cname + string + Name of collection
  • +
  • q + Q + JSON query object
  • +
+ +

Returns:

+
    +
  1. + number + Count of matched/updated records
  2. +
  3. + optional string + Query execution log if l flag presented in the control flags
  4. +
- + + +
+
+ + DB:count (cname, q) +
+
+ Convenient count(*) operation. + +

Parameters:

+
    +
  • cname + string + Name of collection
  • +
  • q + Q + JSON query object
  • +
+ +

Returns:

+
    +
  1. + number + Count of matched/updated records
  2. +
  3. + optional string + Query execution log if l flag presented in the control flags
  4. +
+ + + + +
+
+ + RS:object (i) +
+
+ Returns result set lua table object at specified position i + +

Parameters:

+
    +
  • i + number + Position of record in the result set
  • +
+ +

Returns:

+
    + + table + Resulting lua object constructed from BSON record. +
+ + + + +
+
+ + RS:field (i, name) +
+
+ Returns field value of lua object at specified position i + +

Parameters:

+
    +
  • i + number + Position of record in the result set
  • +
  • name + string + JSON field name
  • +
+ +

Returns:

+
    + + Value of field +
+ + + + +
+
+ + RS.__len () +
+
+ Length of result set. + + + + + + +
+
+ + Q:F (fname) +
+
+ Set current field for the next operation during query building. + +

Parameters:

+
    +
  • fname + string + JSON field path
@@ -738,23 +1054,49 @@ end

Usage:

    -
  • Q():F("fname"):Eq(<fval>)
  • -
  • Q("fname", <fval>)
  • +
    Q:F("name"):Eq("andy"):F("age"):Gt(30) => {"name" : "andy", "age" : {"$gt" : 30}}
- - Q:ElemMatch (val) + + Q:Eq (val)
+ Field equality restriction. + All usage samples represent same thing: {"fname" : fval} -

Element match construction.

-
- $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.
-
+

Parameters:

+
    +
  • val + any BSON value as Lua object including Q instances.
  • +
+ +

Returns:

+
    + + Self Q +
+ + + +

Usage:

+
    +
  • Q():F("fname"):Eq(fval)
  • +
  • Q("fname", fval)
  • +
  • Q():F("fname", fval)
  • +
+
+
+ + Q:ElemMatch (val) +
+
+ Element match construction. + - $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.

Parameters:

    @@ -764,6 +1106,11 @@ end
+

Returns:

+
    + + Self Q +
@@ -784,6 +1131,11 @@ end +

Returns:

+
    + + Self Q +
@@ -803,11 +1155,17 @@ end

Parameters:

  • val + number
+

Returns:

+
    + + Self Q +
@@ -827,11 +1185,17 @@ end

Parameters:

  • val + number
+

Returns:

+
    + + Self Q +
@@ -851,11 +1215,17 @@ end

Parameters:

  • val + number
+

Returns:

+
    + + Self Q +
@@ -875,11 +1245,17 @@ end

Parameters:

  • val + number
+

Returns:

+
    + + Self Q +
@@ -889,32 +1265,699 @@ end
- -

Tables

-
- - Q + + Q:Icase (val)
- Query/JSON builder is used to create EJDB queries or JSON objects with - preserverd keys order (Unlike lua tables).

+ Case insensitive string matching -

Examples: +

Parameters:

+ +

Returns:

+
    -

    see also:

    - + Self Q +
+ + + +

Usage:

+
    +
  • Q():F("name"):Icase("aNdY") => {"name" : {"$icase" : "aNdY"}}
  • +
  • Q():F("name"):Icase({[$in] = {"aNdY", "AnTon"}}) => {"name" : {"$icase" : {"$in" : ["aNdY", "AnTon"]}}}
  • +
+ +
+
+ + Q:Begin (val) +
+
+ String starts with prefix + +

Parameters:

+ + +

Returns:

+
    + + Self Q +
+ + + +

Usage:

+
    +
    Q():F("fpath"):Begin("prefix") => {"fpath" : {"$begin" : "prefix"}}
    +
+ +
+
+ + Q:In (val) +
+
+ Field value matched any value of specified in val table. + +

Parameters:

+
    +
  • val + table + Not empty lua array of values.
  • +
+ +

Returns:

+
    + + Self Q +
+ + + +

Usage:

+
    +
    Q():F("fpath"):In({"val1", "val2", "val3"}) => {"fpath" : {"$in" : ["val1", "val2", "val3"]}}
    +
+ +
+
+ + Q:NotIn (val) +
+
+ Negation of Q:In + +

Parameters:

+
    +
  • val + + +
  • +
+ +

Returns:

+
    + + Self Q +
+ + +

see also:

+ + + +
+
+ + Q:Bt (n1, n2) +
+
+ Between for number types + +

Parameters:

+
    +
  • n1 + number + + +
  • +
  • n2 + number + + +
  • +
+ +

Returns:

+
    + + Self Q +
+ + + +

Usage:

+
    +
    Q():F("age"):Bt(10, 20) => {"age" : {"$bt" : [10, 20]}}
    +
+ +
+
+ + Q:StrAnd (val) +
+
+ String tokens(or string array vals) matches all tokens in specified val array. + +

Parameters:

+
    +
  • val + table + Array of tokens to match.
  • +
+ +

Returns:

+
    + + Self Q +
+ + + +

Usage:

+
    +
    Q():F("description"):StrAnd({"foo", "bar"}) -- descripton contains all tokens: 'foo' and 'bar'
    +
+ +
+
+ + Q:StrOr (val) +
+
+ String tokens(or string array vals) matches any token in specified array. + +

Parameters:

+
    +
  • val + table + Array of tokens to match.
  • +
+ +

Returns:

+
    + + Self Q +
+ + + +

Usage:

+
    +
    Q():F("description"):StrOr({"foo", "bar"}) -- descripton contains all tokens: 'foo' or 'bar'
    +
+ +
+
+ + Q:Inc (val) +
+
+ Increment current field. Only number types are supported. + +

Parameters:

+
    +
  • val + number + + +
  • +
+ +

Returns:

+
    + + Self Q +
+ + +

see also:

+ + +

Usage:

+
    +
    Q():F("count"):Inc(1):F("age"):Inc(-20) => {"$inc" : {"count" : 1, "age" : -20}}
    +
+ +
+
+ + Q:Set (val) +
+
+ Set fields to values. + +

Parameters:

+
    +
  • val + table or Q + Table of fields to set
  • +
+ +

Returns:

+
    + + Self Q +
+ + + +

Usage:

+
    +
    Q():Set({age = 20, count = 1}) => {"$set" : {"age" : 20, count : 1}}
    +
+ +
+
+ + Q:Upsert (val) +
+
+ Atomic upsert. + If matching records are found it will be $set operation, + otherwise new record will be inserted with fields specified by val table. + +

Parameters:

+
    +
  • val + table or Q + Table of fields to set/insert + Insert {"foo" : "bar"} if this object does not exists:
  • +
+ +

Returns:

+
    + + Self Q +
+ + + +

Usage:

+
    +
  • Q("foo","bar"):Upsert(Q("foo", "bar")) => {"foo" : "bar", "$upsert" : {"foo" : "bar"}}
  • +
  • Q("foo","bar"):Upsert({foo ="bar"}) => {"foo" : "bar", "$upsert" : {"foo" : "bar"}}
  • +
+ +
+
+ + Q:AddToSet (val) +
+
+ Atomically adds val to the array field only if val not in the array already. + If containing array is missing it will be created. + +

Parameters:

+
    +
  • val + Value to add
  • +
+ +

Returns:

+
    + + Self Q +
+ + + +

Usage:

+
    +
    Q():F("tags"):AddToSet("red") => {"$addToSet" : {"tags" : "red"}}
    +
+ +
+
+ + Q:AddToSetAll (val) +
+
+ Atomically performs set union with values in val for specified array field. + +

Parameters:

+
    +
  • val + table + Array of values to add
  • +
+ +

Returns:

+
    + + Self Q +
+ + +

see also:

+ + +

Usage:

+
    +
    Q():F("tags"):AddToSetAll({"red", "green"})
    +
+ +
+
+ + Q:Pull (val) +
+
+ Atomically removes all occurrences of val from field, if field is an array. + +

Parameters:

+
    +
  • val + Value to remove
  • +
+ +

Returns:

+
    + + Self Q +
+ + +

see also:

+ + +

Usage:

+
    +
    Q():F("tags"):Pull("red") => {"$pull" : {"tags" : "red"}}
    +
+ +
+
+ + Q:PullAll (val) +
+
+ Atomically performs set substraction of values in val for specified array field. + +

Parameters:

+
    +
  • val + table + Array of values to remove from array field
  • +
+ +

Returns:

+
    + + Self Q +
+ + +

see also:

+ + +

Usage:

+
    +
    Q():F("tags"):PullAll({"red", "green"}) => {"$pullAll" : {"tags" : ["red", "green"]}}
    +
+ +
+
+ + Q:DropAll () +
+
+ In-place record removal operation. + + +

Returns:

+
    + + Self Q +
+ + + +

Usage:

+
    +
    db:update(Q():F("name", "andy"):DropAll()) -- Removes all records with name eq 'andy'
    +
+ +
+
+ + Q:Join (cname, fpath) +
+
+ Make collection join + for select queries. + +

Parameters:

+
    +
  • cname + string + Name for joined collection
  • +
  • fpath + string + Name of field with BSON OIDs of joined objects
  • +
+ +

Returns:

+
    + + Self Q +
+ + + + +
+
+ + Q:Or (...) +
+
+ Add OR joined query restrictions. + +

Parameters:

+
    +
  • ... + table or Q + List of OR joined restrictions
  • +
+ +

Returns:

+
    + + Self Q +
+ + + +

Usage:

+
    +
    Q():Or(Q("name", "anton"), Q("name", "andy"))
    +    Find records with "name" field eq "anton" or "andy"
    +
+ +
+
+ + Q:Skip (val) +
+
+ Sets number of skipped records in the result set. + +

Parameters:

+
    +
  • val + number + + +
  • +
+ +

Returns:

+
    + + Self Q +
+ + + + +
+
+ + Q:Max (val) +
+
+ Sets max number of records in the result set. + +

Parameters:

+
    +
  • val + number + + +
  • +
+ +

Returns:

+
    + + Self Q +
+ + + + +
+
+ + Q:OrderBy (...) +
+
+ Set sorting rules for query results. + tparam table|string + +

Parameters:

+
    +
  • ... + + +
  • +
+ + + + +

Usage:

+
    +
  • Q:OrderBy("name asc", "age desc") => ORDER BY name ASC, age dESC
  • +
  • Q:OrderBy({name = 1}, {age = -1}) => ORDER BY name ASC, age dESC
  • +
+ +
+
+ + Q:Fields (...) +
+
+ Sets fields to be included in resulting objects. + If field presented in $orderby clause it will be forced to include in resulting records. + +

Parameters:

+
    +
  • ... + string + Fields to be included in fetched objects.
  • +
+ +

Returns:

+
    + + Self Q +
+ + + +

Usage:

+
    +
    Q:Fields("name", "age")
    +
+ +
+
+ + Q:NotFields (...) +
+
+ Sets fields to be excluded from resulting objects. + +

Parameters:

+
    +
  • ... + string + Fields to be excluded from fetched objects.
  • +
+ +

Returns:

+
    + + Self Q +
+ + + +

Usage:

+
    +
    Q:NotFields("name", "description")
    +
+ +
+
+

Tables

+
+
+ + Q +
+
+ Query/JSON builder is used to create EJDB queries or JSON objects with + preserverd keys order (Unlike lua tables). + Q can be used to construct BSON objects as well as queries.

+ +

Examples: + + + + +

see also:

+

Usage:

    @@ -938,6 +1981,59 @@ end
+
+ + RS +
+
+ +

Result set cursor object. + Usage:

+
#res - length of result set
+res[i] - BSON representations of object as lua string
+res:object(i) - Lua table constructed from BSON data
+res:field(i, <field name>) - Lua value of fetched BSON object
+res() - Creates iterator for pairs (obj, idx)
+      where obj - Lua table constructed from BSON data
+            idx - Index of fetched object in the result set
+
+ +

Examples:

+
for i = 1, #res do
+  local ob = res:object(i)
+  ...
+end
+
+ +

OR

+ +
for i = 1, #res do
+  res:field(i, "json field name")
+  ...
+end
+
+ +

OR

+ +
for vobj, idx in res() do
+  -- vobj is a lua table representation of fetched json object
+  vobj["json field name"]
+  ...
+end
+
+ + + + + +

see also:

+ + + +
diff --git a/luaejdb/ejdb.lua b/luaejdb/ejdb.lua index b294c99..25be60f 100644 --- a/luaejdb/ejdb.lua +++ b/luaejdb/ejdb.lua @@ -260,16 +260,17 @@ function B:_init(fname, ...) return self end -function B:_checkop() +function B:_checkOp() assert(type(self._field) == "string") end -function B:_setop(op, val, ...) - self:_checkop() +function B:_setOp(op, val, ...) + self:_checkOp() local types, replace = ... local ttypes = type(types) if (ttypes == "string") then - assert(type(val) == types, "Invalid query argument field: " .. self._field .. " val: " .. inspect(val)) + assert(type(val) == types, "Invalid query argument field: " .. self._field .. + " It should have '" .. types .. "' type," .. " got: " .. inspect(val)) elseif (ttypes == "function") then assert(types(val), "Invalid query argument field: " .. self._field .. " val: " .. inspect(val)) elseif (ttypes == "table") then @@ -296,12 +297,33 @@ function B:_setop(op, val, ...) elseif replace then for i = 2, #olist do olist[i] = nil end end - if (op == nil) then + if op == nil then table.insert(olist, setmetatable({ val }, mtBVal)) else - table.insert(olist, { op, val }) + local found = false + for i = 2, #olist do + if olist[i][1] == op then -- found previous added op + found = true + olist[i][2] = val -- replace old value + break + end + end + if not found then + table.insert(olist, { op, val }) + end end self._bson = nil + return self +end + +function B:_invertOp(op, val, ...) + local pf = self._field; + assert(type(op) == "string", "Operation must be a string") + assert(type(pf) == "string", "You should set field before by Q:F('fname')") + self._field = op + self:_setOp(pf, val, ...) + self._field = pf + return self end function B:_toOpVal(op, val) @@ -317,12 +339,13 @@ function B:_hintOp(op, val, ...) self._hints = B() end self._hints:_rootOp(op, val, ...) + return self end function B:_rootOp(name, val, ...) local types = ... self:F(name) - self:_setop(nil, val, types, true) + self:_setOp(nil, val, types, true) self:F(nil) return self end @@ -340,55 +363,61 @@ end -- Generic key=value function B:KV(key, val) self:F(key); - self:_setop(nil, val, nil, true) + self:_setOp(nil, val, nil, true) return self end -function B:Eq(val) self:_setop(nil, val, nil, true) return self end +function B:Eq(val) return self:_setOp(nil, val, nil, true) end -function B:ElemMatch(val) self:_setop("$elemMatch", val) return self end +function B:ElemMatch(val) return self:_setOp("$elemMatch", val) end -function B:Not(val) self:_setop("$not", val) return self end +function B:Not(val) return self:_setOp("$not", val) end -function B:Gt(val) self:_setop("$gt", val) return self end +function B:Gt(val) return self:_setOp("$gt", val, "number") end -function B:Gte(val) self:_setop("$gte", val) return self end +function B:Gte(val) return self:_setOp("$gte", val, "number") end -function B:Lt(val) self:_setop("$lt", val) return self end +function B:Lt(val) return self:_setOp("$lt", val, "number") end -function B:Lte(val) self:_setop("$lte", val) return self end +function B:Lte(val) return self:_setOp("$lte", val, "number") end -function B:Icase(val) self:_setop("$icase", val) return self end +function B:Icase(val) return self:_setOp("$icase", val) end -function B:Begin(val) self:_setop("$begin", val) return self end +function B:Begin(val) return self:_setOp("$begin", val, "string") end -function B:In(val) self:_setop("$in", val) return self end +function B:In(val) return self:_setOp("$in", val, "table") end -function B:NotIn(val) self:_setop("$nin", val) return self end +function B:NotIn(val) return self:_setOp("$nin", val, "table") end -function B:Bt(val) self:_setop("$bt", val) return self end +function B:Bt(n1, n2) return self:_setOp("$bt", { n1, n2 }) end -function B:StrAnd(val) self:_setop("$strand", val) return self end +function B:StrAnd(val) return self:_setOp("$strand", val, "table") end -function B:StrOr(val) self:_setop("$strand", val) return self end +function B:StrOr(val) return self:_setOp("$stror", val, "table") end -function B:Inc(val) self:_setop("$inc", val) return self end +function B:Inc(val) return self:_invertOp("$inc", val, "number") end -function B:Set(val) return self:_rootOp("$set", val) end +function B:Set(val) return self:_rootOp("$set", val, "table") end -function B:AddToSet(val) return self:_rootOp("$addToSet", val) end +function B:AddToSet(val) return self:_invertOp("$addToSet", val) end -function B:AddToSetAll(val) return self:_rootOp("$addToSetAll", val) end +function B:AddToSetAll(val) return self:_invertOp("$addToSetAll", val, "table") end -function B:Pull(val) return self:_rootOp("$pull", val) end +function B:Pull(val) return self:_invertOp("$pull", val) end -function B:PullAll(val) return self:_rootOp("$pullAll", val) end +function B:PullAll(val) return self:_invertOp("$pullAll", val, "table") end -function B:Upsert(val) return self:_rootOp("$upsert", val) end +function B:Upsert(val) return self:_rootOp("$upsert", val, "table") end function B:DropAll() return self:_rootOp("$dropall", true) end -function B:Do(val) return self:_rootOp("$do", val) end +function B:Do(val) return self:_rootOp("$do", val, "table") end + +function B:Join(cname, fpath) + assert(type(cname) == "string", "Type of #1 arg must be string") + assert(type(fpath) == "string", "Type of #2 arg must be string") + return self:_rootOp("$do", { [fpath] = { ["join"] = cname } }) +end function B:Or(...) self._or = self._or or {} diff --git a/luaejdb/ejdb.luadoc b/luaejdb/ejdb.luadoc index 2246f83..d0e3022 100644 --- a/luaejdb/ejdb.luadoc +++ b/luaejdb/ejdb.luadoc @@ -7,6 +7,7 @@ local ejdb = {} --- Query/JSON builder is used to create EJDB queries or JSON objects with -- preserverd keys order (Unlike lua tables). +-- @{Q} **can be used to construct BSON objects as well as queries.** -- @class table -- @name Q -- @@ -15,6 +16,7 @@ local ejdb = {} -- @usage Q("likes", "toys"):OrderBy("name asc", "age desc") -- @usage Q("name", "Andy"):F("_id"):Eq("510f7fa91ad6270a00000000"):F("age"):Gt(20):Lt(40):F("score"):In({ 11, 22.12333, 1362835380447, db.toNull() }):Max(232) -- @usage Q():Or(Q("foo", "bar"), Q("foo", "bar6")):OrderBy({ foo = 1 }) +-- @see Q:F -- @see Q:Eq -- @see Q:ElemMatch -- @see Q:Not @@ -38,7 +40,7 @@ local ejdb = {} -- @see Q:Upsert -- @see Q:Upsert -- @see Q:DropAll --- @see Q:Do +-- @see Q:Join -- @see Q:Or -- @see Q:Skip -- @see Q:Skip @@ -56,10 +58,47 @@ local Q = {} -- @name DB local DB = {} +--- +-- Result set cursor object. +-- @class table +-- @name RS +-- Usage: +-- #res - length of result set +-- res[i] - BSON representations of object as lua string +-- res:object(i) - Lua table constructed from BSON data +-- res:field(i, ) - Lua value of fetched BSON object +-- res() - Creates iterator for pairs (obj, idx) +-- where obj - Lua table constructed from BSON data +-- idx - Index of fetched object in the result set +-- +-- Examples: +-- for i = 1, #res do +-- local ob = res:object(i) +-- ... +-- end +-- +-- OR +-- +-- for i = 1, #res do +-- res:field(i, "json field name") +-- ... +-- end +-- +-- OR +-- +-- for vobj, idx in res() do +-- -- vobj is a lua table representation of fetched json object +-- vobj["json field name"] +-- ... +-- end +-- @see RS:object +-- @see RS:field +local RS = {} + --- Opens EJDB database. -- @usage local db = ejdb.open("foodb", "wrc") --- @param path {String} Database main file --- @param mode {String?} Database open mode flags:
+-- @tparam string path Database main file +-- @tparam ?string mode Database open mode flags:
-- `w` Open as a writer
-- `r` Open as a reader
-- `c` Create db if it not exists
@@ -74,7 +113,7 @@ function ejdb.open(path, mode) end function ejdb.close() end --- Converts string OID into BSON oid table. --- @param val {String} 24 hex chars BSON_OID +-- @tparam string val 24 hex chars BSON_OID function ejdb.toOID(val) end --- Converts os.time table (or number of seconds since epoch) into BSON_DATE. @@ -87,8 +126,8 @@ function ejdb.toDate(val) end function ejdb.toDateNow() end --- Builds BSON_REGEX value --- @param re {String} Regular expression --- @param opts {String} Regular expression flags +-- @tparam string re Regular expression +-- @tparam ?string opts Regular expression flags -- @return BSON_REGEX table value function ejdb.toRegexp(re, opts) end @@ -137,8 +176,8 @@ function DB.toUndefined() end -- 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. --- @param cname {String} Name of collection. --- @param obj Lua table or Q represents JSON object. +-- @tparam string cname Name of collection. +-- @tparam table|Q obj represents JSON object. -- @param ... If last argument is True a saved object will be merged with who's -- already persisted in db. -- @usage dQ:save("parrots2", {foo = "bar"}) @@ -148,7 +187,9 @@ function DB:save(cname, obj, ...) end --- Execute query on collection. -- -- EJDB queries inspired by MongoDB (mongodb.org) and follows same philosophy. --- - Supported queries: +-- +-- Queries and query hints can be constructed by @{Q} query/json builder. +-- - Supported queries: -- - Simple matching of String OR Number OR Array value: -- - {'fpath' : 'val', ...} -- - $not Negate operation. @@ -206,19 +247,20 @@ function DB:save(cname, obj, ...) end -- 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: It is better to execute update queries with `$onlycount=true` hint flag +-- **NOTE:** It is better to execute update queries with `$onlycount=true` hint flag -- or use the special `update()` method to avoid unnecessarily data fetching. --- NOTE: Negate operations: $not and $nin not using indexes +-- +-- **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. --- NOTE: If callback is not provided this function will be synchronous. -- --- QUERY HINTS (specified by `hints` argument): +-- **NOTE:** Only one index can be used in search query operation. +-- +-- **NOTE:** If callback is not provided this function will be synchronous. +-- +-- **QUERY HINTS** specified by calling @{Q:Skip} @{Q:Max}, @{Q:OrderBy}, @{Q:Fields}: -- - $max Maximum number in the result set -- - $skip Number of skipped results in the result set -- - $orderby Sorting order of query fields. --- - $onlycount true|false If `true` only count of matching records will be returned --- without placing records in result set. -- - $fields Set subset of fetched fields. -- If field presented in $orderby clause it will be forced to include in resulting records. -- Example: @@ -264,88 +306,254 @@ function DB:save(cname, obj, ...) end -- end -- -- --- @param cname {String} Name of collection --- @param q {table|Q} JSON query object +-- @tparam string cname Name of collection +-- @tparam Q q JSON query object +-- @string flags Query control flags: +-- `c`: only count of matching records will be returned without placing records in result set. +-- `l`: return query execution log +-- @treturn RS result set, it will be `nil` if `c` flag presented in the control `flags` +-- @treturn number Count of matched/updated records +-- @treturn ?string Query execution log if `l` flag presented in the control `flags` +-- @usage db:find("mycoll", Q("foo", "bar")) => {"foo" : "bar"} +-- @usage db:find("mycoll", Q("foo", "bar"):Max(10)) -- Limit results up to 10 records +-- @usage db:find("parrots2", Q("likes", "toys"):OrderBy("name asc", "age desc")) +-- @usage db:find("parrots2", Q():F("likes"):Eq("toys"):OrderBy({ name = 1 }, { age = -1 })) -- @see Q -- -function DB:find(cname, q, ...) end +function DB:find(cname, q, flags) end + +--- Same as @{DB:find} but retrieves only first matching JSON object. +-- @tparam string cname Name of collection +-- @tparam Q q JSON query object +-- @treturn table Lua table constructed from matched BSON record or `nil` of record not found +-- @treturn number Count of matched/updated records +-- @treturn ?string Query execution log if `l` flag presented in the control `flags` + +function DB:findOne(cname, q, ...) end + +--- Convenient method to execute update queries. +-- @tparam string cname Name of collection +-- @tparam Q q JSON query object +-- @treturn number Count of matched/updated records +-- @treturn ?string Query execution log if `l` flag presented in the control `flags` + +function DB:update(cname, q, ...) end + +--- Convenient `count(*)` operation. +-- @tparam string cname Name of collection +-- @tparam Q q JSON query object +-- @treturn number Count of matched/updated records +-- @treturn ?string Query execution log if `l` flag presented in the control `flags` + +function DB:count(cname, q, ...) end + +--- Returns result set lua table object at specified position `i` +-- @tparam number i Position of record in the result set +-- @treturn table Resulting lua object constructed from BSON record. +function RS:object(i) end ---- Field eq restriction. --- {fname : fval} --- @usage Q():F("fname"):Eq() --- @usage Q("fname", ) +--- Returns field value of lua object at specified position `i` +-- @tparam number i Position of record in the result set +-- @tparam string name JSON field name +-- @return Value of field +function RS:field(i, name) end + +--- Length of result set. +function RS.__len() end + + +--- Set current field for the next operation during query building. +-- @string fname JSON field path +-- @usage Q:F("name"):Eq("andy"):F("age"):Gt(30) => {"name" : "andy", "age" : {"$gt" : 30}} +function Q:F(fname) end + +--- Field equality restriction. +-- @param val any BSON value as Lua object including @{Q} instances. +-- All usage samples represent same thing: `{"fname" : fval}` +-- @usage Q():F("fname"):Eq(fval) +-- @usage Q("fname", fval) +-- @usage Q():F("fname", fval) +-- @return Self @{Q} function Q:Eq(val) self:_setop(nil, val, nil, true) end --- Element match construction. --- - $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. +-- - $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. +-- @return Self @{Q} function Q:ElemMatch(val) end --- The $not negatiation for `val` block -- @usage Q():Not(Q("foo", "bar")) => {"$not" : {"foo" : "bar"}} +-- @return Self @{Q} function Q:Not(val) end --- Greater than (val > arg) +-- @number val -- @usage Q():F("age"):Gt(29) => {"age" : {"$gt" : 29}} +-- @return Self @{Q} function Q:Gt(val) end --- Greater than or equal (val >= arg) +-- @number val -- @usage Q():F("age"):Gt(29) => {"age" : {"$gte" : 29}} +-- @return Self @{Q} function Q:Gte(val) end --- Lesser than (val < arg) +-- @number val -- @usage Q():F("age"):Lt(29) => {"age" : {"$lt" : 29}} +-- @return Self @{Q} function Q:Lt(val) end --- Lesser than or equal (val <= arg) +-- @number val -- @usage Q():F("age"):Lt(29) => {"age" : {"$lte" : 29}} +-- @return Self @{Q} function Q:Lte(val) end +--- Case insensitive string matching +-- @tparam string|table|Q val +-- @usage Q():F("name"):Icase("aNdY") => {"name" : {"$icase" : "aNdY"}} +-- @usage Q():F("name"):Icase({[$in] = {"aNdY", "AnTon"}}) => {"name" : {"$icase" : {"$in" : ["aNdY", "AnTon"]}}} +-- @return Self @{Q} function Q:Icase(val) end +--- String starts with prefix +-- @string val +-- @usage Q():F("fpath"):Begin("prefix") => {"fpath" : {"$begin" : "prefix"}} +-- @return Self @{Q} function Q:Begin(val) end +--- Field value matched any value of specified in `val` table. +-- @tparam table val Not empty lua array of values. +-- @usage Q():F("fpath"):In({"val1", "val2", "val3"}) => {"fpath" : {"$in" : ["val1", "val2", "val3"]}} +-- @return Self @{Q} function Q:In(val) end +--- Negation of @{Q:In} +-- @see Q:In +-- @return Self @{Q} function Q:NotIn(val) end -function Q:Bt(val) end - -function Q:StrAnd(val) end - +--- Between for number types +-- @number n1 +-- @number n2 +-- @usage Q():F("age"):Bt(10, 20) => {"age" : {"$bt" : [10, 20]}} +-- @return Self @{Q} +function Q:Bt(n1, n2) end + +--- String tokens(or string array vals) matches **all** tokens in specified `val` array. +-- @tparam table val Array of tokens to match. +-- @usage Q():F("description"):StrAnd({"foo", "bar"}) -- descripton contains all tokens: 'foo' and 'bar' +-- @return Self @{Q} +function Q:StrAnd(val) end + +--- String tokens(or string array vals) matches **any** token in specified array. +-- @tparam table val Array of tokens to match. +-- @usage Q():F("description"):StrOr({"foo", "bar"}) -- descripton contains all tokens: 'foo' or 'bar' +-- @return Self @{Q} function Q:StrOr(val) end +--- Increment current field. Only number types are supported. +-- @number val +-- @usage Q():F("count"):Inc(1):F("age"):Inc(-20) => {"$inc" : {"count" : 1, "age" : -20}} +-- @return Self @{Q} +-- @see Q:F function Q:Inc(val) end +--- Set fields to values. +-- @tparam table|Q val Table of fields to set +-- @usage Q():Set({age = 20, count = 1}) => {"$set" : {"age" : 20, count : 1}} +-- @return Self @{Q} function Q:Set(val) end -function Q:AddToSet(val) end +--- Atomic upsert. +-- If matching records are found it will be `$set` operation, +-- otherwise new record will be inserted with fields specified by `val` table. +-- @tparam table|Q val Table of fields to set/insert +-- Insert {"foo" : "bar"} if this object does not exists: +-- @usage Q("foo","bar"):Upsert(Q("foo", "bar")) => {"foo" : "bar", "$upsert" : {"foo" : "bar"}} +-- @usage Q("foo","bar"):Upsert({foo ="bar"}) => {"foo" : "bar", "$upsert" : {"foo" : "bar"}} +-- @return Self @{Q} +function Q:Upsert(val) end -function Q:AddToSetAll(val) end +--- Atomically adds `val` to the `array field` only if `val` not in the array already. +-- If containing array is missing it will be created. +-- @param val Value to add +-- @usage Q():F("tags"):AddToSet("red") => {"$addToSet" : {"tags" : "red"}} +-- @return Self @{Q} +function Q:AddToSet(val) end +--- Atomically performs `set union` with values in `val` for specified array field. +-- @tparam table val Array of values to add +-- @usage Q():F("tags"):AddToSetAll({"red", "green"}) +-- @see Q:F +-- @return Self @{Q} +function Q:AddToSetAll(val) end + +--- Atomically removes all occurrences of `val` from field, if field is an array. +-- @param val Value to remove +-- @usage Q():F("tags"):Pull("red") => {"$pull" : {"tags" : "red"}} +-- @see Q:F +-- @return Self @{Q} function Q:Pull(val) end +--- Atomically performs `set substraction` of values in `val` for specified array field. +-- @tparam table val Array of values to remove from array field +-- @usage Q():F("tags"):PullAll({"red", "green"}) => {"$pullAll" : {"tags" : ["red", "green"]}} +-- @see Q:F +-- @return Self @{Q} function Q:PullAll(val) end -function Q:Upsert(val) end - +--- In-place record removal operation. +-- @usage db:update(Q():F("name", "andy"):DropAll()) -- Removes all records with name eq 'andy' +-- @return Self @{Q} function Q:DropAll() end -function Q:Do(val) end - +--- Make collection join +-- for select queries. +-- @string cname Name for joined collection +-- @string fpath Name of field with BSON OIDs of joined objects +-- @return Self @{Q} +function Q:Join(cname, fpath) end + +--- Add *OR* joined query restrictions. +-- @tparam table|Q ... List of OR joined restrictions +-- @usage Q():Or(Q("name", "anton"), Q("name", "andy")) +-- Find records with "name" field eq "anton" or "andy" +-- @return Self @{Q} function Q:Or(...) end +--- Sets number of skipped records in the result set. +-- @number val +-- @return Self @{Q} function Q:Skip(val) end +--- Sets max number of records in the result set. +-- @number val +-- @return Self @{Q} function Q:Max(val) end +--- Set sorting rules for query results. +-- tparam table|string +-- @usage Q:OrderBy("name asc", "age desc") => ORDER BY name ASC, age dESC +-- @usage Q:OrderBy({name = 1}, {age = -1}) => ORDER BY name ASC, age dESC function Q:OrderBy(...) end +--- Sets fields to be included in resulting objects. +-- If field presented in $orderby clause it will be forced to include in resulting records. +-- @string ... Fields to be included in fetched objects. +-- @usage Q:Fields("name", "age") +-- @return Self @{Q} function Q:Fields(...) end +--- Sets fields to be excluded from resulting objects. +-- @string ... Fields to be excluded from fetched objects. +-- @usage Q:NotFields("name", "description") +-- @return Self @{Q} function Q:NotFields(...) end diff --git a/luaejdb/test/t1.lua b/luaejdb/test/t1.lua index 14369c6..59d2dcb 100644 --- a/luaejdb/test/t1.lua +++ b/luaejdb/test/t1.lua @@ -191,18 +191,23 @@ assert(log:find("MAIN IDX: 'NONE'")) assert(db:getTransactionStatus("mycoll") == false) db:beginTransaction("mycoll") assert(db:getTransactionStatus("mycoll") == true) -db:save("mycoll", {name=1}) +db:save("mycoll", { name = 1 }) assert(db:findOne("mycoll", Q("name", 1))); db:rollbackTransaction("mycoll") assert(db:getTransactionStatus("mycoll") == false) assert(db:findOne("mycoll", Q("name", 1)) == nil); -assert(db:update("ecoll", Q("k1", "v1"):Upsert({k1="v1"})) == 1) +assert(db:update("ecoll", Q("k1", "v1"):Upsert({ k1 = "v1", k2 = 1, k3 = 2 })) == 1) assert(db:update("ecoll", Q("k1", "v1"):Upsert(Q("k1", "v2"))) == 1) assert(db:count("ecoll", Q("k1", "v2")) == 1) +-- test $inc +assert(db:update("ecoll", Q("k1", "v2"):F("k2"):Inc(1):F("k3"):Inc(-2)) == 1); +assert(db:count("ecoll", Q():F("k2", 2):F("k3", 0)) == 1) + db:ensureStringIndex("mycoll", "foo") +print(ejdb.print_bson(Q():F("tags"):AddToSetAll({"red", "green"}):toBSON())) --print(inspect(db:getDBMeta())) db:dropCollection("ecoll", true); diff --git a/node/ejdb.js b/node/ejdb.js index c58f935..31d4b3c 100644 --- a/node/ejdb.js +++ b/node/ejdb.js @@ -333,7 +333,7 @@ function parseQueryArgs(args) { * - {.., '$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. + * $addToSet Atomically adds value to the array only if value not in the array already. * If containing array is missing it will be created. * - {.., '$addToSet' : {'fpath' : val1, 'fpathN' : valN, ...}} * $addToSetAll Batch version if $addToSet @@ -414,7 +414,7 @@ EJDB.prototype.find = function() { }; /** - * Same as #find() but retrieves only one matching JSON object. + * Same as #find() but retrieves only first matching JSON object. * If callback is not provided this function will be synchronous. * * Call variations of findOne(): -- 2.7.4