#11 & implemented
authoradam <adamansky@gmail.com>
Thu, 13 Dec 2012 18:28:13 +0000 (01:28 +0700)
committeradam <adamansky@gmail.com>
Thu, 13 Dec 2012 18:28:13 +0000 (01:28 +0700)
tcejdb/bson.c
tcejdb/bson.h
tcejdb/ejdb.c
tcejdb/testejdb/t2.c

index c3b0995..1e73bd5 100644 (file)
@@ -354,8 +354,8 @@ static void bson_visit_fields_impl(bson_traverse_flags_t flags, char* pstack, in
         if (curr > 0) {
             curr--; //remove leading dot
         }
-        }
     }
+}
 
 EJDB_EXPORT void bson_visit_fields(bson_iterator *it, bson_traverse_flags_t flags, BSONVISITOR visitor, void *op) {
     char pstack[BSON_MAX_FPATH_LEN + 1];
@@ -1594,10 +1594,56 @@ typedef struct {
     const void *mbuf;
     int mfields;
     int ecode;
-    int nstack;
     bool duty;
 } BSON_MASETS_CTX;
 
+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;
+    assert(ctx && ctx->mfields >= 0);
+    bson_iterator mit;
+    bson_type bt = bson_iterator_type(it);
+    if (bt != BSON_OBJECT && bt != BSON_ARRAY) { //simple primitive case
+        if (after) {
+            return BSON_VCMD_OK;
+        }
+        bson_append_field_from_iterator(it, ctx->bsout);
+        return (BSON_VCMD_SKIP_AFTER);
+    }
+    if (bt == BSON_ARRAY) {
+        bson_iterator_from_buffer(&mit, ctx->mbuf);
+        bt = bson_find_fieldpath_value2(fpath, fpathlen, &mit);
+        if (bt == BSON_EOO) {
+            bson_append_field_from_iterator(it, ctx->bsout);
+            return (BSON_VCMD_SKIP_NESTED | BSON_VCMD_SKIP_AFTER);
+        }
+        if (ctx->mfields > 0) {
+            --ctx->mfields;
+        }
+        //Find and merge
+        bson_iterator ait;
+        bson_iterator_subiterator(it, &ait);
+        bson_append_start_array(ctx->bsout, key);
+        int c = 0;
+        while ((bt = bson_iterator_next(&ait)) != BSON_EOO) {
+            if (!bson_compare_it_current(&ait, &mit)) {
+                continue;
+            }
+            char kbuf[TCNUMBUFSIZ];
+            bson_numstrn(kbuf, TCNUMBUFSIZ, c++);
+            bson_append_field_from_iterator2(kbuf, &ait, ctx->bsout);
+        }
+        bson_append_finish_array(ctx->bsout);
+        return (BSON_VCMD_SKIP_NESTED | BSON_VCMD_SKIP_AFTER);
+    }
+    //bt is BSON_OBJECT
+    if (!after) {
+        bson_append_start_object(ctx->bsout, key);
+    } else { //after
+        bson_append_finish_object(ctx->bsout);
+    }
+    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;
     assert(ctx && ctx->mfields >= 0);
@@ -1645,28 +1691,15 @@ static bson_visitor_cmd_t bson_merge_array_sets_tf(const char *fpath, int fpathl
     }
     //bt is BSON_OBJECT
     if (!after) {
-        bson_iterator_from_buffer(&mit, ctx->mbuf);
-        while ((bt = bson_iterator_next(&mit)) != BSON_EOO) {
-            const char *mkey = bson_iterator_key(&mit);
-            int i = 0;
-            for (; i < fpathlen && *(mkey + i) != '\0' && *(fpath + i) == *(mkey + i); ++i);
-            if (i == fpathlen) { //fpath prefixes some field
-                ctx->nstack++;
-                bson_append_start_object(ctx->bsout, key);
-                break;
-            }
-        }
+        bson_append_start_object(ctx->bsout, key);
     } else { //after
-        if (ctx->nstack > 0) {
-            --ctx->nstack;
-            bson_append_finish_object(ctx->bsout);
-        }
+        bson_append_finish_object(ctx->bsout);
     }
     return (BSON_VCMD_OK);
 
 }
 
-EJDB_EXPORT int bson_merge_array_sets(const void *mbuf, const void *inbuf, bson *bsout) {
+EJDB_EXPORT int bson_merge_array_sets(const void *mbuf, const void *inbuf, bool pull, bson *bsout) {
     assert(mbuf && inbuf && bsout);
     if (bsout->finished) {
         return BSON_ERROR;
@@ -1675,7 +1708,6 @@ EJDB_EXPORT int bson_merge_array_sets(const void *mbuf, const void *inbuf, bson
     ctx.bsout = bsout;
     ctx.mbuf = mbuf;
     ctx.mfields = 0;
-    ctx.nstack = 0;
     ctx.duty = false;
     ctx.ecode = BSON_OK;
 
@@ -1686,9 +1718,12 @@ EJDB_EXPORT int bson_merge_array_sets(const void *mbuf, const void *inbuf, bson
         ctx.mfields++;
     }
     bson_iterator_from_buffer(&it, inbuf);
-    bson_visit_fields(&it, BSON_TRAVERSE_ARRAYS_EXCLUDED, bson_merge_array_sets_tf, &ctx);
-    assert(ctx.nstack == 0);
-    if (ctx.mfields == 0) {
+    if (pull) {
+        bson_visit_fields(&it, BSON_TRAVERSE_ARRAYS_EXCLUDED, bson_merge_array_sets_pull_tf, &ctx);
+    } else {
+        bson_visit_fields(&it, BSON_TRAVERSE_ARRAYS_EXCLUDED, bson_merge_array_sets_tf, &ctx);
+    }
+    if (ctx.mfields == 0 || pull) {
         return ctx.ecode;
     }
     //Append missing arrays fields
index 9432b39..e041d57 100644 (file)
@@ -1129,7 +1129,7 @@ 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 bool bson_find_unmerged_array_sets(const void *mbuf, const void *inbuf);
-EJDB_EXPORT int bson_merge_array_sets(const void *mbuf, const void *inbuf, bson *bsout);
+EJDB_EXPORT int bson_merge_array_sets(const void *mbuf, const void *inbuf, bool pull, bson *bsout);
 
 
 EJDB_EXTERN_C_END
index 7ae1ba7..5ae6b94 100644 (file)
@@ -1751,6 +1751,32 @@ static bool _qryupdate(EJCOLL *jcoll, const EJQ *ejq, void *bsbuf, int bsbufsz,
         goto finish;
     }
 
+    if (pullqf) { //$pull
+        char* inbuf = (bsout.finished) ? bsout.data : bsbuf;
+        if (!bson_find_unmerged_array_sets(bson_data(pullqf->updateobj), inbuf)) {
+            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 merge
+            if (bson_merge_array_sets(bson_data(pullqf->updateobj), inbuf, true, &bsout)) {
+                rv = false;
+                _ejdbsetecode(jcoll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__);
+            }
+            if (inbuf != bsbuf) {
+                TCFREE(inbuf);
+            }
+            bson_finish(&bsout);
+            update = true;
+        }
+    }
+    if (!rv) {
+        goto finish;
+    }
+
     if (addsetqf) { //$addToSet
         char* inbuf = (bsout.finished) ? bsout.data : bsbuf;
         if (bson_find_unmerged_array_sets(bson_data(addsetqf->updateobj), inbuf)) {
@@ -1763,7 +1789,7 @@ static bool _qryupdate(EJCOLL *jcoll, const EJQ *ejq, void *bsbuf, int bsbufsz,
                 bson_init_size(&bsout, bsbufsz);
             }
             //$addToSet merge
-            if (bson_merge_array_sets(bson_data(addsetqf->updateobj), inbuf, &bsout)) {
+            if (bson_merge_array_sets(bson_data(addsetqf->updateobj), inbuf, false, &bsout)) {
                 rv = false;
                 _ejdbsetecode(jcoll->jb, JBEQUPDFAILED, __FILE__, __LINE__, __func__);
             }
@@ -1772,7 +1798,6 @@ static bool _qryupdate(EJCOLL *jcoll, const EJQ *ejq, void *bsbuf, int bsbufsz,
             }
             bson_finish(&bsout);
             update = true;
-            //bson_print(stderr, &bsout);
         }
     }
 
@@ -3054,7 +3079,8 @@ static int _parse_qobj_impl(EJDB *jb, EJQ *q, bson_iterator *it, TCLIST *qlist,
             if (!strcmp("$set", fkey) ||
                     !strcmp("$inc", fkey) ||
                     !strcmp("$dropall", fkey) ||
-                    !strcmp("$addToSet", fkey)) {
+                    !strcmp("$addToSet", fkey) ||
+                    !strcmp("$pull", fkey)) {
                 if (pqf) { //Top level ops
                     ret = JBEQERROR;
                     break;
@@ -3151,14 +3177,18 @@ static int _parse_qobj_impl(EJDB *jb, EJQ *q, bson_iterator *it, TCLIST *qlist,
             case BSON_OBJECT:
             {
                 if (isckey) {
-                    if (!pqf && !strcmp("$set", fkey)) { //top level set OP
-                        qf.flags |= EJCONDSET;
-                    } else if (!pqf && !strcmp("$addToSet", fkey)) {
-                        qf.flags |= EJCONDADDSET;
-                    } else if (!strcmp("$inc", fkey)) {
+                    if (!strcmp("$inc", fkey)) {
                         qf.flags |= EJCONDINC;
+                    } else if (!pqf) { //top level op
+                        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;
+                        }
                     }
-                    if ((qf.flags & (EJCONDSET | EJCONDINC | EJCONDADDSET))) {
+                    if ((qf.flags & (EJCONDSET | EJCONDINC | EJCONDADDSET | EJCONDPULL))) {
                         assert(qf.updateobj == NULL);
                         qf.q->flags |= EJQUPDATING;
                         qf.updateobj = bson_create();
index 7d5a145..78031e7 100644 (file)
@@ -3301,6 +3301,7 @@ void test$addToSet() {
     bson_append_start_object(&bsq1, "$addToSet");
     bson_append_string(&bsq1, "personal.tags", "tag2");
     bson_append_string(&bsq1, "labels", "green");
+    //bson_append_int(&bsq1, "scores", 1);
     bson_append_finish_object(&bsq1);
     bson_finish(&bsq1);
     CU_ASSERT_FALSE_FATAL(bsq1.err);
@@ -3317,7 +3318,7 @@ void test$addToSet() {
     bson_destroy(&bsq1);
     tcxstrdel(log);
     ejdbquerydel(q1);
-    
+
 
     //check updated  data
     bson_init_as_query(&bsq1);
@@ -3333,7 +3334,114 @@ void test$addToSet() {
     //fprintf(stderr, "\n\n%s", TCXSTRPTR(log));
 
     for (int i = 0; i < TCLISTNUM(q1res); ++i) {
-        bson_print(stderr, TCLISTVALPTR(q1res, i));
+        //bson_print_raw(stderr, TCLISTVALPTR(q1res, i), 0);
+        CU_ASSERT_FALSE(bson_compare_string("tag1", TCLISTVALPTR(q1res, i), "personal.tags.0"));
+        CU_ASSERT_FALSE(bson_compare_string("tag2", TCLISTVALPTR(q1res, i), "personal.tags.1"));
+        CU_ASSERT_FALSE(bson_compare_string("green", TCLISTVALPTR(q1res, i), "labels.0"));
+        CU_ASSERT_FALSE(!bson_compare_string("green", TCLISTVALPTR(q1res, i), "labels.1"));
+    }
+
+    bson_destroy(&bsq1);
+    tclistdel(q1res);
+    tcxstrdel(log);
+    ejdbquerydel(q1);
+
+    //Uppend more vals
+    bson_init_as_query(&bsq1);
+    bson_append_string(&bsq1, "name", "Антонов");
+    bson_append_start_object(&bsq1, "$inc");
+    bson_append_int(&bsq1, "age", -1);
+    bson_append_finish_object(&bsq1);
+    bson_append_start_object(&bsq1, "$addToSet");
+    bson_append_string(&bsq1, "personal.tags", "tag3");
+    bson_append_string(&bsq1, "labels", "red");
+    bson_append_finish_object(&bsq1);
+    bson_finish(&bsq1);
+    CU_ASSERT_FALSE_FATAL(bsq1.err);
+
+    q1 = ejdbcreatequery(jb, &bsq1, NULL, 0, NULL);
+    CU_ASSERT_PTR_NOT_NULL_FATAL(q1);
+    count = 0;
+    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"));
+    bson_destroy(&bsq1);
+    tcxstrdel(log);
+    ejdbquerydel(q1);
+
+    bson_init_as_query(&bsq1);
+    bson_append_string(&bsq1, "name", "Антонов");
+    bson_finish(&bsq1);
+    CU_ASSERT_FALSE_FATAL(bsq1.err);
+
+    //check again
+    q1 = ejdbcreatequery(jb, &bsq1, NULL, 0, NULL);
+    CU_ASSERT_PTR_NOT_NULL_FATAL(q1);
+    log = tcxstrnew();
+    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) {
+        //bson_print_raw(stderr, TCLISTVALPTR(q1res, i), 0);
+        CU_ASSERT_FALSE(bson_compare_string("tag1", TCLISTVALPTR(q1res, i), "personal.tags.0"));
+        CU_ASSERT_FALSE(bson_compare_string("tag2", TCLISTVALPTR(q1res, i), "personal.tags.1"));
+        CU_ASSERT_FALSE(bson_compare_string("tag3", TCLISTVALPTR(q1res, i), "personal.tags.2"));
+        CU_ASSERT_FALSE(bson_compare_string("green", TCLISTVALPTR(q1res, i), "labels.0"));
+        CU_ASSERT_FALSE(bson_compare_string("red", TCLISTVALPTR(q1res, i), "labels.1"));
+    }
+
+    bson_destroy(&bsq1);
+    tclistdel(q1res);
+    tcxstrdel(log);
+    ejdbquerydel(q1);
+}
+
+void test$pull() {
+    EJCOLL *coll = ejdbcreatecoll(jb, "contacts", NULL);
+    CU_ASSERT_PTR_NOT_NULL_FATAL(coll);
+    bson bsq1;
+    bson_init_as_query(&bsq1);
+    bson_append_string(&bsq1, "name", "Антонов");
+    bson_append_start_object(&bsq1, "$pull");
+    bson_append_string(&bsq1, "personal.tags", "tag2");
+    bson_append_string(&bsq1, "labels", "green");
+    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
+    bson_init_as_query(&bsq1);
+    bson_append_string(&bsq1, "name", "Антонов");
+    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) {
+        //bson_print_raw(stderr, TCLISTVALPTR(q1res, i), 0);
+        CU_ASSERT_FALSE(bson_compare_string("tag1", TCLISTVALPTR(q1res, i), "personal.tags.0"));
+        CU_ASSERT_FALSE(bson_compare_string("tag3", TCLISTVALPTR(q1res, i), "personal.tags.1"));
+        CU_ASSERT_FALSE(bson_compare_string("red", TCLISTVALPTR(q1res, i), "labels.0"));
     }
 
     bson_destroy(&bsq1);
@@ -3399,7 +3507,8 @@ int main() {
             (NULL == CU_add_test(pSuite, "testDropAll", testDropAll)) ||
             (NULL == CU_add_test(pSuite, "testTokens$begin", testTokens$begin)) ||
             (NULL == CU_add_test(pSuite, "testOneFieldManyConditions", testOneFieldManyConditions)) ||
-            (NULL == CU_add_test(pSuite, "test$addToSet", test$addToSet))
+            (NULL == CU_add_test(pSuite, "test$addToSet", test$addToSet)) ||
+            (NULL == CU_add_test(pSuite, "test$pull", test$pull))
             ) {
         CU_cleanup_registry();
         return CU_get_error();