#88 new bson_merge3 with supporting of 'fpaths'
authoradam <adamansky@gmail.com>
Wed, 31 Jul 2013 16:04:36 +0000 (23:04 +0700)
committeradam <adamansky@gmail.com>
Wed, 31 Jul 2013 16:04:36 +0000 (23:04 +0700)
tcejdb/bson.c
tcejdb/bson.h
tcejdb/testejdb/t2.c

index 00a86f8..b277e0a 100644 (file)
@@ -1439,6 +1439,119 @@ int bson_append_field_from_iterator(const bson_iterator *from, bson *into) {
     return bson_append_field_from_iterator2(bson_iterator_key(from), from, into);
 }
 
+typedef struct {
+    bson *bsout;
+    TCMAP *mfields;
+    const void *bsdata2; //bsdata to merge with
+    int nstack; //nested object stack pos
+    int matched; //number of matched merge fields
+    bson_bool_t overwrite; //overwrite bson1 fields with merged
+} _BSON_MERGE3_CTX;
+
+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;
+    assert(ctx && ctx->bsout && ctx->mfields && ipath && key && it && op);
+
+    bson_visitor_cmd_t rv = BSON_VCMD_OK;
+    TCMAP *mfields = ctx->mfields;
+    const void *buf;
+    int bufsz;
+
+    bson_iterator it2;
+    bson_iterator_from_buffer(&it2, ctx->bsdata2);
+    const char *it2start = it2.cur;
+
+    bson_type bt = bson_iterator_type(it);
+    bson_bool_t allset = (ctx->matched == TCMAPRNUM(mfields));
+
+    if (bt != BSON_OBJECT && bt != BSON_ARRAY) {
+        if (after) { //simple primitive case
+            return BSON_VCMD_OK;
+        }
+        buf = allset ? NULL : tcmapget(mfields, ipath, ipathlen, &bufsz);
+        if (buf) {
+            ctx->matched++;
+            off_t it2off;
+            assert(bufsz == sizeof(it2off));
+            memcpy(&it2off, buf, sizeof(it2off));
+            assert(it2off >= 0);
+            it2.cur = it2start + it2off;
+            it2.first = (it2off == 0);
+            bson_append_field_from_iterator(&it2, ctx->bsout);
+        } else {
+            bson_append_field_from_iterator(it, ctx->bsout);
+        }
+        return (BSON_VCMD_SKIP_AFTER);
+    } else { //more complicated case
+        if (!after) {
+            buf = allset ? NULL : tcmapget(mfields, ipath, ipathlen, &bufsz);
+            if (buf) { //field hitted
+                ctx->matched++;
+                off_t it2off;
+                assert(bufsz == sizeof(it2off));
+                memcpy(&it2off, buf, sizeof(it2off));
+                assert(it2off >= 0);
+                it2.cur = it2start + it2off;
+                it2.first = (it2off == 0);
+                bson_append_field_from_iterator(&it2, ctx->bsout);
+                return (BSON_VCMD_SKIP_NESTED | BSON_VCMD_SKIP_AFTER);
+            } else { //replay object or array
+                ctx->nstack++;
+                if (bt == BSON_OBJECT) {
+                    bson_append_start_object(ctx->bsout, key);
+                } else if (bt == BSON_ARRAY) {
+                    bson_append_start_array(ctx->bsout, key);
+                } else {
+                    assert(0);
+                }
+            }
+        } else { //after
+            if (ctx->nstack > 0) {
+                --ctx->nstack;
+                if (bt == BSON_OBJECT) {
+                    bson_append_finish_object(ctx->bsout);
+                } else if (bt == BSON_ARRAY) {
+                    bson_append_finish_array(ctx->bsout);
+                } else {
+                    assert(0);
+                }
+            }
+        }
+    }
+    return rv;
+}
+
+
+//merge with fpath support
+int bson_merge3(const void *bsdata1, const void *bsdata2, bson_bool_t overwrite, bson *out) {
+    assert(bsdata1 && bsdata2 && out);
+    bson_iterator it1, it2;
+    bson_type bt;
+    bson_iterator_from_buffer(&it1, bsdata1);
+    bson_iterator_from_buffer(&it2, bsdata2);
+    const char *it2start = it2.cur;
+    TCMAP *mfields = tcmapnew2(TCMAPTINYBNUM);
+    _BSON_MERGE3_CTX ctx = {
+        .bsout = out,
+        .mfields = mfields,
+        .bsdata2 = bsdata2,
+        .matched = 0,
+        .nstack = 0,
+        .overwrite = overwrite
+    };
+    //collect fpaths of active bsons
+    while ((bt = bson_iterator_next(&it2)) != BSON_EOO) {
+        const char* key = bson_iterator_key(&it2);
+        off_t i2off = (it2.cur - it2start);
+        tcmapput(mfields, key, strlen(key), &i2off, sizeof (i2off));
+    }
+    bson_visit_fields(&it1, 0, _bson_merge3_visitor, &ctx);
+    assert(ctx.nstack == 0);
+    tcmapdel(mfields);
+    return BSON_OK;
+}
+
 int bson_merge2(const void *b1data, const void *b2data, bson_bool_t overwrite, bson *out) {
     bson_iterator it1, it2;
     bson_type bt1, bt2;
index 891c021..86d7a66 100644 (file)
@@ -1084,21 +1084,34 @@ EJDB_EXPORT int bson_append_array_from_iterator(const char *key, bson_iterator *
 
 
 /**
- * Merge bson  'b2' into 'b1' saving result the 'out' object.
- * 'b1' & 'b2' bson must be finished BSONS.
+ * Merge bson  `b2` into `b1` saving result the 'out' object.
+ * `b1` & `b2` bson must be finished BSONS.
  * Resulting 'out' bson must be allocated and not finished.
  *
  * Nested object skipped and usupported.
  *
- * @param b1 BSON to to be merged into out
- * @param b2 BSON to to be merged into out
- * @param overwrite if True All b1 fields will be overwriten by corresponding b2 fields
+ * @param b1 BSON to to be merged in `out`
+ * @param b2 Second BSON to to be merged in `out`
+ * @param overwrite if `true` all `b1` fields will be overwriten by corresponding `b2` fields
+ * @param out
  *
  * @return BSON_OK or BSON_ERROR.
  */
 EJDB_EXPORT int bson_merge(const bson *b1, const bson *b2, bson_bool_t overwrite, bson *out);
 EJDB_EXPORT int bson_merge2(const void *b1data, const void *b2data, bson_bool_t overwrite, bson *out);
 
+/**
+ * Merge bsons.
+ * `bsdata2` may contain field path keys (eg: 'foo.bar').
+ * @param bsdata1 BSON data to to be merged in `out`
+ * @param bsdata2 Second BSON data to to be merged in `out`
+ * @param overwrite if `true` all `bsdata1` fields will be overwriten by corresponding `bsdata2` fields
+ * @param out Resulting `out` bson must be allocated and not finished.
+ *
+ * @return BSON_OK or BSON_ERROR.
+ */
+EJDB_EXPORT int bson_merge3(const void *bsdata1, const void *bsdata2, bson_bool_t overwrite, 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);
index de4a371..235178d 100644 (file)
@@ -3137,6 +3137,10 @@ void testUpdate2() { //https://github.com/Softmotions/ejdb/issues/9
 
 }
 
+void testUpdate3() {
+
+}
+
 void testQueryBool() {
     EJCOLL *coll = ejdbcreatecoll(jb, "contacts", NULL);
     CU_ASSERT_PTR_NOT_NULL_FATAL(coll);