Refactor & fix _qrybsrecurrmatch. Update $elemMatch test data.
authorChristian Manning <cmanning999@gmail.com>
Fri, 23 May 2014 18:06:47 +0000 (19:06 +0100)
committerChristian Manning <cmanning999@gmail.com>
Fri, 23 May 2014 18:06:47 +0000 (19:06 +0100)
_qrybsrecurrmatch was not checking all elements of an array as it
should have, which led to failures for all data and queries more
complex than the rather simple test cases.
It also now properly accounts for negation.

tcejdb/ejdb.c
tcejdb/testejdb/t2.c

index 25f4cde..98af830 100644 (file)
@@ -2069,57 +2069,68 @@ static bool _qrybsrecurrmatch(EJQF *qf, FFPCTX *ffpctx, int currpos) {
     assert(qf && ffpctx && ffpctx->stopnestedarr);
     bson_type bt = bson_find_fieldpath_value3(ffpctx);
     if (bt == BSON_ARRAY && ffpctx->stopos < ffpctx->fplen) { //a bit of complicated code  in this case =)
-        //we just stepped in some array in middle of our fieldpath, so have to perform recusive nested iterations
+        //we just stepped in some array in middle of our fieldpath, so have to perform recursive nested iterations
         //$elemMatch active in this context
         while (ffpctx->fpath[ffpctx->stopos] == '.' && ffpctx->stopos < ffpctx->fplen) ffpctx->stopos++;
         ffpctx->fplen = ffpctx->fplen - ffpctx->stopos;
         assert(ffpctx->fplen > 0);
         ffpctx->fpath = ffpctx->fpath + ffpctx->stopos;
         currpos += ffpctx->stopos; //adjust cumulative field position
+
         bson_iterator sit;
         BSON_ITERATOR_SUBITERATOR(ffpctx->input, &sit);
-        int c = 0;
-        while ((bt = bson_iterator_next(&sit)) != BSON_EOO) {
-            if (bt == BSON_OBJECT || bt == BSON_ARRAY) {
-                bson_iterator sit2;
-                BSON_ITERATOR_SUBITERATOR(&sit, &sit2);
-                ffpctx->input = &sit2;
-                if (_qrybsrecurrmatch(qf, ffpctx, currpos) != qf->negate) {
-                    bool ret = true;
-                    if (qf->elmatchgrp > 0 && qf->elmatchpos == currpos) { //$elemMatch matching group exists at right place
-                        for (int i = TCLISTNUM(qf->q->qflist) - 1; i >= 0; --i) {
-                            EJQF *eqf = TCLISTVALPTR(qf->q->qflist, i);
-                            if (eqf == qf || (eqf->mflags & EJFEXCLUDED) || eqf->elmatchgrp != qf->elmatchgrp) {
-                                continue;
-                            }
-                            eqf->mflags |= EJFEXCLUDED;
-                            BSON_ITERATOR_SUBITERATOR(&sit, &sit2);
-                            FFPCTX nffpctx = *ffpctx;
-                            nffpctx.fplen = eqf->fpathsz - eqf->elmatchpos;
-                            if (nffpctx.fplen <= 0) { //should never happen if query construction is correct
-                                assert(false);
-                                ret = false;
-                                break;
-                            }
-                            nffpctx.fpath = eqf->fpath + eqf->elmatchpos;
-                            nffpctx.input = &sit2;
-                            nffpctx.stopos = 0;
-                            if (!_qrybsrecurrmatch(eqf, &nffpctx, eqf->elmatchpos)) {
-                                ret = false;
-                                break;
-                            }
-                        }
+        for (int arr_idx = 0;(bt = bson_iterator_next(&sit)) != BSON_EOO; ++arr_idx) {
+            if (bt != BSON_OBJECT && bt != BSON_ARRAY)
+                continue;
+
+            bson_iterator sit2;
+            BSON_ITERATOR_SUBITERATOR(&sit, &sit2);
+
+            ffpctx->input = &sit2;
+
+            // Match using context initialised above.
+            if (_qrybsrecurrmatch(qf, ffpctx, currpos) == qf->negate) {
+                continue;
+            }
+
+            bool ret = true;
+            if (qf->elmatchgrp > 0 && qf->elmatchpos == currpos) { //$elemMatch matching group exists at right place
+                // Match all sub-queries on current field pos. Early exit (break) on failure.
+                for (int i = TCLISTNUM(qf->q->qflist) - 1; i >= 0; --i) {
+                    EJQF *eqf = TCLISTVALPTR(qf->q->qflist, i);
+                    if (eqf == qf || (eqf->mflags & EJFEXCLUDED) || eqf->elmatchgrp != qf->elmatchgrp) {
+                        continue;
+                    }
+                    eqf->mflags |= EJFEXCLUDED;
+                    BSON_ITERATOR_SUBITERATOR(&sit, &sit2);
+                    FFPCTX nffpctx = *ffpctx;
+                    nffpctx.fplen = eqf->fpathsz - eqf->elmatchpos;
+                    if (nffpctx.fplen <= 0) { //should never happen if query construction is correct
+                        assert(false);
+                        ret = false;
+                        break;
                     }
-                    if (ret) {
-                        _qrysetarrayidx(ffpctx, qf, (currpos - 1), c);
+                    nffpctx.fpath = eqf->fpath + eqf->elmatchpos;
+                    nffpctx.input = &sit2;
+                    nffpctx.stopos = 0;
+
+                    // Match sub-query at current field pos.
+                    // Ignores outer negate (qf) on inner query (eqf).
+                    if (_qrybsrecurrmatch(eqf, &nffpctx, eqf->elmatchpos) == qf->negate) {
+                        // Skip all remaining sub-queries on this field. Go to next element, if any.
+                        ret = false;
+                        break;
                     }
-
-                    return ret;
                 }
-                ++c;
+            }
+            if (ret) {
+                _qrysetarrayidx(ffpctx, qf, (currpos - 1), arr_idx);
+                // Only return success at this point.
+                // An failure here may precede a later success so proceed to next element, if any.
+                return ret != qf->negate;
             }
         }
-        return false;
+        return qf->negate;
     } else {
         if (bt == BSON_EOO || bt == BSON_UNDEFINED || bt == BSON_NULL) {
             return qf->negate; //Field missing
index baa0dc3..175f999 100644 (file)
@@ -54,9 +54,12 @@ void testAddData() {
     bson_append_finish_object(&a1); //EOF address
     bson_append_start_array(&a1, "complexarr");
     bson_append_start_object(&a1, "0");
-    bson_append_string(&a1, "foo", "bar");
-    bson_append_string(&a1, "foo2", "bar2");
-    bson_append_string(&a1, "foo3", "bar3");
+    bson_append_string(&a1, "key", "title");
+    bson_append_string(&a1, "value", "some title");
+    bson_append_finish_object(&a1);
+    bson_append_start_object(&a1, "1");
+    bson_append_string(&a1, "key", "comment");
+    bson_append_string(&a1, "value", "some comment");
     bson_append_finish_object(&a1);
     bson_append_finish_array(&a1); //EOF complexarr
     CU_ASSERT_FALSE_FATAL(a1.err);
@@ -78,10 +81,14 @@ void testAddData() {
     bson_append_finish_object(&a1);
     bson_append_start_array(&a1, "complexarr");
     bson_append_start_object(&a1, "0");
-    bson_append_string(&a1, "foo", "bar");
-    bson_append_string(&a1, "foo2", "bar3");
+    bson_append_string(&a1, "key", "title");
+    bson_append_string(&a1, "value", "some title");
+    bson_append_finish_object(&a1);
+    bson_append_start_object(&a1, "1");
+    bson_append_string(&a1, "key", "title");
+    bson_append_string(&a1, "value", "some other title");
     bson_append_finish_object(&a1);
-    bson_append_int(&a1, "1", 333);
+    bson_append_int(&a1, "2", 333);
     bson_append_finish_array(&a1); //EOF complexarr
     bson_append_start_array(&a1, "labels");
     bson_append_string(&a1, "0", "red");
@@ -3716,7 +3723,8 @@ void testFindInComplexArray() {
     CU_ASSERT_PTR_NOT_NULL_FATAL(coll);
     bson bsq1;
     bson_init_as_query(&bsq1);
-    bson_append_string(&bsq1, "complexarr.foo", "bar");
+    bson_append_string(&bsq1, "complexarr.key", "title");
+    bson_append_string(&bsq1, "complexarr.value", "some title");
     bson_finish(&bsq1);
     CU_ASSERT_FALSE_FATAL(bsq1.err);
 
@@ -3727,10 +3735,21 @@ void testFindInComplexArray() {
     TCLIST *q1res = ejdbqryexecute(coll, q1, &count, 0, log);
     CU_ASSERT_PTR_NOT_NULL_FATAL(q1res);
     //fprintf(stderr, "%s", TCXSTRPTR(log));
-    CU_ASSERT_EQUAL(count, 2);
-    for (int i = 0; i < TCLISTNUM(q1res); ++i) {
-        CU_ASSERT_FALSE(bson_compare_string("bar", TCLISTVALPTR(q1res, i), "complexarr.0.foo"));
-    }
+    CU_ASSERT_EQUAL_FATAL(count, 2);
+
+    // 0
+    CU_ASSERT_FALSE(bson_compare_string("Адаманский", TCLISTVALPTR(q1res, 0), "name"));
+
+    CU_ASSERT_FALSE(bson_compare_string("title", TCLISTVALPTR(q1res, 0), "complexarr.0.key"));
+    CU_ASSERT_FALSE(bson_compare_string("some title", TCLISTVALPTR(q1res, 0), "complexarr.0.value"));
+
+    CU_ASSERT_FALSE(bson_compare_string("title", TCLISTVALPTR(q1res, 0), "complexarr.1.key"));
+    CU_ASSERT_FALSE(bson_compare_string("some other title", TCLISTVALPTR(q1res, 0), "complexarr.1.value"));
+
+    // 1
+    CU_ASSERT_FALSE(bson_compare_string("Антонов", TCLISTVALPTR(q1res, 1), "name"));
+    CU_ASSERT_FALSE(bson_compare_string("title", TCLISTVALPTR(q1res, 1), "complexarr.0.key"));
+    CU_ASSERT_FALSE(bson_compare_string("some title", TCLISTVALPTR(q1res, 1), "complexarr.0.value"));
 
     bson_destroy(&bsq1);
     tclistdel(q1res);
@@ -3739,7 +3758,7 @@ void testFindInComplexArray() {
 
     //Check matching positional element
     bson_init_as_query(&bsq1);
-    bson_append_string(&bsq1, "complexarr.0.foo", "bar");
+    bson_append_string(&bsq1, "complexarr.0.key", "title");
     bson_finish(&bsq1);
     q1 = ejdbcreatequery(jb, &bsq1, NULL, 0, NULL);
     CU_ASSERT_PTR_NOT_NULL_FATAL(q1);
@@ -3747,10 +3766,21 @@ void testFindInComplexArray() {
     q1res = ejdbqryexecute(coll, q1, &count, 0, log);
     CU_ASSERT_PTR_NOT_NULL_FATAL(q1res);
     //fprintf(stderr, "\n%s", TCXSTRPTR(log));
-    CU_ASSERT_EQUAL(count, 2);
-    for (int i = 0; i < TCLISTNUM(q1res); ++i) {
-        CU_ASSERT_FALSE(bson_compare_string("bar", TCLISTVALPTR(q1res, i), "complexarr.0.foo"));
-    }
+    CU_ASSERT_EQUAL_FATAL(count, 2);
+
+    // 0
+    CU_ASSERT_FALSE(bson_compare_string("Адаманский", TCLISTVALPTR(q1res, 0), "name"));
+
+    CU_ASSERT_FALSE(bson_compare_string("title", TCLISTVALPTR(q1res, 0), "complexarr.0.key"));
+    CU_ASSERT_FALSE(bson_compare_string("some title", TCLISTVALPTR(q1res, 0), "complexarr.0.value"));
+
+    // 1
+    CU_ASSERT_FALSE(bson_compare_string("title", TCLISTVALPTR(q1res, 0), "complexarr.1.key"));
+    CU_ASSERT_FALSE(bson_compare_string("some other title", TCLISTVALPTR(q1res, 0), "complexarr.1.value"));
+
+    CU_ASSERT_FALSE(bson_compare_string("Антонов", TCLISTVALPTR(q1res, 1), "name"));
+    CU_ASSERT_FALSE(bson_compare_string("title", TCLISTVALPTR(q1res, 1), "complexarr.0.key"));
+    CU_ASSERT_FALSE(bson_compare_string("some title", TCLISTVALPTR(q1res, 1), "complexarr.0.value"));
 
     bson_destroy(&bsq1);
     tclistdel(q1res);
@@ -3759,7 +3789,7 @@ void testFindInComplexArray() {
 
     //Check simple el
     bson_init_as_query(&bsq1);
-    bson_append_int(&bsq1, "complexarr.1", 333);
+    bson_append_int(&bsq1, "complexarr.2", 333);
     bson_finish(&bsq1);
     q1 = ejdbcreatequery(jb, &bsq1, NULL, 0, NULL);
     CU_ASSERT_PTR_NOT_NULL_FATAL(q1);
@@ -3767,10 +3797,19 @@ void testFindInComplexArray() {
     q1res = ejdbqryexecute(coll, q1, &count, 0, log);
     CU_ASSERT_PTR_NOT_NULL_FATAL(q1res);
     //fprintf(stderr, "\n%s", TCXSTRPTR(log));
-    CU_ASSERT_EQUAL(count, 1);
-    for (int i = 0; i < TCLISTNUM(q1res); ++i) {
-        CU_ASSERT_FALSE(bson_compare_long(333, TCLISTVALPTR(q1res, i), "complexarr.1"));
-    }
+    CU_ASSERT_EQUAL_FATAL(count, 1);
+
+    // 0
+    CU_ASSERT_FALSE(bson_compare_string("Адаманский", TCLISTVALPTR(q1res, 0), "name"));
+
+    CU_ASSERT_FALSE(bson_compare_string("title", TCLISTVALPTR(q1res, 0), "complexarr.0.key"));
+    CU_ASSERT_FALSE(bson_compare_string("some title", TCLISTVALPTR(q1res, 0), "complexarr.0.value"));
+
+    CU_ASSERT_FALSE(bson_compare_string("title", TCLISTVALPTR(q1res, 0), "complexarr.1.key"));
+    CU_ASSERT_FALSE(bson_compare_string("some other title", TCLISTVALPTR(q1res, 0), "complexarr.1.value"));
+
+    CU_ASSERT_FALSE(bson_compare_long(333, TCLISTVALPTR(q1res, 0), "complexarr.2"));
+
     bson_destroy(&bsq1);
     tclistdel(q1res);
     tcxstrdel(log);
@@ -3786,10 +3825,19 @@ void testFindInComplexArray() {
     q1res = ejdbqryexecute(coll, q1, &count, 0, log);
     CU_ASSERT_PTR_NOT_NULL_FATAL(q1res);
     //fprintf(stderr, "\n%s", TCXSTRPTR(log));
-    CU_ASSERT_EQUAL(count, 1);
-    for (int i = 0; i < TCLISTNUM(q1res); ++i) {
-        CU_ASSERT_FALSE(bson_compare_long(333, TCLISTVALPTR(q1res, i), "complexarr.1"));
-    }
+    CU_ASSERT_EQUAL_FATAL(count, 1);
+
+    // 0
+    CU_ASSERT_FALSE(bson_compare_string("Адаманский", TCLISTVALPTR(q1res, 0), "name"));
+
+    CU_ASSERT_FALSE(bson_compare_string("title", TCLISTVALPTR(q1res, 0), "complexarr.0.key"));
+    CU_ASSERT_FALSE(bson_compare_string("some title", TCLISTVALPTR(q1res, 0), "complexarr.0.value"));
+
+    CU_ASSERT_FALSE(bson_compare_string("title", TCLISTVALPTR(q1res, 0), "complexarr.1.key"));
+    CU_ASSERT_FALSE(bson_compare_string("some other title", TCLISTVALPTR(q1res, 0), "complexarr.1.value"));
+
+    CU_ASSERT_FALSE(bson_compare_long(333, TCLISTVALPTR(q1res, 0), "complexarr.2"));
+
     bson_destroy(&bsq1);
     tclistdel(q1res);
     tcxstrdel(log);
@@ -3798,7 +3846,7 @@ void testFindInComplexArray() {
 
     //$exists
     bson_init_as_query(&bsq1);
-    bson_append_start_object(&bsq1, "complexarr.foo");
+    bson_append_start_object(&bsq1, "complexarr.key");
     bson_append_bool(&bsq1, "$exists", true);
     bson_append_finish_object(&bsq1);
     bson_finish(&bsq1);
@@ -3816,7 +3864,7 @@ void testFindInComplexArray() {
 
     //$exists 2
     bson_init_as_query(&bsq1);
-    bson_append_start_object(&bsq1, "complexarr.1");
+    bson_append_start_object(&bsq1, "complexarr.2");
     bson_append_bool(&bsq1, "$exists", true);
     bson_append_finish_object(&bsq1);
     bson_finish(&bsq1);
@@ -3851,16 +3899,15 @@ void testFindInComplexArray() {
 }
 
 void test$elemMatch() {
-    //{complexarr : {$elemMatch : {foo : 'bar', foo2 : 'bar2', foo3 : 'bar3'}}}
+    // { complexarr: { $elemMatch: { key: 'title', value: 'some title' } } }
     EJCOLL *coll = ejdbcreatecoll(jb, "contacts", NULL);
     CU_ASSERT_PTR_NOT_NULL_FATAL(coll);
     bson bsq1;
     bson_init_as_query(&bsq1);
     bson_append_start_object(&bsq1, "complexarr");
     bson_append_start_object(&bsq1, "$elemMatch");
-    bson_append_string(&bsq1, "foo", "bar");
-    bson_append_string(&bsq1, "foo2", "bar2");
-    bson_append_string(&bsq1, "foo3", "bar3");
+    bson_append_string(&bsq1, "key", "title");
+    bson_append_string(&bsq1, "value", "some title");
     bson_append_finish_object(&bsq1);
     bson_append_finish_object(&bsq1);
     bson_finish(&bsq1);
@@ -3872,14 +3919,27 @@ void test$elemMatch() {
     TCXSTR *log = tcxstrnew();
     TCLIST *q1res = ejdbqryexecute(coll, q1, &count, 0, log);
     CU_ASSERT_PTR_NOT_NULL_FATAL(q1res);
-    CU_ASSERT_EQUAL(count, 1);
-    //fprintf(stderr, "%s", TCXSTRPTR(log));
-    for (int i = 0; i < TCLISTNUM(q1res); ++i) {
-        CU_ASSERT_FALSE(bson_compare_string("Антонов", TCLISTVALPTR(q1res, i), "name"));
-        CU_ASSERT_FALSE(bson_compare_string("bar", TCLISTVALPTR(q1res, i), "complexarr.0.foo"));
-        CU_ASSERT_FALSE(bson_compare_string("bar2", TCLISTVALPTR(q1res, i), "complexarr.0.foo2"));
-        CU_ASSERT_FALSE(bson_compare_string("bar3", TCLISTVALPTR(q1res, i), "complexarr.0.foo3"));
-    }
+    CU_ASSERT_EQUAL_FATAL(count, 2);
+//    fprintf(stderr, "%s", TCXSTRPTR(log));
+
+    // 0
+    CU_ASSERT_FALSE(bson_compare_string("Адаманский", TCLISTVALPTR(q1res, 0), "name"));
+
+    CU_ASSERT_FALSE(bson_compare_string("title", TCLISTVALPTR(q1res, 0), "complexarr.0.key"));
+    CU_ASSERT_FALSE(bson_compare_string("some title", TCLISTVALPTR(q1res, 0), "complexarr.0.value"));
+
+    CU_ASSERT_FALSE(bson_compare_string("title", TCLISTVALPTR(q1res, 0), "complexarr.1.key"));
+    CU_ASSERT_FALSE(bson_compare_string("some other title", TCLISTVALPTR(q1res, 0), "complexarr.1.value"));
+
+    // 1
+    CU_ASSERT_FALSE(bson_compare_string("Антонов", TCLISTVALPTR(q1res, 1), "name"));
+
+    CU_ASSERT_FALSE(bson_compare_string("title", TCLISTVALPTR(q1res, 1), "complexarr.0.key"));
+    CU_ASSERT_FALSE(bson_compare_string("some title", TCLISTVALPTR(q1res, 1), "complexarr.0.value"));
+
+    CU_ASSERT_FALSE(bson_compare_string("comment", TCLISTVALPTR(q1res, 1), "complexarr.1.key"));
+    CU_ASSERT_FALSE(bson_compare_string("some comment", TCLISTVALPTR(q1res, 1), "complexarr.1.value"));
+
     bson_destroy(&bsq1);
     tclistdel(q1res);
     tcxstrdel(log);
@@ -3888,8 +3948,8 @@ void test$elemMatch() {
     bson_init_as_query(&bsq1);
     bson_append_start_object(&bsq1, "complexarr");
     bson_append_start_object(&bsq1, "$elemMatch");
-    bson_append_string(&bsq1, "foo", "bar");
-    bson_append_string(&bsq1, "foo2", "bar3");
+    bson_append_string(&bsq1, "key", "title");
+    bson_append_string(&bsq1, "value", "some other title");
     bson_append_finish_object(&bsq1);
     bson_append_finish_object(&bsq1);
     bson_finish(&bsq1);
@@ -3900,12 +3960,17 @@ void test$elemMatch() {
     q1res = ejdbqryexecute(coll, q1, &count, 0, log);
     CU_ASSERT_PTR_NOT_NULL_FATAL(q1res);
     //fprintf(stderr, "%s", TCXSTRPTR(log));
-    CU_ASSERT_EQUAL(count, 1);
-    for (int i = 0; i < TCLISTNUM(q1res); ++i) {
-        CU_ASSERT_FALSE(bson_compare_string("Адаманский", TCLISTVALPTR(q1res, i), "name"));
-        CU_ASSERT_FALSE(bson_compare_string("bar", TCLISTVALPTR(q1res, i), "complexarr.0.foo"));
-        CU_ASSERT_FALSE(bson_compare_string("bar3", TCLISTVALPTR(q1res, i), "complexarr.0.foo2"));
-    }
+    CU_ASSERT_EQUAL_FATAL(count, 1);
+
+    // 0
+    CU_ASSERT_FALSE(bson_compare_string("Адаманский", TCLISTVALPTR(q1res, 0), "name"));
+
+    CU_ASSERT_FALSE(bson_compare_string("title", TCLISTVALPTR(q1res, 0), "complexarr.0.key"));
+    CU_ASSERT_FALSE(bson_compare_string("some title", TCLISTVALPTR(q1res, 0), "complexarr.0.value"));
+
+    CU_ASSERT_FALSE(bson_compare_string("title", TCLISTVALPTR(q1res, 0), "complexarr.1.key"));
+    CU_ASSERT_FALSE(bson_compare_string("some other title", TCLISTVALPTR(q1res, 0), "complexarr.1.value"));
+
     bson_destroy(&bsq1);
     tclistdel(q1res);
     tcxstrdel(log);
@@ -3914,9 +3979,9 @@ void test$elemMatch() {
     bson_init_as_query(&bsq1);
     bson_append_start_object(&bsq1, "complexarr");
     bson_append_start_object(&bsq1, "$elemMatch");
-    bson_append_string(&bsq1, "foo", "bar");
-    bson_append_start_object(&bsq1, "foo2");
-    bson_append_string(&bsq1, "$not", "bar3");
+    bson_append_string(&bsq1, "key", "title");
+    bson_append_start_object(&bsq1, "value");
+    bson_append_string(&bsq1, "$not", "some title");
     bson_append_finish_object(&bsq1);
     bson_append_finish_object(&bsq1);
     bson_append_finish_object(&bsq1);
@@ -3927,22 +3992,26 @@ void test$elemMatch() {
     log = tcxstrnew();
     q1res = ejdbqryexecute(coll, q1, &count, 0, log);
     CU_ASSERT_PTR_NOT_NULL_FATAL(q1res);
-    //fprintf(stderr, "%s", TCXSTRPTR(log));
-    CU_ASSERT_EQUAL(count, 1);
-    for (int i = 0; i < TCLISTNUM(q1res); ++i) {
-        CU_ASSERT_FALSE(bson_compare_string("Антонов", TCLISTVALPTR(q1res, i), "name"));
-        CU_ASSERT_FALSE(bson_compare_string("bar", TCLISTVALPTR(q1res, i), "complexarr.0.foo"));
-        CU_ASSERT_FALSE(bson_compare_string("bar2", TCLISTVALPTR(q1res, i), "complexarr.0.foo2"));
-        CU_ASSERT_FALSE(bson_compare_string("bar3", TCLISTVALPTR(q1res, i), "complexarr.0.foo3"));
-    }
+//    fprintf(stderr, "%s", TCXSTRPTR(log));
+    CU_ASSERT_EQUAL_FATAL(count, 1);
+
+    // 0
+    CU_ASSERT_FALSE(bson_compare_string("Адаманский", TCLISTVALPTR(q1res, 0), "name"));
+
+    CU_ASSERT_FALSE(bson_compare_string("title", TCLISTVALPTR(q1res, 0), "complexarr.0.key"));
+    CU_ASSERT_FALSE(bson_compare_string("some title", TCLISTVALPTR(q1res, 0), "complexarr.0.value"));
+
+    CU_ASSERT_FALSE(bson_compare_string("title", TCLISTVALPTR(q1res, 0), "complexarr.1.key"));
+    CU_ASSERT_FALSE(bson_compare_string("some other title", TCLISTVALPTR(q1res, 0), "complexarr.1.value"));
+
     bson_destroy(&bsq1);
     tclistdel(q1res);
     tcxstrdel(log);
     ejdbquerydel(q1);
 
     bson_init_as_query(&bsq1);
-    bson_append_string(&bsq1, "complexarr.foo", "bar");
-    bson_append_string(&bsq1, "complexarr.foo2", "bar3");
+    bson_append_string(&bsq1, "complexarr.key", "title");
+    bson_append_string(&bsq1, "complexarr.value", "some other title");
     bson_finish(&bsq1);
     CU_ASSERT_FALSE_FATAL(bsq1.err);
     q1 = ejdbcreatequery(jb, &bsq1, NULL, 0, NULL);
@@ -3951,12 +4020,16 @@ void test$elemMatch() {
     q1res = ejdbqryexecute(coll, q1, &count, 0, log);
     CU_ASSERT_PTR_NOT_NULL_FATAL(q1res);
     //fprintf(stderr, "%s", TCXSTRPTR(log));
-    CU_ASSERT_EQUAL(count, 1);
-    for (int i = 0; i < TCLISTNUM(q1res); ++i) {
-        CU_ASSERT_FALSE(bson_compare_string("Адаманский", TCLISTVALPTR(q1res, i), "name"));
-        CU_ASSERT_FALSE(bson_compare_string("bar", TCLISTVALPTR(q1res, i), "complexarr.0.foo"));
-        CU_ASSERT_FALSE(bson_compare_string("bar3", TCLISTVALPTR(q1res, i), "complexarr.0.foo2"));
-    }
+    CU_ASSERT_EQUAL_FATAL(count, 1);
+
+    // 0
+    CU_ASSERT_FALSE(bson_compare_string("Адаманский", TCLISTVALPTR(q1res, 0), "name"));
+
+    CU_ASSERT_FALSE(bson_compare_string("title", TCLISTVALPTR(q1res, 0), "complexarr.0.key"));
+    CU_ASSERT_FALSE(bson_compare_string("some title", TCLISTVALPTR(q1res, 0), "complexarr.0.value"));
+
+    CU_ASSERT_FALSE(bson_compare_string("title", TCLISTVALPTR(q1res, 0), "complexarr.1.key"));
+    CU_ASSERT_FALSE(bson_compare_string("some other title", TCLISTVALPTR(q1res, 0), "complexarr.1.value"));
 
     bson_destroy(&bsq1);
     tclistdel(q1res);
@@ -3965,7 +4038,7 @@ void test$elemMatch() {
 }
 
 void test$not$elemMatch() {
-    //{complexarr : {$elemMatch : {foo : 'bar', foo2 : 'bar2', foo3 : 'bar3'}}}
+    // { complexarr: { $not: { $elemMatch: { key: 'title', value: 'some title' } } } }
     EJCOLL *coll = ejdbcreatecoll(jb, "contacts", NULL);
     CU_ASSERT_PTR_NOT_NULL_FATAL(coll);
     bson bsq1;
@@ -3973,9 +4046,8 @@ void test$not$elemMatch() {
     bson_append_start_object(&bsq1, "complexarr");
     bson_append_start_object(&bsq1, "$not");
     bson_append_start_object(&bsq1, "$elemMatch");
-    bson_append_string(&bsq1, "foo", "bar");
-    bson_append_string(&bsq1, "foo2", "bar2");
-    bson_append_string(&bsq1, "foo3", "bar3");
+    bson_append_string(&bsq1, "key", "title");
+    bson_append_string(&bsq1, "value", "some title");
     bson_append_finish_object(&bsq1);//$elemMatch
     bson_append_finish_object(&bsq1);//$not
     //include $exists to exclude documents without complexarr
@@ -3990,14 +4062,43 @@ void test$not$elemMatch() {
     TCXSTR *log = tcxstrnew();
     TCLIST *q1res = ejdbqryexecute(coll, q1, &count, 0, log);
     CU_ASSERT_PTR_NOT_NULL_FATAL(q1res);
+    //fprintf(stderr, "%s", TCXSTRPTR(log));
+    CU_ASSERT_EQUAL_FATAL(count, 0);
 
-    CU_ASSERT_EQUAL(count, 1);
+    bson_destroy(&bsq1);
+    tclistdel(q1res);
+    tcxstrdel(log);
+    ejdbquerydel(q1);
+
+    bson_init_as_query(&bsq1);
+    bson_append_start_object(&bsq1, "complexarr");
+    bson_append_start_object(&bsq1, "$not");
+    bson_append_start_object(&bsq1, "$elemMatch");
+    bson_append_string(&bsq1, "key", "title");
+    bson_append_string(&bsq1, "value", "some other title");
+    bson_append_finish_object(&bsq1);//$elemMatch
+    bson_append_finish_object(&bsq1);//$not
+    bson_append_bool(&bsq1, "$exists", true);
+    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);
+    log = tcxstrnew();
+    q1res = ejdbqryexecute(coll, q1, &count, 0, log);
+    CU_ASSERT_PTR_NOT_NULL_FATAL(q1res);
     //fprintf(stderr, "%s", TCXSTRPTR(log));
-    for (int i = 0; i < TCLISTNUM(q1res); ++i) {
-        CU_ASSERT_FALSE(bson_compare_string("Адаманский", TCLISTVALPTR(q1res, i), "name"));
-        CU_ASSERT_FALSE(bson_compare_string("bar", TCLISTVALPTR(q1res, i), "complexarr.0.foo"));
-        CU_ASSERT_FALSE(bson_compare_string("bar3", TCLISTVALPTR(q1res, i), "complexarr.0.foo2"));
-    }
+    CU_ASSERT_EQUAL_FATAL(count, 1);
+
+    // 0
+    CU_ASSERT_FALSE(bson_compare_string("Антонов", TCLISTVALPTR(q1res, 0), "name"));
+
+    CU_ASSERT_FALSE(bson_compare_string("title", TCLISTVALPTR(q1res, 0), "complexarr.0.key"));
+    CU_ASSERT_FALSE(bson_compare_string("some title", TCLISTVALPTR(q1res, 0), "complexarr.0.value"));
+
+    CU_ASSERT_FALSE(bson_compare_string("comment", TCLISTVALPTR(q1res, 0), "complexarr.1.key"));
+    CU_ASSERT_FALSE(bson_compare_string("some comment", TCLISTVALPTR(q1res, 0), "complexarr.1.value"));
+
     bson_destroy(&bsq1);
     tclistdel(q1res);
     tcxstrdel(log);
@@ -4007,8 +4108,7 @@ void test$not$elemMatch() {
     bson_append_start_object(&bsq1, "complexarr");
     bson_append_start_object(&bsq1, "$not");
     bson_append_start_object(&bsq1, "$elemMatch");
-    bson_append_string(&bsq1, "foo", "bar");
-    bson_append_string(&bsq1, "foo2", "bar3");
+    bson_append_string(&bsq1, "key", "comment");
     bson_append_finish_object(&bsq1);//$elemMatch
     bson_append_finish_object(&bsq1);//$not
     bson_append_bool(&bsq1, "$exists", true);
@@ -4021,13 +4121,17 @@ void test$not$elemMatch() {
     q1res = ejdbqryexecute(coll, q1, &count, 0, log);
     CU_ASSERT_PTR_NOT_NULL_FATAL(q1res);
     //fprintf(stderr, "%s", TCXSTRPTR(log));
-    CU_ASSERT_EQUAL(count, 1);
-    for (int i = 0; i < TCLISTNUM(q1res); ++i) {
-        CU_ASSERT_FALSE(bson_compare_string("Антонов", TCLISTVALPTR(q1res, i), "name"));
-        CU_ASSERT_FALSE(bson_compare_string("bar", TCLISTVALPTR(q1res, i), "complexarr.0.foo"));
-        CU_ASSERT_FALSE(bson_compare_string("bar2", TCLISTVALPTR(q1res, i), "complexarr.0.foo2"));
-        CU_ASSERT_FALSE(bson_compare_string("bar3", TCLISTVALPTR(q1res, i), "complexarr.0.foo3"));
-    }
+    CU_ASSERT_EQUAL_FATAL(count, 1);
+
+    // 0
+    CU_ASSERT_FALSE(bson_compare_string("Адаманский", TCLISTVALPTR(q1res, 0), "name"));
+
+    CU_ASSERT_FALSE(bson_compare_string("title", TCLISTVALPTR(q1res, 0), "complexarr.0.key"));
+    CU_ASSERT_FALSE(bson_compare_string("some title", TCLISTVALPTR(q1res, 0), "complexarr.0.value"));
+
+    CU_ASSERT_FALSE(bson_compare_string("title", TCLISTVALPTR(q1res, 0), "complexarr.1.key"));
+    CU_ASSERT_FALSE(bson_compare_string("some other title", TCLISTVALPTR(q1res, 0), "complexarr.1.value"));
+
     bson_destroy(&bsq1);
     tclistdel(q1res);
     tcxstrdel(log);
@@ -4038,9 +4142,9 @@ void test$not$elemMatch() {
     bson_append_start_object(&bsq1, "complexarr");
     bson_append_start_object(&bsq1, "$not");
     bson_append_start_object(&bsq1, "$elemMatch");
-    bson_append_string(&bsq1, "foo", "bar");
-    bson_append_start_object(&bsq1, "foo2");
-    bson_append_string(&bsq1, "$not", "bar3");
+    bson_append_string(&bsq1, "key", "title");
+    bson_append_start_object(&bsq1, "value");
+    bson_append_string(&bsq1, "$not", "some title");
     bson_append_finish_object(&bsq1);
     bson_append_finish_object(&bsq1);
     bson_append_finish_object(&bsq1);
@@ -4053,12 +4157,17 @@ void test$not$elemMatch() {
     q1res = ejdbqryexecute(coll, q1, &count, 0, log);
     CU_ASSERT_PTR_NOT_NULL_FATAL(q1res);
     //fprintf(stderr, "%s", TCXSTRPTR(log));
-    CU_ASSERT_EQUAL(count, 1);
-    for (int i = 0; i < TCLISTNUM(q1res); ++i) {
-        CU_ASSERT_FALSE(bson_compare_string("Адаманский", TCLISTVALPTR(q1res, i), "name"));
-        CU_ASSERT_FALSE(bson_compare_string("bar", TCLISTVALPTR(q1res, i), "complexarr.0.foo"));
-        CU_ASSERT_FALSE(bson_compare_string("bar3", TCLISTVALPTR(q1res, i), "complexarr.0.foo2"));
-    }
+    CU_ASSERT_EQUAL_FATAL(count, 1);
+
+    // 0
+    CU_ASSERT_FALSE(bson_compare_string("Антонов", TCLISTVALPTR(q1res, 0), "name"));
+
+    CU_ASSERT_FALSE(bson_compare_string("title", TCLISTVALPTR(q1res, 0), "complexarr.0.key"));
+    CU_ASSERT_FALSE(bson_compare_string("some title", TCLISTVALPTR(q1res, 0), "complexarr.0.value"));
+
+    CU_ASSERT_FALSE(bson_compare_string("comment", TCLISTVALPTR(q1res, 0), "complexarr.1.key"));
+    CU_ASSERT_FALSE(bson_compare_string("some comment", TCLISTVALPTR(q1res, 0), "complexarr.1.value"));
+
     bson_destroy(&bsq1);
     tclistdel(q1res);
     tcxstrdel(log);