Fix: $rename can operate on nested json objects #107
authorAnton Adamansky <adamansky@gmail.com>
Wed, 29 Apr 2015 06:50:31 +0000 (12:50 +0600)
committerAnton Adamansky <adamansky@gmail.com>
Wed, 29 Apr 2015 06:50:31 +0000 (12:50 +0600)
Changelog
src/bson/bson.c
src/bson/bson.h
src/ejdb/ejdb.c
src/ejdb/ejdb.h
src/ejdb/tests/ejdbtest2.c

index f5a8369..2ab202b 100644 (file)
--- a/Changelog
+++ b/Changelog
@@ -1,6 +1,8 @@
 ejdb (1.2.8) UNRELEASED; urgency=low
 
   * Fix: Problem with the bson2json conversion dealing with doubles #135
+  *    $push and $pushAll operations are implemented. #130
+  * Fix: $rename can operate on nested json objects #107
 
  -- Anton Adamansky <adamansky@gmail.com>  Mon, 27 Apr 2015 21:30:11 +0600
 
index 28d7cf5..7d22a7e 100644 (file)
@@ -1500,8 +1500,12 @@ typedef struct {
     int matched; //number of matched merge fields
 } _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) {
+static bson_visitor_cmd_t _bson_merge_fieldpaths_visitor(
+        const char *ipath, int ipathlen, 
+        const char *key, int keylen,
+        const bson_iterator *it, 
+        bool after, void *op) {
+            
     _BSONMERGE3CTX *ctx = op;
     assert(ctx && ctx->bsout && ctx->mfields && ipath && key && it && op);
     const void *buf;
@@ -1571,7 +1575,7 @@ static bson_visitor_cmd_t _bson_merge3_visitor(const char *ipath, int ipathlen,
 
 //merge with fpath support
 
-int bson_merge3(const void *bsdata1, const void *bsdata2, bson *out) {
+int bson_merge_fieldpaths(const void *bsdata1, const void *bsdata2, bson *out) {
     assert(bsdata1 && bsdata2 && out);
     bson_iterator it1, it2;
     bson_type bt;
@@ -1592,7 +1596,7 @@ int bson_merge3(const void *bsdata1, const void *bsdata2, bson *out) {
         off_t it2off = (it2.cur - it2start);
         tcmapput(mfields, key, strlen(key), &it2off, sizeof (it2off));
     }
-    bson_visit_fields(&it1, 0, _bson_merge3_visitor, &ctx);
+    bson_visit_fields(&it1, 0, _bson_merge_fieldpaths_visitor, &ctx);
     assert(ctx.nstack == 0);
     if (TCMAPRNUM(mfields) == 0) { //all merge fields applied
         tcmapdel(mfields);
@@ -1651,7 +1655,9 @@ int bson_merge(const bson *b1, const bson *b2, bson_bool_t overwrite, bson *out)
 int bson_merge_recursive2(const void *b1data, const void *b2data, bson_bool_t overwrite, bson *out) {
     bson_iterator it1, it2;
     bson_type bt1, bt2;
-
+    if (out->finished) {
+        return BSON_ERROR;
+    }
     BSON_ITERATOR_FROM_BUFFER(&it1, b1data);
     BSON_ITERATOR_FROM_BUFFER(&it2, b2data);
     //Append all fields in B1 merging with fields in B2 (for nested objects & arrays)
@@ -1719,8 +1725,12 @@ typedef struct {
 } _BSONSTRIPVISITORCTX;
 
 /* Discard excluded fields from BSON */
-static bson_visitor_cmd_t _bsonstripvisitor_exclude(const char *ipath, int ipathlen, const char *key, int keylen,
-        const bson_iterator *it, bool after, void *op) {
+static bson_visitor_cmd_t _bsonstripvisitor_exclude(
+        const char *ipath, int ipathlen, 
+        const char *key, int keylen,
+        const bson_iterator *it, 
+        bool after, void *op) {
+    
     _BSONSTRIPVISITORCTX *ictx = op;
     assert(ictx);
     BSONSTRIPCTX *sctx = ictx->sctx;
@@ -1769,6 +1779,21 @@ static bson_visitor_cmd_t _bsonstripvisitor_exclude(const char *ipath, int ipath
             return (BSON_VCMD_SKIP_NESTED | BSON_VCMD_SKIP_AFTER);
         }
     } else {
+        if (sctx->collector) {
+            const char *k = NULL;
+            char cpath[BSON_MAX_FPATH_LEN + 1];
+            assert(ipathlen <= BSON_MAX_FPATH_LEN && !sctx->collector->finished);
+            if (sctx->fkfields) {
+                k = tcmapget(sctx->fkfields, ipath, ipathlen, &bufsz);
+            }
+            if (!k) {
+                memcpy(cpath, ipath, ipathlen);
+                cpath[ipathlen] = '\0'; 
+                k = cpath;
+            }
+            bson_iterator cit = *it;
+            bson_append_field_from_iterator2(k, &cit, sctx->collector);
+        }
         if (!after && ictx->astack > 0 && bson_isnumstr(key, keylen)) {
             bson_append_undefined(sctx->bsout, key);
         }
@@ -1778,8 +1803,11 @@ static bson_visitor_cmd_t _bsonstripvisitor_exclude(const char *ipath, int ipath
 }
 
 /* Accept only included fields into BSON */
-static bson_visitor_cmd_t _bsonstripvisitor_include(const char *ipath, int ipathlen, const char *key, int keylen,
+static bson_visitor_cmd_t _bsonstripvisitor_include(
+        const char *ipath, int ipathlen, 
+        const char *key, int keylen,
         const bson_iterator *it, bool after, void *op) {
+    
     _BSONSTRIPVISITORCTX *ictx = op;
     assert(ictx);
     BSONSTRIPCTX *sctx = ictx->sctx;
@@ -1892,6 +1920,46 @@ int bson_strip2(BSONSTRIPCTX *sctx) {
     return bson_finish(sctx->bsout);
 }
 
+int bson_rename(TCMAP *fields, const void *bsbuf, bson *bsout, int *rencnt) {
+    *rencnt = 0;
+    if (TCMAPRNUM(fields) == 0) {
+        return BSON_OK; //nothing to rename
+    }
+    
+    int rv;
+    bson res, collector;
+    bson_init(&res);
+    bson_init(&collector);
+    
+    BSONSTRIPCTX sctx = {
+        .ifields = fields,
+        .imode = false,
+        .bsbuf = bsbuf,
+        .bsout = &res,
+        .collector = &collector,
+        .fkfields = fields
+    };
+    if ((rv = bson_strip2(&sctx)) != BSON_OK) {
+        goto finish;
+    }
+    if ((rv = bson_finish(&res)) != BSON_OK) {
+        goto finish;
+    }
+    if ((rv = bson_finish(&collector)) != BSON_OK) {
+        goto finish;
+    }
+    if ((rv = bson_merge_fieldpaths(bson_data(&res), 
+                                    bson_data(&collector), 
+                                    bsout)) != BSON_OK) {
+        goto finish;
+    }
+    *rencnt = sctx.matched;
+finish:
+    bson_destroy(&res);
+    bson_destroy(&collector);
+    return rv;
+}
+
 int bson_inplace_set_bool(bson_iterator *pos, bson_bool_t val) {
     assert(pos);
     bson_type bt = BSON_ITERATOR_TYPE(pos);
index 26947e2..37fe385 100644 (file)
@@ -234,8 +234,15 @@ typedef enum {
     BSON_VCMD_SKIP_NESTED = 1 << 1,
     BSON_VCMD_SKIP_AFTER = 1 << 2
 } bson_visitor_cmd_t;
-typedef bson_visitor_cmd_t(*BSONVISITOR)(const char *ipath, int ipathlen, const char *key, int keylen, const bson_iterator *it, bool after, void *op);
-EJDB_EXPORT void bson_visit_fields(bson_iterator *it, bson_traverse_flags_t flags, BSONVISITOR visitor, void *op);
+
+typedef bson_visitor_cmd_t(*BSONVISITOR)(const char *ipath, int ipathlen, 
+                                         const char *key, int keylen, 
+                                         const bson_iterator *it, 
+                                         bool after, void *op);
+
+EJDB_EXPORT void bson_visit_fields(bson_iterator *it, 
+                                   bson_traverse_flags_t flags, 
+                                   BSONVISITOR visitor, void *op);
 
 
 EJDB_EXPORT bson_iterator* bson_iterator_create(void);
@@ -1149,19 +1156,20 @@ EJDB_EXPORT int bson_merge_recursive2(const void *b1data, const void *b2data, bs
  *
  * @return BSON_OK or BSON_ERROR.
  */
-EJDB_EXPORT int bson_merge3(const void *bsdata1, const void *bsdata2, bson *out);
+EJDB_EXPORT int bson_merge_fieldpaths(const void *bsdata1, const void *bsdata2, bson *out);
 
 EJDB_EXPORT int bson_inplace_set_bool(bson_iterator *pos, bson_bool_t val);
 EJDB_EXPORT int bson_inplace_set_long(bson_iterator *pos, int64_t val);
 EJDB_EXPORT int bson_inplace_set_double(bson_iterator *pos, double val);
 
 typedef struct {
-    TCMAP *ifields; //Required Map of fieldpaths. Map values are a simple boolean bufs.
-    bool imode; //Required If true fpaths will be included. Otherwise fpaths will be excluded from bson.
-    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
+    TCMAP *ifields;     //Required Map of fieldpaths. Map values are a simple boolean bufs.
+    bool imode;         //Required If true fpaths will be included. Otherwise fpaths will be excluded from bson.
+    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
+    bson *collector;    //Optional: Collector for excluded data (fieldpath -> excluded value)
 } BSONSTRIPCTX;
 
 /**
@@ -1171,13 +1179,26 @@ typedef struct {
  * @param ifields Map of fieldpaths. Map values are a simple boolean bufs.
  * @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 bsout Allocated and not finished output 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, int *matched);
 EJDB_EXPORT int bson_strip2(BSONSTRIPCTX *sctx);
 
+/**
+ * @brief Rename a fields specified by `fields` rename mapping.
+ * 
+ * This operation unsets both all and new fieldpaths and then sets 
+ * new fieldpath values. 
+ * 
+ * @param fields Rename mapping old `fieldpath` to new `fieldpath`.
+ * @param bsbuf BSON buffer to process.
+ * @param bsout Allocated and not finished output bson* object
+ * @param rencnt A number of fieldpaths actually renamed.
+ */
+EJDB_EXPORT int bson_rename(TCMAP *fields, const void *bsbuf, bson *bsout, int *rencnt);
+
 
 /**
  * Compares field path primitive values of two BSONs
index d4639a1..e168b5e 100644 (file)
@@ -2979,7 +2979,7 @@ static bool _qryupdate(_QRYCTX *ctx, void *bsbuf, int bsbufsz) {
         }
     }
     
-       if (renameqf) { //todo rename nested fields!
+       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
@@ -2988,47 +2988,31 @@ static bool _qryupdate(_QRYCTX *ctx, void *bsbuf, int bsbufsz) {
             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);
+        TCMAP *rfields = tcmapnew2(TCMAPTINYBNUM);
+        BSON_ITERATOR_INIT(&it, updobj);
+        while ((bt = bson_iterator_next(&it)) != BSON_EOO) {
+            if (bt != BSON_STRING) {
+                continue;
             }
-                       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);
+            const char *nfpath = bson_iterator_string(&it);
+            int nlen = bson_iterator_string_len(&it);
+            if (nlen == 0) {
+                continue;
+            }
+            tcmapputkeep(rfields, 
+                         BSON_ITERATOR_KEY(&it), strlen(BSON_ITERATOR_KEY(&it)), 
+                         nfpath, nlen);
+        }
+        int rencnt;
+        if (bson_rename(rfields, inbuf, &bsout, &rencnt) != BSON_OK) {
+            rv = false;
+            _ejdbsetecode(coll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__);    
+        }
+        if (rencnt > 0) {
+            update++;
         }
+        tcmapdel(rfields);
         bson_finish(&bsout);
         if (inbuf != bsbuf) {
             TCFREE(inbuf);
@@ -3049,7 +3033,7 @@ static bool _qryupdate(_QRYCTX *ctx, void *bsbuf, int bsbufsz) {
             assert(bsout.data == NULL);
             bson_init_size(&bsout, bsbufsz);
         }
-        int matched = 0;
+        int matched;
         bson *updobj = _qfgetupdateobj(unsetqf);
         TCMAP *ifields = tcmapnew2(TCMAPTINYBNUM);
         BSON_ITERATOR_INIT(&it, updobj);
@@ -3087,7 +3071,7 @@ static bool _qryupdate(_QRYCTX *ctx, void *bsbuf, int bsbufsz) {
             assert(bsout.data == NULL);
             bson_init_size(&bsout, bsbufsz);
         }
-        int err = bson_merge3(bsbuf, bson_data(updobj), &bsout);
+        int err = bson_merge_fieldpaths(bsbuf, bson_data(updobj), &bsout);
         if (err) {
             rv = false;
             _ejdbsetecode(coll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__);
index cc87b7a..f61150d 100644 (file)
@@ -36,11 +36,11 @@ typedef struct EJCOLL EJCOLL;
 struct EJQ; /**< EJDB query. */
 typedef struct EJQ EJQ;
 
-typedef struct { /**< EJDB collection tuning options. */
-    bool large; /**< Large collection. It can be larger than 2GB. Default false */
-    bool compressed; /**< Collection records will be compressed with DEFLATE compression. Default: false */
-    int64_t records; /**< Expected records number in the collection. Default: 128K */
-    int cachedrecords; /**< Maximum number of records cached in memory. Default: 0 */
+typedef struct {        /**< EJDB collection tuning options. */
+    bool large;         /**< Large collection. It can be larger than 2GB. Default false */
+    bool compressed;    /**< Collection records will be compressed with DEFLATE compression. Default: false */
+    int64_t records;    /**< Expected records number in the collection. Default: 128K */
+    int cachedrecords;  /**< Maximum number of records cached in memory. Default: 0 */
 } EJCOLLOPTS;
 
 
@@ -49,51 +49,51 @@ typedef TCLIST* EJQRESULT; /**< EJDB query result */
 #define JBMAXCOLNAMELEN 128
 
 enum { /** Error codes */
-    JBEINVALIDCOLNAME = 9000, /**< Invalid collection name. */
-    JBEINVALIDBSON = 9001, /**< Invalid bson object. */
-    JBEINVALIDBSONPK = 9002, /**< Invalid bson object id. */
+    JBEINVALIDCOLNAME = 9000,   /**< Invalid collection name. */
+    JBEINVALIDBSON = 9001,      /**< Invalid bson object. */
+    JBEINVALIDBSONPK = 9002,    /**< Invalid bson object id. */
     JBEQINVALIDQCONTROL = 9003, /**< Invalid query control field starting with '$'. */
-    JBEQINOPNOTARRAY = 9004, /**< $strand, $stror, $in, $nin, $bt keys requires not empty array value. */
-    JBEMETANVALID = 9005, /**< Inconsistent database metadata. */
-    JBEFPATHINVALID = 9006, /**< Invalid field path value. */
-    JBEQINVALIDQRX = 9007, /**< Invalid query regexp value. */
-    JBEQRSSORTING = 9008, /**< Result set sorting error. */
-    JBEQERROR = 9009, /**< Query generic error. */
-    JBEQUPDFAILED = 9010, /**< Updating failed. */
-    JBEQONEEMATCH = 9011, /**< Only one $elemMatch allowed in the fieldpath. */
-    JBEQINCEXCL = 9012, /**< $fields hint cannot mix include and exclude fields */
-    JBEQACTKEY = 9013, /**< action key in $do block can only be one of: $join, $slice */
-    JBEMAXNUMCOLS = 9014, /**< Exceeded the maximum number of collections per database */
-    JBEEI = 9015, /**< EJDB export/import error */
-    JBEEJSONPARSE = 9016, /**< JSON parsing failed */
-    JBETOOBIGBSON = 9017, /**< BSON size is too big */
-    JBEINVALIDCMD = 9018 /**< Invalid ejdb command specified */
+    JBEQINOPNOTARRAY = 9004,    /**< $strand, $stror, $in, $nin, $bt keys requires not empty array value. */
+    JBEMETANVALID = 9005,       /**< Inconsistent database metadata. */
+    JBEFPATHINVALID = 9006,     /**< Invalid field path value. */
+    JBEQINVALIDQRX = 9007,      /**< Invalid query regexp value. */
+    JBEQRSSORTING = 9008,       /**< Result set sorting error. */
+    JBEQERROR = 9009,           /**< Query generic error. */
+    JBEQUPDFAILED = 9010,       /**< Updating failed. */
+    JBEQONEEMATCH = 9011,       /**< Only one $elemMatch allowed in the fieldpath. */
+    JBEQINCEXCL = 9012,         /**< $fields hint cannot mix include and exclude fields */
+    JBEQACTKEY = 9013,          /**< action key in $do block can only be one of: $join, $slice */
+    JBEMAXNUMCOLS = 9014,       /**< Exceeded the maximum number of collections per database */
+    JBEEI = 9015,               /**< EJDB export/import error */
+    JBEEJSONPARSE = 9016,       /**< JSON parsing failed */
+    JBETOOBIGBSON = 9017,       /**< BSON size is too big */
+    JBEINVALIDCMD = 9018        /**< Invalid ejdb command specified */
 };
 
 enum { /** Database open modes */
-    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. */
+    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 = 1u << 0, /**< Drop 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 */
+    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 = 1u, /*< Query only count(*) */
-    JBQRYFINDONE = 1u << 1 /*< Fetch first record only */
+    JBQRYCOUNT = 1u,        /*< Query only count(*) */
+    JBQRYFINDONE = 1u << 1  /*< Fetch first record only */
 };
 
 /**
index f8f3d98..5eb94f6 100644 (file)
@@ -4091,6 +4091,60 @@ void testPushAll(void) {
     ejdbquerydel(q1);
 }
 
+void testRename2(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, "$rename");
+    bson_append_string(&bsq1, "abc.g", "abc.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.f.0"));
+        CU_ASSERT_FALSE(bson_compare_string("f", TCLISTVALPTR(q1res, i), "abc.f.1"));
+        CU_ASSERT_FALSE(bson_compare_string("h", TCLISTVALPTR(q1res, i), "abc.f.2"));
+        CU_ASSERT_FALSE(bson_compare_string("h", TCLISTVALPTR(q1res, i), "abc.f.3"));
+        CU_ASSERT_FALSE(bson_compare_long(11, TCLISTVALPTR(q1res, i), "abc.f.4"));
+    }
+
+    bson_destroy(&bsq1);
+    tclistdel(q1res);
+    tcxstrdel(log);
+    ejdbquerydel(q1);
+}
+
 
 void testPull(void) {
     EJCOLL *coll = ejdbcreatecoll(jb, "contacts", NULL);
@@ -6008,6 +6062,7 @@ int main() {
             (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, "testRename2", testRename2)) ||
             (NULL == CU_add_test(pSuite, "testPull", testPull)) ||
             (NULL == CU_add_test(pSuite, "testFindInComplexArray", testFindInComplexArray)) ||
             (NULL == CU_add_test(pSuite, "testElemMatch", testElemMatch)) ||