diff options
author | Tyutyunkov Vyacheslav <tve@softmotions.com> | 2015-04-03 17:43:09 +0600 |
---|---|---|
committer | Tyutyunkov Vyacheslav <tve@softmotions.com> | 2015-04-03 17:43:09 +0600 |
commit | 430e50e0a941b586376aa2ce3cfe38532c92a0de (patch) | |
tree | fccce149f42ffe34e87bf509dd5b5ebdd9161d17 | |
parent | fec228b08a3c9c3ca7086745a630d1dc67d33404 (diff) | |
download | ejdb-430e50e0a941b586376aa2ce3cfe38532c92a0de.tar.gz ejdb-430e50e0a941b586376aa2ce3cfe38532c92a0de.tar.bz2 ejdb-430e50e0a941b586376aa2ce3cfe38532c92a0de.zip |
#123
-rw-r--r-- | src/bson/bson.c | 110 | ||||
-rw-r--r-- | src/bson/bson.h | 17 | ||||
-rw-r--r-- | src/ejdb/tests/ejdbtest2.c | 67 |
3 files changed, 176 insertions, 18 deletions
diff --git a/src/bson/bson.c b/src/bson/bson.c index c2f92c4..6c5d151 100644 --- a/src/bson/bson.c +++ b/src/bson/bson.c @@ -1651,6 +1651,70 @@ int bson_merge(const bson *b1, const bson *b2, bson_bool_t overwrite, bson *out) return bson_merge2(bson_data(b1), bson_data(b2), overwrite, 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; + + 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) + while ((bt1 = bson_iterator_next(&it1)) != BSON_EOO) { + const char* k1 = BSON_ITERATOR_KEY(&it1); + bt2 = bson_find_from_buffer(&it2, b2data, k1); + if (bt1 == BSON_OBJECT && bt2 == BSON_OBJECT) { + int res; + bson_append_start_object(out, k1); + if ((res = bson_merge_recursive2(bson_iterator_value(&it1), bson_iterator_value(&it2), overwrite, out)) != BSON_OK) { + return res; + } + bson_append_finish_object(out); + } else if (bt1 == BSON_ARRAY && bt2 == BSON_ARRAY) { + int c = 0; + bson_iterator sit; + bson_type sbt; + + bson_append_start_array(out, k1); + BSON_ITERATOR_SUBITERATOR(&it1, &sit); + while ((sbt = bson_iterator_next(&sit)) != BSON_EOO) { + bson_append_field_from_iterator(&sit, out); + ++c; + } + + char kbuf[TCNUMBUFSIZ]; + BSON_ITERATOR_SUBITERATOR(&it2, &sit); + while ((sbt = bson_iterator_next(&sit)) != BSON_EOO) { + bson_numstrn(kbuf, TCNUMBUFSIZ, c++); + bson_append_field_from_iterator2(kbuf, &sit, out); + } + + bson_append_finish_array(out); + } else if (overwrite && strcmp(JDBIDKEYNAME, k1) && bt2 != BSON_EOO) { + bson_append_field_from_iterator(&it2, out); + } else { + bson_append_field_from_iterator(&it1, out); + } + } + + BSON_ITERATOR_FROM_BUFFER(&it1, b1data); + BSON_ITERATOR_FROM_BUFFER(&it2, b2data); + //Append all fields from B2 missing in B1 + while ((bt2 = bson_iterator_next(&it2)) != BSON_EOO) { + const char* k2 = BSON_ITERATOR_KEY(&it2); + if ((bt1 = bson_find_from_buffer(&it1, b1data, k2)) == BSON_EOO) { + bson_append_field_from_iterator(&it2, out); + } + } + return BSON_OK; +} + +int bson_merge_recursive(const bson *b1, const bson *b2, bson_bool_t overwrite, bson *out) { + assert(b1 && b2 && out); + if (!b1->finished || !b2->finished || out->finished) { + return BSON_ERROR; + } + return bson_merge_recursive2(bson_data(b1), bson_data(b2), overwrite, out); +} + typedef struct { int nstack; //nested object stack pos int matched; //number of matched include fields @@ -2306,34 +2370,33 @@ int bson_merge_array_sets(const void *mbuf, const void *inbuf, bool pull, bool e if (ctx.mfields == 0 || pull) { return ctx.ecode; } + //Append missing arrays fields BSON_ITERATOR_FROM_BUFFER(&it, mbuf); while ((bt = bson_iterator_next(&it)) != BSON_EOO) { const char *fpath = BSON_ITERATOR_KEY(&it); - BSON_ITERATOR_FROM_BUFFER(&it2, inbuf); + // all data from inbuf already in bsout + bson_finish(bsout); + BSON_ITERATOR_INIT(&it2, bsout); bt2 = bson_find_fieldpath_value(fpath, &it2); if (bt2 != BSON_EOO) continue; int i = 0; int lvl = 0; const char *pdp = fpath; + bson bst; + bson_init(&bst); while (*(fpath + i) != '\0') { for (; *(fpath + i) != '\0' && *(fpath + i) != '.'; ++i); - BSON_ITERATOR_FROM_BUFFER(&it2, inbuf); - bt2 = bson_find_fieldpath_value2(fpath, i, &it2); - if (bt2 == BSON_EOO) { - if (*(fpath + i) == '\0') { //EOF - assert((fpath + i) - pdp > 0); - bson_append_start_array2(bsout, pdp, (fpath + i) - pdp); - bson_append_field_from_iterator2("0", &it, bsout); - bson_append_finish_array(bsout); - break; - } else { - ++lvl; - assert((fpath + i) - pdp > 0); - bson_append_start_object2(bsout, pdp, (fpath + i) - pdp); - } - } else if (bt2 != BSON_OBJECT) { + if (*(fpath + i) == '\0') { //EOF + assert((fpath + i) - pdp > 0); + bson_append_start_array2(&bst, pdp, (fpath + i) - pdp); + bson_append_field_from_iterator2("0", &it, &bst); + bson_append_finish_array(&bst); break; + } else { + ++lvl; + assert((fpath + i) - pdp > 0); + bson_append_start_object2(&bst, pdp, (fpath + i) - pdp); } pdp = (fpath + i); while (*pdp == '.') { @@ -2342,9 +2405,22 @@ int bson_merge_array_sets(const void *mbuf, const void *inbuf, bool pull, bool e } } for (; lvl > 0; --lvl) { - bson_append_finish_object(bsout); + bson_append_finish_object(&bst); + } + bson_finish(&bst); + + bson bsc; + bson_init_finished_data(&bsc, bson_data(bsout)); + bson_init_size(bsout, bson_size(bsout)); + 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; } diff --git a/src/bson/bson.h b/src/bson/bson.h index 02395d0..f32baaf 100644 --- a/src/bson/bson.h +++ b/src/bson/bson.h @@ -1124,6 +1124,23 @@ EJDB_EXPORT int bson_merge(const bson *b1, const bson *b2, bson_bool_t overwrite EJDB_EXPORT int bson_merge2(const void *b1data, const void *b2data, bson_bool_t overwrite, bson *out); /** + * Recursive 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. + * + * NOTE. Arrays with same paths joined. + * + * @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_recursive(const bson *b1, const bson *b2, bson_bool_t overwrite, bson *out); +EJDB_EXPORT int bson_merge_recursive2(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` diff --git a/src/ejdb/tests/ejdbtest2.c b/src/ejdb/tests/ejdbtest2.c index b34ecf3..c710919 100644 --- a/src/ejdb/tests/ejdbtest2.c +++ b/src/ejdb/tests/ejdbtest2.c @@ -3771,6 +3771,71 @@ void testAddToSet(void) { ejdbquerydel(q1); } +void testTicket123(void) { + EJCOLL *coll = ejdbcreatecoll(jb, "ticket123", NULL); + CU_ASSERT_PTR_NOT_NULL_FATAL(coll); + + bson bs1; + bson_oid_t oid1; + bson_init(&bs1); + bson_append_start_object(&bs1, "abc"); + bson_append_start_array(&bs1, "de"); + bson_append_string(&bs1, "0", "g"); + bson_append_finish_array(&bs1); + bson_append_start_array(&bs1, "fg"); + bson_append_finish_array(&bs1); + bson_append_finish_object(&bs1); + bson_finish(&bs1); + + CU_ASSERT_TRUE_FATAL(ejdbsavebson(coll, &bs1, &oid1)); + + bson bsq1; + bson_init_as_query(&bsq1); + bson_append_start_object(&bsq1, "$addToSet"); + 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")); + } + + bson_destroy(&bs1); + bson_destroy(&bsq1); + tclistdel(q1res); + tcxstrdel(log); + ejdbquerydel(q1); +} + + void testPull(void) { EJCOLL *coll = ejdbcreatecoll(jb, "contacts", NULL); CU_ASSERT_PTR_NOT_NULL_FATAL(coll); @@ -5680,6 +5745,7 @@ int main() { (NULL == CU_add_test(pSuite, "testTokensBegin", testTokensBegin)) || (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, "testPull", testPull)) || (NULL == CU_add_test(pSuite, "testFindInComplexArray", testFindInComplexArray)) || (NULL == CU_add_test(pSuite, "testElemMatch", testElemMatch)) || @@ -5706,7 +5772,6 @@ int main() { (NULL == CU_add_test(pSuite, "testSlice", testSlice)) || (NULL == CU_add_test(pSuite, "testTicket117", testTicket117)) || (NULL == CU_add_test(pSuite, "testMetaInfo", testMetaInfo)) - ) { CU_cleanup_registry(); return CU_get_error(); |