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];
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);
}
//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;
ctx.bsout = bsout;
ctx.mbuf = mbuf;
ctx.mfields = 0;
- ctx.nstack = 0;
ctx.duty = false;
ctx.ecode = BSON_OK;
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
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)) {
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__);
}
}
bson_finish(&bsout);
update = true;
- //bson_print(stderr, &bsout);
}
}
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;
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();
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);
bson_destroy(&bsq1);
tcxstrdel(log);
ejdbquerydel(q1);
-
+
//check updated data
bson_init_as_query(&bsq1);
//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);
(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();