$push and $pushAll implemented. Fixed #130
authorAnton Adamansky <adamansky@gmail.com>
Tue, 28 Apr 2015 16:25:24 +0000 (22:25 +0600)
committerAnton Adamansky <adamansky@gmail.com>
Tue, 28 Apr 2015 16:25:24 +0000 (22:25 +0600)
ejdb.project
src/bson/bson.c
src/bson/bson.h
src/ejdb/ejdb.c
src/ejdb/ejdb.h
src/ejdb/ejdb_private.h
src/ejdb/tests/ejdbtest2.c

index b16105c..3c92eca 100644 (file)
         <LibraryPath Value="Debug"/>
       </Linker>
       <ResourceCompiler Options="" Required="no"/>
-      <General OutputFile="" IntermediateDirectory="./Debug" Command="$(ProjectPath)/build/src/ejdb/tests/ejdbtest4" CommandArguments="" UseSeparateDebugArgs="no" DebugArguments="" WorkingDirectory="$(ProjectPath)/build/src/ejdb/tests" PauseExecWhenProcTerminates="yes" IsGUIProgram="no" IsEnabled="yes"/>
+      <General OutputFile="" IntermediateDirectory="./Debug" Command="$(ProjectPath)/build/src/ejdb/tests/ejdbtest2" CommandArguments="" UseSeparateDebugArgs="no" DebugArguments="" WorkingDirectory="$(ProjectPath)/build/src/ejdb/tests" PauseExecWhenProcTerminates="yes" IsGUIProgram="no" IsEnabled="yes"/>
       <Environment EnvVarSetName="&lt;Use Defaults&gt;" DbgSetName="&lt;Use Defaults&gt;">
         <![CDATA[]]>
       </Environment>
index 2f8e02e..28d7cf5 100644 (file)
@@ -899,10 +899,8 @@ int bson_ensure_space(bson *b, const int bytesNeeded) {
 
 int bson_finish(bson *b) {
     int i;
-
     if (b->err & BSON_NOT_UTF8)
         return BSON_ERROR;
-
     if (!b->finished) {
         if (bson_ensure_space(b, 1) == BSON_ERROR) return BSON_ERROR;
         bson_append_byte(b, 0);
@@ -910,7 +908,6 @@ int bson_finish(bson *b) {
         bson_little_endian32(b->data, &i);
         b->finished = 1;
     }
-
     return BSON_OK;
 }
 
@@ -1501,11 +1498,11 @@ typedef struct {
     const void *bsdata2; //bsdata to merge with
     int nstack; //nested object stack pos
     int matched; //number of matched merge fields
-} _BSON_MERGE3_CTX;
+} _BSONMERGE3CTX;
 
 static bson_visitor_cmd_t _bson_merge3_visitor(const char *ipath, int ipathlen, const char *key, int keylen,
         const bson_iterator *it, bool after, void *op) {
-    _BSON_MERGE3_CTX *ctx = op;
+    _BSONMERGE3CTX *ctx = op;
     assert(ctx && ctx->bsout && ctx->mfields && ipath && key && it && op);
     const void *buf;
     const char *mpath;
@@ -1582,7 +1579,7 @@ int bson_merge3(const void *bsdata1, const void *bsdata2, bson *out) {
     BSON_ITERATOR_FROM_BUFFER(&it2, bsdata2);
     const char *it2start = it2.cur;
     TCMAP *mfields = tcmapnew2(TCMAPTINYBNUM);
-    _BSON_MERGE3_CTX ctx = {
+    _BSONMERGE3CTX ctx = {
         .bsout = out,
         .mfields = mfields,
         .bsdata2 = bsdata2,
@@ -1679,7 +1676,6 @@ int bson_merge_recursive2(const void *b1data, const void *b2data, bson_bool_t ov
                 bson_append_field_from_iterator(&sit, out);
                 ++c;
             }
-
             char kbuf[TCNUMBUFSIZ];
             BSON_ITERATOR_SUBITERATOR(&it2, &sit);
             while ((sbt = bson_iterator_next(&sit)) != BSON_EOO) {
@@ -1772,8 +1768,11 @@ static bson_visitor_cmd_t _bsonstripvisitor_exclude(const char *ipath, int ipath
             bson_append_field_from_iterator(it, sctx->bsout);
             return (BSON_VCMD_SKIP_NESTED | BSON_VCMD_SKIP_AFTER);
         }
-    } else if (!after && ictx->astack > 0 && bson_isnumstr(key, keylen)) {
-        bson_append_undefined(sctx->bsout, key);
+    } else {
+        if (!after && ictx->astack > 0 && bson_isnumstr(key, keylen)) {
+            bson_append_undefined(sctx->bsout, key);
+        }
+        ictx->matched++;
     }
     return (BSON_VCMD_SKIP_NESTED | BSON_VCMD_SKIP_AFTER);
 }
@@ -1859,15 +1858,18 @@ static bson_visitor_cmd_t _bsonstripvisitor_include(const char *ipath, int ipath
     return rv;
 }
 
-int bson_strip(TCMAP *ifields, bool imode, const void *bsbuf, bson *bsout) {
+int bson_strip(TCMAP *ifields, bool imode, const void *bsbuf, bson *bsout, int *matched) {
     BSONSTRIPCTX sctx = {
         .ifields = ifields,
         .imode = imode,
         .bsbuf = bsbuf,
         .bsout = bsout,
-        .fkfields = NULL
+        .fkfields = NULL,
+        .matched = 0
     };
-    return bson_strip2(&sctx);
+    int rc = bson_strip2(&sctx);
+    *matched = sctx.matched;
+    return rc;
 }
 
 /* Include or exclude fpaths in the specified BSON and put resulting data into `bsout`. */
@@ -1886,6 +1888,7 @@ int bson_strip2(BSONSTRIPCTX *sctx) {
     BSON_ITERATOR_FROM_BUFFER(&it, sctx->bsbuf);
     bson_visit_fields(&it, 0, (sctx->imode) ? _bsonstripvisitor_include : _bsonstripvisitor_exclude, &ictx);
     assert(ictx.nstack == 0);
+    sctx->matched = ictx.matched;
     return bson_finish(sctx->bsout);
 }
 
@@ -2110,7 +2113,7 @@ void bson_init_with_data(bson *bs, const void *bsdata) {
     bs->finished = true;
 }
 
-bool bson_find_merged_array_sets(const void *mbuf, const void *inbuf, bool expandall) {
+bool bson_find_merged_arrays(const void *mbuf, const void *inbuf, bool expandall) {
     assert(mbuf && inbuf);
     bool found = false;
     bson_iterator it, it2;
@@ -2149,7 +2152,7 @@ bool bson_find_merged_array_sets(const void *mbuf, const void *inbuf, bool expan
     return found;
 }
 
-bool bson_find_unmerged_array_sets(const void *mbuf, const void *inbuf) {
+bool bson_find_unmerged_arrays(const void *mbuf, const void *inbuf) {
     assert(mbuf && inbuf);
     bool allfound = false;
     bson_iterator it, it2;
@@ -2188,10 +2191,16 @@ typedef struct {
     int ecode;
     bool duty;
     bool expandall;
-} BSON_MASETS_CTX;
+    bson_merge_array_mode mode;
+} _BSONMARRCTX;
+
 
-static bson_visitor_cmd_t bson_merge_array_sets_pull_tf(const char *fpath, int fpathlen, const char *key, int keylen, const bson_iterator *it, bool after, void *op) {
-    BSON_MASETS_CTX *ctx = op;
+static bson_visitor_cmd_t _bson_merge_arrays_pull_visitor(
+                        const char *fpath, int fpathlen, 
+                        const char *key, int keylen, 
+                        const bson_iterator *it, bool after, void *op) {
+                            
+    _BSONMARRCTX *ctx = op;
     assert(ctx && ctx->mfields >= 0);
     bson_iterator mit;
     bson_type bt = BSON_ITERATOR_TYPE(it);
@@ -2250,8 +2259,13 @@ static bson_visitor_cmd_t bson_merge_array_sets_pull_tf(const char *fpath, int f
     return (BSON_VCMD_OK);
 }
 
-static bson_visitor_cmd_t bson_merge_array_sets_tf(const char *fpath, int fpathlen, const char *key, int keylen, const bson_iterator *it, bool after, void *op) {
-    BSON_MASETS_CTX *ctx = op;
+static bson_visitor_cmd_t _bson_merge_arrays_visitor(
+                        const char *fpath, int fpathlen, 
+                        const char *key, int keylen, 
+                        const bson_iterator *it, 
+                        bool after, void *op) {
+                            
+    _BSONMARRCTX *ctx = op;
     assert(ctx && ctx->mfields >= 0);
     bson_iterator mit;
     bson_type bt = BSON_ITERATOR_TYPE(it);
@@ -2297,11 +2311,13 @@ static bson_visitor_cmd_t bson_merge_array_sets_tf(const char *fpath, int fpathl
             BSON_ITERATOR_SUBITERATOR(&mit, &mitsub); //mit has BSON_ARRAY type
             while ((bt = bson_iterator_next(&mitsub)) != BSON_EOO) {
                 found = false;
-                BSON_ITERATOR_SUBITERATOR(it, &ait); //Rewind main array iterator
-                while ((bt = bson_iterator_next(&ait)) != BSON_EOO) {
-                    if (!bson_compare_it_current(&ait, &mitsub)) {
-                        found = true;
-                        break;
+                if (ctx->mode == BSON_MERGE_ARRAY_ADDSET) {
+                    BSON_ITERATOR_SUBITERATOR(it, &ait); //Rewind main array iterator
+                    while ((bt = bson_iterator_next(&ait)) != BSON_EOO) {
+                        if (!bson_compare_it_current(&ait, &mitsub)) {
+                            found = true;
+                            break;
+                        }
                     }
                 }
                 if (!found) { //Append missing element
@@ -2313,7 +2329,9 @@ static bson_visitor_cmd_t bson_merge_array_sets_tf(const char *fpath, int fpathl
             }
         } else { //Single element to add
             while ((bt = bson_iterator_next(&ait)) != BSON_EOO) {
-                if (!found && !bson_compare_it_current(&ait, &mit)) {
+                if ((ctx->mode == BSON_MERGE_ARRAY_ADDSET) && 
+                    !found && 
+                    !bson_compare_it_current(&ait, &mit)) {
                     found = true;
                 }
                 bson_append_field_from_iterator(&ait, ctx->bsout);
@@ -2336,21 +2354,30 @@ static bson_visitor_cmd_t bson_merge_array_sets_tf(const char *fpath, int fpathl
         bson_append_finish_object(ctx->bsout);
     }
     return (BSON_VCMD_OK);
-
 }
 
-int bson_merge_array_sets(const void *mbuf, const void *inbuf, bool pull, bool expandall, bson *bsout) {
+int bson_merge_arrays(const void *mbuf, 
+                      const void *inbuf, 
+                      bson_merge_array_mode mode, 
+                      bool expandall, 
+                      bson *bsout) {
+                          
     assert(mbuf && inbuf && bsout);
+    assert(mode == BSON_MERGE_ARRAY_ADDSET || 
+           mode == BSON_MERGE_ARRAY_PULL || 
+           mode == BSON_MERGE_ARRAY_PUSH);
+    
     if (bsout->finished) {
         return BSON_ERROR;
     }
-    BSON_MASETS_CTX ctx = {
+    _BSONMARRCTX ctx = {
         .bsout = bsout,
         .mbuf = mbuf,
         .mfields = 0,
         .duty = false,
         .expandall = expandall,
-        .ecode = BSON_OK
+        .ecode = BSON_OK,
+        .mode = mode
     };
     bson_type bt, bt2;
     bson_iterator it, it2;
@@ -2362,12 +2389,13 @@ int bson_merge_array_sets(const void *mbuf, const void *inbuf, bool pull, bool e
         ctx.mfields++;
     }
     BSON_ITERATOR_FROM_BUFFER(&it, inbuf);
-    if (pull) {
-        bson_visit_fields(&it, 0, bson_merge_array_sets_pull_tf, &ctx);
+    if (mode == BSON_MERGE_ARRAY_PULL) {
+        bson_visit_fields(&it, 0, _bson_merge_arrays_pull_visitor, &ctx);
     } else {
-        bson_visit_fields(&it, 0, bson_merge_array_sets_tf, &ctx);
+        bson_visit_fields(&it, 0, _bson_merge_arrays_visitor, &ctx);
     }
-    if (ctx.mfields == 0 || pull) {
+    if (ctx.mfields == 0 || //all fields are merged
+        mode == BSON_MERGE_ARRAY_PULL) {
         return ctx.ecode;
     }
 
@@ -2415,12 +2443,10 @@ int bson_merge_array_sets(const void *mbuf, const void *inbuf, bool pull, bool e
         int res = bson_merge_recursive(&bsc, &bst, false, bsout);
         bson_destroy(&bsc);
         bson_destroy(&bst);
-
         if (res != BSON_OK) {
             return BSON_ERROR;
         }
     }
-
     return ctx.ecode;
 }
 
index 3de71e7..26947e2 100644 (file)
@@ -1161,6 +1161,7 @@ typedef struct {
     const void *bsbuf; //Required BSON buffer to process.
     bson *bsout; //Required Allocated output not finished bson* object.
     TCMAP *fkfields; //Optional: Map (fpath => bson key) used to force specific bson keys for selected fpaths.
+    int matched;  //Output: number of matched fieldpaths
 } BSONSTRIPCTX;
 
 /**
@@ -1171,9 +1172,10 @@ typedef struct {
  * @param imode If true fpaths will be included. Otherwise fpaths will be excluded from bson.
  * @param bsbuf BSON buffer to process.
  * @param bsout Allocated output not finished bson* object
+ * @param matched[out] Number of matched include/exclude fieldpaths.
  * @return BSON error code
  */
-EJDB_EXPORT int bson_strip(TCMAP *ifields, bool imode, const void *bsbuf, bson *bsout);
+EJDB_EXPORT int bson_strip(TCMAP *ifields, bool imode, const void *bsbuf, bson *bsout, int *matched);
 EJDB_EXPORT int bson_strip2(BSONSTRIPCTX *sctx);
 
 
@@ -1185,7 +1187,9 @@ EJDB_EXPORT int bson_strip2(BSONSTRIPCTX *sctx);
  * @param fplen Length of fpath value
  */
 EJDB_EXPORT int bson_compare(const void *bsdata1, const void *bsdata2, const char* fpath, int fplen);
-EJDB_EXPORT int bson_compare_fpaths(const void *bsdata1, const void *bsdata2, const char *fpath1, int fplen1, const char *fpath2, int fplen2);
+EJDB_EXPORT int bson_compare_fpaths(const void *bsdata1, const void *bsdata2, 
+                                    const char *fpath1, int fplen1, 
+                                    const char *fpath2, int fplen2);
 EJDB_EXPORT int bson_compare_it_current(const bson_iterator *it1, const bson_iterator *it2);
 EJDB_EXPORT int bson_compare_string(const char* cv, const void *bsdata, const char *fpath);
 EJDB_EXPORT int bson_compare_long(const int64_t cv, const void *bsdata, const char *fpath);
@@ -1206,10 +1210,18 @@ EJDB_EXPORT bson* bson_create_from_buffer(const void *buf, int bufsz);
 EJDB_EXPORT bson* bson_create_from_buffer2(bson *bs, const void *buf, int bufsz);
 EJDB_EXPORT void bson_init_with_data(bson *bs, const void *bsdata);
 
-EJDB_EXPORT bool bson_find_unmerged_array_sets(const void *mbuf, const void *inbuf);
-EJDB_EXPORT bool bson_find_merged_array_sets(const void *mbuf, const void *inbuf, bool expandall);
-EJDB_EXPORT int bson_merge_array_sets(const void *mbuf, const void *inbuf, bool pull, bool expandall, bson *bsout);
-
+typedef enum {
+    BSON_MERGE_ARRAY_ADDSET = 0,
+    BSON_MERGE_ARRAY_PULL = 1,
+    BSON_MERGE_ARRAY_PUSH = 2
+} bson_merge_array_mode;
+
+EJDB_EXPORT int bson_merge_arrays(const void *mbuf, const void *inbuf, 
+                                  bson_merge_array_mode mode, 
+                                  bool expandall, bson *bsout);
+                                  
+EJDB_EXPORT bool bson_find_unmerged_arrays(const void *mbuf, const void *inbuf);
+EJDB_EXPORT bool bson_find_merged_arrays(const void *mbuf, const void *inbuf, bool expandall);
 
 /**
  * Convert BSON into JSON buffer.
index d568a57..d4639a1 100644 (file)
@@ -2776,7 +2776,8 @@ static bool _pushprocessedbson(_QRYCTX *ctx, const void *bsbuf, int bsbufsz) {
             .fkfields = _fkfields,
             .imode = ctx->imode,
             .bsbuf = inbuf,
-            .bsout = &bsout
+            .bsout = &bsout,
+            .matched = 0
         };
         if (bson_strip2(&sctx) != BSON_OK) {
             _ejdbsetecode(jb, JBEINVALIDBSON, __FILE__, __LINE__, __func__);
@@ -2892,7 +2893,7 @@ static bool _qryupdate(_QRYCTX *ctx, void *bsbuf, int bsbufsz) {
     assert(ctx && ctx->q && (ctx->q->flags & EJQUPDATING) && bsbuf && ctx->didxctx);
 
     bool rv = true;
-    bool update = false;
+    int update = 0;
     EJCOLL *coll = ctx->coll;
     EJQ *q = ctx->q;
     bson_oid_t *oid;
@@ -2900,7 +2901,7 @@ static bool _qryupdate(_QRYCTX *ctx, void *bsbuf, int bsbufsz) {
     bson_iterator it, it2;
     TCMAP *rowm = NULL;
 
-    if (q->flags & EJQDROPALL) { //Record will be dropped
+    if (q->flags & EJQDROPALL) { //Records will be dropped
         bt = bson_find_from_buffer(&it, bsbuf, JDBIDKEYNAME);
         if (bt != BSON_OID) {
             _ejdbsetecode(coll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__);
@@ -2926,6 +2927,7 @@ static bool _qryupdate(_QRYCTX *ctx, void *bsbuf, int bsbufsz) {
         }
         return rv;
     }
+    
     //Apply update operation
     bson bsout;
     bsout.data = NULL;
@@ -2937,62 +2939,169 @@ static bool _qryupdate(_QRYCTX *ctx, void *bsbuf, int bsbufsz) {
     const EJQF *incqf = NULL; /*$inc*/
        const EJQF *renameqf = NULL; /*$rename*/
     const EJQF *addsetqf[2] = {NULL}; /*$addToSet, $addToSetAll*/
+    const EJQF *pushqf[2] = {NULL}; /*$push, $pushAll */
     const EJQF *pullqf[2] = {NULL}; /*$pull, $pullAll*/
 
-    //$set, $inc, $addToSet, $addToSetAll, $pull, $pullAll operations
     for (int i = 0; i < TCLISTNUM(q->qflist); ++i) {
+        
         const EJQF *qf = TCLISTVALPTR(q->qflist, i);
+        const uint32_t flags = qf->flags;
+        
         if (qf->updateobj == NULL) {
             continue;
-        }
-        if (qf->flags & EJCONDSET) { //$set
+        } 
+        if (flags & EJCONDSET) { //$set
             setqf = qf;
-            continue;
-        }
-        if (qf->flags & EJCONDUNSET) { //$unset
+        } else if (flags & EJCONDUNSET) { //$unset
             unsetqf = qf;
-            continue;
-        }
-        if (qf->flags & EJCONDINC) { //$inc
+        } else if (flags & EJCONDINC) { //$inc
             incqf = qf;
-            continue;
-        }
-               if (qf->flags & EJCONDRENAME) { //$rename
+        } else if (flags & EJCONDRENAME) { //$rename
                        renameqf = qf;
-                       continue;
-               }
-        if (qf->flags & EJCONDADDSET) { //$addToSet, $addToSetAll
-            if (qf->flags & EJCONDALL) {
+               } else if (flags & EJCONDADDSET) { //$addToSet, $addToSetAll
+            if (flags & EJCONDALL) {
                 addsetqf[1] = qf;
             } else {
                 addsetqf[0] = qf;
             }
-        }
-        if (qf->flags & EJCONDPULL) { //$pull, $pullAll
-            if (qf->flags & EJCONDALL) {
+        } else if (flags & EJCONDPUSH) { //$push, $pushAll
+            if (flags & EJCONDALL) {
+                pushqf[1] = qf;
+            } else {
+                pushqf[0] = qf;
+            }
+        } else if (flags & EJCONDPULL) { //$pull, $pullAll
+            if (flags & EJCONDALL) {
                 pullqf[1] = qf;
             } else {
                 pullqf[0] = qf;
             }
         }
     }
+    
+       if (renameqf) { //todo rename nested fields!
+        char *inbuf = (bsout.finished) ? bsout.data : bsbuf;
+        if (bsout.finished) {
+            //reinit `bsout`, `inbuf` already points to `bsout.data` and will be freed later
+            bson_init_size(&bsout, bson_size(&bsout));
+        } else {
+            assert(bsout.data == NULL);
+            bson_init_size(&bsout, bsbufsz);
+        }
+        TCMAP *efields = NULL;
+               bson *updobj = _qfgetupdateobj(renameqf);
+               BSON_ITERATOR_INIT(&it, updobj);
+               while (rv && (bt = bson_iterator_next(&it)) != BSON_EOO) {
+                       if (bt != BSON_STRING) {
+                               continue;
+                       }
+            const char *ofpath = BSON_ITERATOR_KEY(&it);
+                       const char *nfpath = bson_iterator_string(&it);
+            bt2 = bson_find_from_buffer(&it2, inbuf, ofpath);
+                       if (bt2 == BSON_EOO) { 
+                               continue;
+                       }
+                       if (bson_append_field_from_iterator2(nfpath, &it2, &bsout) != BSON_OK) {
+                               rv = false;
+                               _ejdbsetecode(coll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__);
+                               break;
+                       }
+            update++;
+            if (!efields) {
+                efields = tcmapnew2(TCMAPTINYBNUM);
+            }
+                       tcmapputkeep(efields, ofpath, strlen(ofpath), "", 0);
+                       tcmapputkeep(efields, nfpath, strlen(nfpath), "", 0);
+               }
+
+        BSON_ITERATOR_FROM_BUFFER(&it, inbuf);
+        while (rv && (bt = bson_iterator_next(&it)) != BSON_EOO) {
+            const char *fpath = BSON_ITERATOR_KEY(&it);
+                       if (efields && tcmapget2(efields, fpath)) { 
+                               continue;
+                       }
+                       if (bson_append_field_from_iterator(&it, &bsout) != BSON_OK) {
+                               rv = false;
+                               _ejdbsetecode(coll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__);
+                               break;
+                       }
+               }
+        if (efields) {
+            tcmapdel(efields);
+        }
+        bson_finish(&bsout);
+        if (inbuf != bsbuf) {
+            TCFREE(inbuf);
+        }
+        if (updobj != renameqf->updateobj) {
+            bson_del(updobj);
+        }
+        if (!rv) {
+            goto finish;
+        }
+       }
+    
+    if (unsetqf) { //$unset
+        char *inbuf = (bsout.finished) ? bsout.data : bsbuf;
+        if (bsout.finished) {
+            bson_init_size(&bsout, bson_size(&bsout));
+        } else {
+            assert(bsout.data == NULL);
+            bson_init_size(&bsout, bsbufsz);
+        }
+        int matched = 0;
+        bson *updobj = _qfgetupdateobj(unsetqf);
+        TCMAP *ifields = tcmapnew2(TCMAPTINYBNUM);
+        BSON_ITERATOR_INIT(&it, updobj);
+        while ((bt = bson_iterator_next(&it)) != BSON_EOO) {
+            const char *fpath = BSON_ITERATOR_KEY(&it);
+            tcmapput(ifields, fpath, strlen(fpath), &yes, sizeof(yes));
+        }
+        if (bson_strip(ifields, false, inbuf, &bsout, &matched) != BSON_OK) {
+            rv = false;
+            _ejdbsetecode(coll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__);
+        }
+        if (matched > 0) {
+            update++;
+        }
+        tcmapdel(ifields);
+        bson_finish(&bsout);
+        if (inbuf != bsbuf) {
+            TCFREE(inbuf);
+        }
+        if (updobj != unsetqf->updateobj) {
+            bson_del(updobj);
+        }
+        if (!rv) {
+            goto finish;
+        }
+    }
 
     if (setqf) { //$set
+        update++;
         bson *updobj = _qfgetupdateobj(setqf);
-        update = true;
-        bson_init_size(&bsout, bsbufsz);
+        char *inbuf = (bsout.finished) ? bsout.data : bsbuf;
+        if (bsout.finished) {
+            bson_init_size(&bsout, bson_size(&bsout));
+        } else {
+            assert(bsout.data == NULL);
+            bson_init_size(&bsout, bsbufsz);
+        }
         int err = bson_merge3(bsbuf, bson_data(updobj), &bsout);
         if (err) {
             rv = false;
             _ejdbsetecode(coll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__);
         }
         bson_finish(&bsout);
+        if (inbuf != bsbuf) {
+            TCFREE(inbuf);
+        }
         if (updobj != setqf->updateobj) {
             bson_del(updobj);
         }
-    }
-    if (!rv) {
-        goto finish;
+        if (!rv) {
+            goto finish;
+        }
     }
 
     if (incqf) { //$inc
@@ -3022,7 +3131,7 @@ static bool _qryupdate(_QRYCTX *ctx, void *bsbuf, int bsbufsz) {
                     _ejdbsetecode(coll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__);
                     break;
                 }
-                update = true;
+                update++;
             } else {
                 int64_t v = bson_iterator_long(&it2);
                 v += bson_iterator_long(&it);
@@ -3031,7 +3140,7 @@ static bool _qryupdate(_QRYCTX *ctx, void *bsbuf, int bsbufsz) {
                     _ejdbsetecode(coll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__);
                     break;
                 }
-                update = true;
+                update++;
             }
         }
         if (updobj != incqf->updateobj) {
@@ -3046,16 +3155,16 @@ static bool _qryupdate(_QRYCTX *ctx, void *bsbuf, int bsbufsz) {
         const EJQF *qf = pullqf[i];
         if (!qf) continue;
         char *inbuf = (bsout.finished) ? bsout.data : bsbuf;
-        if (bson_find_merged_array_sets(bson_data(qf->updateobj), inbuf, (qf->flags & EJCONDALL))) {
+        if (bson_find_merged_arrays(bson_data(qf->updateobj), inbuf, (qf->flags & EJCONDALL))) {
             if (bsout.finished) {
-                //reinit `bsout`, `inbuf` already points to `bsout.data` and will be freed later
                 bson_init_size(&bsout, bson_size(&bsout));
             } else {
                 assert(bsout.data == NULL);
                 bson_init_size(&bsout, bsbufsz);
             }
             //$pull $pullAll merge
-            if (bson_merge_array_sets(bson_data(qf->updateobj), inbuf, true, (qf->flags & EJCONDALL), &bsout)) {
+            if (bson_merge_arrays(bson_data(qf->updateobj), inbuf, 
+                                  BSON_MERGE_ARRAY_PULL, (qf->flags & EJCONDALL), &bsout)) {
                 rv = false;
                 _ejdbsetecode(coll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__);
             }
@@ -3063,28 +3172,51 @@ static bool _qryupdate(_QRYCTX *ctx, void *bsbuf, int bsbufsz) {
                 TCFREE(inbuf);
             }
             bson_finish(&bsout);
-            update = true;
+            update++;
         }
         if (!rv) {
             goto finish;
         }
     }
+    
+    for (int i = 0; i < 2; ++i) { //$push $pushAll
+        const EJQF *qf = pushqf[i];
+        if (!qf) continue;
+        char *inbuf = (bsout.finished) ? bsout.data : bsbuf;
+        if (bsout.finished) {
+            bson_init_size(&bsout, bson_size(&bsout));
+        } else {
+            assert(bsout.data == NULL);
+            bson_init_size(&bsout, bsbufsz);
+        }
+        //$push $pushAll merge
+        if (bson_merge_arrays(bson_data(qf->updateobj), inbuf, 
+                              BSON_MERGE_ARRAY_PUSH, (qf->flags & EJCONDALL), &bsout)) {
+            rv = false;
+            _ejdbsetecode(coll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__);
+        }
+        if (inbuf != bsbuf) {
+            TCFREE(inbuf);
+        }
+        bson_finish(&bsout);
+        update++;
+    }
 
     for (int i = 0; i < 2; ++i) { //$addToSet $addToSetAll
         const EJQF *qf = addsetqf[i];
         if (!qf) continue;
         char *inbuf = (bsout.finished) ? bsout.data : bsbuf;
-        if ((qf->flags & EJCONDALL) || bson_find_unmerged_array_sets(bson_data(qf->updateobj), inbuf)) {
+        if ((qf->flags & EJCONDALL) || bson_find_unmerged_arrays(bson_data(qf->updateobj), inbuf)) {
             //Missing $addToSet element in some array field found
             if (bsout.finished) {
-                //reinit `bsout`, `inbuf` already points to `bsout.data` and will be freed later
                 bson_init_size(&bsout, bson_size(&bsout));
             } else {
                 assert(bsout.data == NULL);
                 bson_init_size(&bsout, bsbufsz);
             }
             //$addToSet $addToSetAll merge
-            if (bson_merge_array_sets(bson_data(qf->updateobj), inbuf, false, (qf->flags & EJCONDALL), &bsout)) {
+            if (bson_merge_arrays(bson_data(qf->updateobj), inbuf, 
+                                  BSON_MERGE_ARRAY_ADDSET, (qf->flags & EJCONDALL), &bsout)) {
                 rv = false;
                 _ejdbsetecode(coll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__);
             }
@@ -3092,97 +3224,14 @@ static bool _qryupdate(_QRYCTX *ctx, void *bsbuf, int bsbufsz) {
                 TCFREE(inbuf);
             }
             bson_finish(&bsout);
-            update = true;
-        }
-    }
-
-    if (unsetqf) { //$unset
-        char *inbuf = (bsout.finished) ? bsout.data : bsbuf;
-        if (bsout.finished) {
-            //reinit `bsout`, `inbuf` already points to `bsout.data` and will be freed later
-            bson_init_size(&bsout, bson_size(&bsout));
-        } else {
-            assert(bsout.data == NULL);
-            bson_init_size(&bsout, bsbufsz);
-        }
-        bson *updobj = _qfgetupdateobj(unsetqf);
-        TCMAP *ifields = tcmapnew2(TCMAPTINYBNUM);
-        BSON_ITERATOR_INIT(&it, updobj);
-        while ((bt = bson_iterator_next(&it)) != BSON_EOO) {
-            const char *fpath = BSON_ITERATOR_KEY(&it);
-            tcmapput(ifields, fpath, strlen(fpath), &yes, sizeof(yes));
+            update++;
         }
-        if (bson_strip(ifields, false, inbuf, &bsout) != BSON_OK) {
-            rv = false;
-            _ejdbsetecode(coll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__);
-        }
-        tcmapdel(ifields);
-        if (inbuf != bsbuf) {
-            TCFREE(inbuf);
+        if (!rv) {
+            goto finish;
         }
-        bson_finish(&bsout);
-        update = true;
     }
-       if (!rv) {
-               goto finish;
-       }
-
-       if (renameqf) {
-        char *inbuf = (bsout.finished) ? bsout.data : bsbuf;
-        if (bsout.finished) {
-            //reinit `bsout`, `inbuf` already points to `bsout.data` and will be freed later
-            bson_init_size(&bsout, bson_size(&bsout));
-        } else {
-            assert(bsout.data == NULL);
-            bson_init_size(&bsout, bsbufsz);
-        }
-
-        TCMAP *efields = tcmapnew2(TCMAPTINYBNUM);
-               bson *updobj = _qfgetupdateobj(renameqf);
-               BSON_ITERATOR_INIT(&it, updobj);
-               while (rv && (bt = bson_iterator_next(&it)) != BSON_EOO) {
-                       if (bt != BSON_STRING) {
-                               continue;
-                       }
-            const char *ofpath = BSON_ITERATOR_KEY(&it);
-                       const char *nfpath = bson_iterator_string(&it);
-                       
-                       BSON_ITERATOR_FROM_BUFFER(&it2, inbuf);
-            bt2 = bson_find_from_buffer(&it2, inbuf, ofpath);
-                       if (bt2 == BSON_EOO) { 
-                               continue;
-                       }
-
-                       if (bson_append_field_from_iterator2(nfpath, &it2, &bsout) != BSON_OK) {
-                               rv = false;
-                               _ejdbsetecode(coll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__);
-                               break;
-                       }
-                       tcmapputkeep(efields, ofpath, strlen(ofpath), "", 0);
-                       tcmapputkeep(efields, nfpath, strlen(nfpath), "", 0);
-               }
-               
-        BSON_ITERATOR_FROM_BUFFER(&it, inbuf);
-        while (rv && (bt = bson_iterator_next(&it)) != BSON_EOO) {
-            const char *fpath = BSON_ITERATOR_KEY(&it);
-                       if (tcmapget2(efields, fpath)) { 
-                               continue;
-                       }
-                       
-                       if (bson_append_field_from_iterator(&it, &bsout) != BSON_OK) {
-                               rv = false;
-                               _ejdbsetecode(coll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__);
-                               break;
-                       }
-               }
-        tcmapdel(efields);
-        if (inbuf != bsbuf) {
-            TCFREE(inbuf);
-        }
-        bson_finish(&bsout);
-        update = true;
-       }
-
+    
+    //Finishing document update
     if (!update || !rv) {
         goto finish;
     }
@@ -4726,9 +4775,11 @@ static int _parse_qobj_impl(EJDB *jb, EJQ *q, bson_iterator *it, TCLIST *qlist,
                        !strcmp("$dropall", fkey) ||
                        !strcmp("$addToSet", fkey) ||
                        !strcmp("$addToSetAll", fkey) ||
-                       !strcmp("$upsert", fkey) ||
+                       !strcmp("$push", fkey) ||
+                       !strcmp("$pushAll", fkey) ||
                        !strcmp("$pull", fkey) ||
                        !strcmp("$pullAll", fkey) ||
+                       !strcmp("$upsert", fkey) ||
                        !strcmp("$do", fkey) ||
                        !strcmp("$unset", fkey) ||
                        !strcmp("$rename", fkey)
@@ -4861,23 +4912,28 @@ static int _parse_qobj_impl(EJDB *jb, EJQ *q, bson_iterator *it, TCLIST *qlist,
                     if (!strcmp("$inc", fkey)) {
                         qf.flags |= EJCONDINC;
                     } else if (!pqf) { //top level op
-                        if (!strcmp("$set", fkey)) { //top level set OP
+                        if (!strcmp("$do", fkey)) {
+                            qf.flags |= EJCONDOIT;
+                        } else if (!strcmp("$set", fkey)) { //top level set OP
                             qf.flags |= EJCONDSET;
                         } else if (!strcmp("$addToSet", fkey)) {
                             qf.flags |= EJCONDADDSET;
-                        } else if (!strcmp("$pull", fkey)) {
-                            qf.flags |= EJCONDPULL;
-                        } else if (!strcmp("$upsert", fkey)) {
-                            qf.flags |= EJCONDSET;
-                            qf.flags |= EJCONDUPSERT;
                         } else if (!strcmp("$addToSetAll", fkey)) {
                             qf.flags |= EJCONDADDSET;
                             qf.flags |= EJCONDALL;
+                        } else if (!strcmp("$push", fkey)) {
+                            qf.flags |= EJCONDPUSH;
+                        } else if (!strcmp("$pushAll", fkey)) {
+                            qf.flags |= EJCONDPUSH;
+                            qf.flags |= EJCONDALL;
+                        } else if (!strcmp("$pull", fkey)) {
+                            qf.flags |= EJCONDPULL;
                         } else if (!strcmp("$pullAll", fkey)) {
                             qf.flags |= EJCONDPULL;
                             qf.flags |= EJCONDALL;
-                        } else if (!strcmp("$do", fkey)) {
-                            qf.flags |= EJCONDOIT;
+                        } else if (!strcmp("$upsert", fkey)) {
+                            qf.flags |= EJCONDSET;
+                            qf.flags |= EJCONDUPSERT;
                         } else if (!strcmp("$unset", fkey)) {
                             qf.flags |= EJCONDUNSET;
                         } else if (!strcmp("$rename", fkey)) {
@@ -4885,7 +4941,10 @@ static int _parse_qobj_impl(EJDB *jb, EJQ *q, bson_iterator *it, TCLIST *qlist,
                         }
                     }
 
-                    if ((qf.flags & (EJCONDSET | EJCONDINC | EJCONDADDSET | EJCONDPULL | EJCONDUNSET | EJCONDRENAME))) {
+                    if ((qf.flags & 
+                          (EJCONDSET | EJCONDINC | EJCONDADDSET | EJCONDPULL | EJCONDPUSH | 
+                           EJCONDUNSET | EJCONDRENAME))) {
+                               
                         assert(qf.updateobj == NULL);
                         qf.q->flags |= EJQUPDATING;
                         qf.updateobj = bson_create();
@@ -4895,10 +4954,11 @@ static int _parse_qobj_impl(EJDB *jb, EJQ *q, bson_iterator *it, TCLIST *qlist,
                         BSON_ITERATOR_SUBITERATOR(it, &sit);
                         while ((sbt = bson_iterator_next(&sit)) != BSON_EOO) {
                             if ((qf.flags & EJCONDALL) && sbt != BSON_ARRAY) {
-                                //$addToSetAll & $pullAll accepts only a"$set"rrays
+                                //addToSet, push, pull accept only an arrays
                                 continue;
                             }
-                            if (!(qf.flags & EJCONDALL) && (qf.flags & (EJCONDSET | EJCONDINC | EJCONDUNSET | EJCONDRENAME))) { //Checking the $(query) positional operator.
+                            if (!(qf.flags & EJCONDALL) && 
+                                 (qf.flags & (EJCONDSET | EJCONDINC | EJCONDUNSET | EJCONDRENAME))) { //Checking the $(query) positional operator.
                                 const char* ukey = BSON_ITERATOR_KEY(&sit);
                                 char *pptr;
                                 if ((pptr = strstr(ukey, ".$")) && pptr && (*(pptr + 2) == '\0' || *(pptr + 2) == '.')) {// '.$' || '.$.'
index 90ae8f2..cc87b7a 100644 (file)
@@ -71,29 +71,29 @@ enum { /** Error codes */
 };
 
 enum { /** Database open modes */
-    JBOREADER = 1 << 0, /**< Open as a reader. */
-    JBOWRITER = 1 << 1, /**< Open as a writer. */
-    JBOCREAT = 1 << 2, /**< Create if db file not exists. */
-    JBOTRUNC = 1 << 3, /**< Truncate db on open. */
-    JBONOLCK = 1 << 4, /**< Open without locking. */
-    JBOLCKNB = 1 << 5, /**< Lock without blocking. */
-    JBOTSYNC = 1 << 6 /**< Synchronize every transaction. */
+    JBOREADER = 1u << 0, /**< Open as a reader. */
+    JBOWRITER = 1u << 1, /**< Open as a writer. */
+    JBOCREAT = 1u << 2, /**< Create if db file not exists. */
+    JBOTRUNC = 1u << 3, /**< Truncate db on open. */
+    JBONOLCK = 1u << 4, /**< Open without locking. */
+    JBOLCKNB = 1u << 5, /**< Lock without blocking. */
+    JBOTSYNC = 1u << 6 /**< Synchronize every transaction. */
 };
 
 enum { /** Index modes, index types. */
-    JBIDXDROP = 1 << 0, /**< Drop index. */
-    JBIDXDROPALL = 1 << 1, /**< Drop index for all types. */
-    JBIDXOP = 1 << 2, /**< Optimize indexes. */
-    JBIDXREBLD = 1 << 3, /**< Rebuild index. */
-    JBIDXNUM = 1 << 4, /**< Number index. */
-    JBIDXSTR = 1 << 5, /**< String index.*/
-    JBIDXARR = 1 << 6, /**< Array token index. */
-    JBIDXISTR = 1 << 7 /**< Case insensitive string index */
+    JBIDXDROP = 1u << 0, /**< Drop index. */
+    JBIDXDROPALL = 1u << 1, /**< Drop index for all types. */
+    JBIDXOP = 1u << 2, /**< Optimize indexes. */
+    JBIDXREBLD = 1u << 3, /**< Rebuild index. */
+    JBIDXNUM = 1u << 4, /**< Number index. */
+    JBIDXSTR = 1u << 5, /**< String index.*/
+    JBIDXARR = 1u << 6, /**< Array token index. */
+    JBIDXISTR = 1u << 7 /**< Case insensitive string index */
 };
 
 enum { /*< Query search mode flags in ejdbqryexecute() */
-    JBQRYCOUNT = 1, /*< Query only count(*) */
-    JBQRYFINDONE = 1 << 1 /*< Fetch first record only */
+    JBQRYCOUNT = 1u, /*< Query only count(*) */
+    JBQRYFINDONE = 1u << 1 /*< Fetch first record only */
 };
 
 /**
index 206ac51..4bb03ff 100644 (file)
@@ -50,36 +50,37 @@ struct EJDB {
 
 enum { /**> Query field flags */
     // Comparison flags
-    EJCOMPGT = 1, /**> Comparison GT */
-    EJCOMPGTE = 1 << 1, /**> Comparison GTE */
-    EJCOMPLT = 1 << 2, /**> Comparison LT */
-    EJCOMPLTE = 1 << 3, /**> Comparison LTE */
-    EJCONDSTARTWITH = 1 << 4, /**> Starts with */
-
-    EJFEXCLUDED = 1 << 5, /**> If query field excluded from matching */
-    EJFNOINDEX = 1 << 6, /**> Do not use index for field */
-    EJFORDERUSED = 1 << 7, /**> This ordering field was used */
-    EJFPKMATCHING = 1 << 8, /**> _id PK field matching */
-
-    EJCONDICASE = 1 << 9, /**> Ignore case in matching */
-
-    EJCONDSET = 1 << 10, /**> $set Set field update operation */
-    EJCONDINC = 1 << 11, /**> $inc Inc field update operation */
-    EJCONDADDSET = 1 << 12, /**> $addToSet Adds value to the array only if its not in the array already.  */
-    EJCONDPULL = 1 << 13, /**> $pull Removes all occurrences of value from field, if field is an array */
-    EJCONDUPSERT = 1 << 14, /**> $upsert Upsert $set operation */
-    EJCONDALL = 1 << 15, /**> 'All' modificator for $pull or $addToSet ($addToSetAll or $pullAll) */
-    EJCONDOIT = 1 << 16, /**> $do query field operation */
-    EJCONDUNSET = 1 << 17, /**> $unset Field value */
-    EJCONDRENAME = 1 << 18 /**> $rename Field value */
+    EJCOMPGT = 1u, /**> Comparison GT */
+    EJCOMPGTE = 1u << 1, /**> Comparison GTE */
+    EJCOMPLT = 1u << 2, /**> Comparison LT */
+    EJCOMPLTE = 1u << 3, /**> Comparison LTE */
+    EJCONDSTARTWITH = 1u << 4, /**> Starts with */
+
+    EJFEXCLUDED = 1u << 5, /**> If query field excluded from matching */
+    EJFNOINDEX = 1u << 6, /**> Do not use index for field */
+    EJFORDERUSED = 1u << 7, /**> This ordering field was used */
+    EJFPKMATCHING = 1u << 8, /**> _id PK field matching */
+
+    EJCONDICASE = 1u << 9, /**> Ignore case in matching */
+
+    EJCONDSET = 1u << 10, /**> $set Set field update operation */
+    EJCONDINC = 1u << 11, /**> $inc Inc field update operation */
+    EJCONDADDSET = 1u << 12, /**> $addToSet, $addToSetAll. Adds a value to the array only if its not in the array already.  */
+    EJCONDPULL = 1u << 13, /**> $pull Removes all occurrences of value from field, if field is an array */
+    EJCONDUPSERT = 1u << 14, /**> $upsert Upsert $set operation */
+    EJCONDALL = 1u << 15, /**> 'All' modificator for $pull(All), $addToSet(All), $push(All) */
+    EJCONDOIT = 1u << 16, /**> $do query field operation */
+    EJCONDUNSET = 1u << 17, /**> $unset Field value */
+    EJCONDRENAME = 1u << 18, /**> $rename Field value */
+    EJCONDPUSH  = 1u << 19 /**> $push, $pushAll. Adds a value to the array */
 };
 
 enum { /**> Query flags */
     EJQINTERNAL = 1, /**> Internal query object used in _ejdbqryexecute */
-    EJQUPDATING = 1 << 1, /**> Query in updating mode */
-    EJQDROPALL = 1 << 2, /**> Drop bson object if matched */
-    EJQONLYCOUNT = 1 << 3, /**> Only count mode */
-    EJQHASUQUERY = 1 << 4 /**> It means the query contains update $(query) fields #91 */
+    EJQUPDATING = 1u << 1, /**> Query in updating mode */
+    EJQDROPALL = 1u << 2, /**> Drop bson object if matched */
+    EJQONLYCOUNT = 1u << 3, /**> Only count mode */
+    EJQHASUQUERY = 1u << 4 /**> It means the query contains update $(query) fields #91 */
 };
 
 typedef struct { /**> $(query) matchin slot used in update $ placeholder processing. #91 */
index 42c480c..f8f3d98 100644 (file)
@@ -3983,6 +3983,114 @@ void testTicket123(void) {
     ejdbquerydel(q1);
 }
 
+void testPush(void) {
+    EJCOLL *coll = ejdbcreatecoll(jb, "ticket123", NULL);
+    CU_ASSERT_PTR_NOT_NULL_FATAL(coll);
+    
+    bson bsq1;
+    bson_init_as_query(&bsq1);
+    bson_append_start_object(&bsq1, "$push");
+    bson_append_string(&bsq1, "abc.g", "f");
+    bson_append_finish_object(&bsq1);
+    bson_finish(&bsq1);
+    CU_ASSERT_FALSE_FATAL(bsq1.err);
+    
+    EJQ *q1 = ejdbcreatequery(jb, &bsq1, NULL, 0, NULL);
+    CU_ASSERT_PTR_NOT_NULL_FATAL(q1);
+    uint32_t count = 0;
+    TCXSTR *log = tcxstrnew();
+    ejdbqryexecute(coll, q1, &count, JBQRYCOUNT, log);
+    CU_ASSERT_PTR_NOT_NULL(strstr(TCXSTRPTR(log), "UPDATING MODE: YES"));
+    CU_ASSERT_PTR_NOT_NULL(strstr(TCXSTRPTR(log), "RS COUNT: 1"));
+    CU_ASSERT_PTR_NOT_NULL(strstr(TCXSTRPTR(log), "RS SIZE: 0"));
+    //fprintf(stderr, "%s", TCXSTRPTR(log));
+    bson_destroy(&bsq1);
+    tcxstrdel(log);
+    ejdbquerydel(q1);
+    
+    //check updated  data
+    bson_init_as_query(&bsq1);
+    bson_finish(&bsq1);
+    CU_ASSERT_FALSE_FATAL(bsq1.err);
+
+    q1 = ejdbcreatequery(jb, &bsq1, NULL, 0, NULL);
+    CU_ASSERT_PTR_NOT_NULL_FATAL(q1);
+    log = tcxstrnew();
+    TCLIST *q1res = ejdbqryexecute(coll, q1, &count, 0, log);
+
+    CU_ASSERT_EQUAL(TCLISTNUM(q1res), 1);
+    //fprintf(stderr, "\n\n%s", TCXSTRPTR(log));
+
+    for (int i = 0; i < TCLISTNUM(q1res); ++i) {
+        CU_ASSERT_FALSE(bson_compare_string("g", TCLISTVALPTR(q1res, i), "abc.de.0"));
+        CU_ASSERT_FALSE(bson_compare_string("f", TCLISTVALPTR(q1res, i), "abc.g.0"));
+        CU_ASSERT_FALSE(bson_compare_string("f", TCLISTVALPTR(q1res, i), "abc.g.1"));
+    }
+
+    bson_destroy(&bsq1);
+    tclistdel(q1res);
+    tcxstrdel(log);
+    ejdbquerydel(q1);
+}
+
+
+void testPushAll(void) {
+    EJCOLL *coll = ejdbcreatecoll(jb, "ticket123", NULL);
+    CU_ASSERT_PTR_NOT_NULL_FATAL(coll);
+    
+    bson bsq1;
+    bson_init_as_query(&bsq1);
+    bson_append_start_object(&bsq1, "$pushAll");
+    bson_append_start_array(&bsq1, "abc.g");
+    bson_append_string(&bsq1, "0", "h");
+    bson_append_string(&bsq1, "1", "h");
+    bson_append_int(&bsq1, "2", 11);
+    bson_append_finish_array(&bsq1);
+    bson_append_finish_object(&bsq1);
+    bson_finish(&bsq1);
+    CU_ASSERT_FALSE_FATAL(bsq1.err);
+    
+    EJQ *q1 = ejdbcreatequery(jb, &bsq1, NULL, 0, NULL);
+    CU_ASSERT_PTR_NOT_NULL_FATAL(q1);
+    uint32_t count = 0;
+    TCXSTR *log = tcxstrnew();
+    ejdbqryexecute(coll, q1, &count, JBQRYCOUNT, log);
+    CU_ASSERT_PTR_NOT_NULL(strstr(TCXSTRPTR(log), "UPDATING MODE: YES"));
+    CU_ASSERT_PTR_NOT_NULL(strstr(TCXSTRPTR(log), "RS COUNT: 1"));
+    CU_ASSERT_PTR_NOT_NULL(strstr(TCXSTRPTR(log), "RS SIZE: 0"));
+    //fprintf(stderr, "%s", TCXSTRPTR(log));
+    bson_destroy(&bsq1);
+    tcxstrdel(log);
+    ejdbquerydel(q1);
+    
+    //check updated  data
+    bson_init_as_query(&bsq1);
+    bson_finish(&bsq1);
+    CU_ASSERT_FALSE_FATAL(bsq1.err);
+
+    q1 = ejdbcreatequery(jb, &bsq1, NULL, 0, NULL);
+    CU_ASSERT_PTR_NOT_NULL_FATAL(q1);
+    log = tcxstrnew();
+    TCLIST *q1res = ejdbqryexecute(coll, q1, &count, 0, log);
+
+    CU_ASSERT_EQUAL(TCLISTNUM(q1res), 1);
+    //fprintf(stderr, "\n\n%s", TCXSTRPTR(log));
+
+    for (int i = 0; i < TCLISTNUM(q1res); ++i) {
+        CU_ASSERT_FALSE(bson_compare_string("g", TCLISTVALPTR(q1res, i), "abc.de.0"));
+        CU_ASSERT_FALSE(bson_compare_string("f", TCLISTVALPTR(q1res, i), "abc.g.0"));
+        CU_ASSERT_FALSE(bson_compare_string("f", TCLISTVALPTR(q1res, i), "abc.g.1"));
+        CU_ASSERT_FALSE(bson_compare_string("h", TCLISTVALPTR(q1res, i), "abc.g.2"));
+        CU_ASSERT_FALSE(bson_compare_string("h", TCLISTVALPTR(q1res, i), "abc.g.3"));
+        CU_ASSERT_FALSE(bson_compare_long(11, TCLISTVALPTR(q1res, i), "abc.g.4"));
+    }
+
+    bson_destroy(&bsq1);
+    tclistdel(q1res);
+    tcxstrdel(log);
+    ejdbquerydel(q1);
+}
+
 
 void testPull(void) {
     EJCOLL *coll = ejdbcreatecoll(jb, "contacts", NULL);
@@ -4019,7 +4127,7 @@ void testPull(void) {
     CU_ASSERT_PTR_NOT_NULL_FATAL(q1);
     log = tcxstrnew();
     TCLIST *q1res = ejdbqryexecute(coll, q1, &count, 0, log);
-    \
+    
     CU_ASSERT_EQUAL(TCLISTNUM(q1res), 1);
     //fprintf(stderr, "\n\n%s", TCXSTRPTR(log));
 
@@ -5898,6 +6006,8 @@ int main() {
             (NULL == CU_add_test(pSuite, "testOneFieldManyConditions", testOneFieldManyConditions)) ||
             (NULL == CU_add_test(pSuite, "testAddToSet", testAddToSet)) ||
             (NULL == CU_add_test(pSuite, "testTicket123", testTicket123)) ||
+            (NULL == CU_add_test(pSuite, "testPush", testPush)) ||
+            (NULL == CU_add_test(pSuite, "testPushAll", testPushAll)) ||
             (NULL == CU_add_test(pSuite, "testPull", testPull)) ||
             (NULL == CU_add_test(pSuite, "testFindInComplexArray", testFindInComplexArray)) ||
             (NULL == CU_add_test(pSuite, "testElemMatch", testElemMatch)) ||