You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by ga...@apache.org on 2020/02/13 14:57:51 UTC

[couchdb] 07/20: range query fixes from tests

This is an automated email from the ASF dual-hosted git repository.

garren pushed a commit to branch fdb-mango-indexes
in repository https://gitbox.apache.org/repos/asf/couchdb.git

commit 0a494cc415f66d20ffccb95a54cd4d279767a2f7
Author: Garren Smith <ga...@gmail.com>
AuthorDate: Wed Jan 29 16:48:33 2020 +0200

    range query fixes from tests
---
 src/mango/src/mango_cursor_view.erl          |   3 +-
 src/mango/src/mango_fdb.erl                  |  21 +-
 src/mango/src/mango_idx.erl                  |   1 +
 src/mango/src/mango_indexer.erl              |  28 +-
 src/mango/test/01-index-crud-test.py         | 710 +++++++++++++--------------
 src/mango/test/03-operator-test.py           |   4 +-
 src/mango/test/05-index-selection-test.py    |   1 +
 src/mango/test/11-ignore-design-docs-test.py |   2 +
 src/mango/test/13-stable-update-test.py      |   2 +
 src/mango/test/21-fdb-indexing.py            |  48 ++
 src/mango/test/exunit/mango_indexer_test.exs |  39 +-
 src/mango/test/mango.py                      |   4 +
 12 files changed, 448 insertions(+), 415 deletions(-)

diff --git a/src/mango/src/mango_cursor_view.erl b/src/mango/src/mango_cursor_view.erl
index 26c60b7..9669b5f 100644
--- a/src/mango/src/mango_cursor_view.erl
+++ b/src/mango/src/mango_cursor_view.erl
@@ -44,7 +44,8 @@ create(Db, Indexes, Selector, Opts) ->
     Limit = couch_util:get_value(limit, Opts, mango_opts:default_limit()),
     Skip = couch_util:get_value(skip, Opts, 0),
     Fields = couch_util:get_value(fields, Opts, all_fields),
-    Bookmark = couch_util:get_value(bookmark, Opts), 
+    Bookmark = couch_util:get_value(bookmark, Opts),
+    io:format("Index selected ~p Range ~p ~n", [Index, IndexRanges]),
 
     {ok, #cursor{
         db = Db,
diff --git a/src/mango/src/mango_fdb.erl b/src/mango/src/mango_fdb.erl
index 091d5f7..dbd22fa 100644
--- a/src/mango/src/mango_fdb.erl
+++ b/src/mango/src/mango_fdb.erl
@@ -103,25 +103,12 @@ args_to_fdb_opts(Args) ->
         {<<255>>, _, _} ->
             %% all_docs no endkey
             [];
-        {[<<255>>], _, _} ->
+        {[], _, _} ->
             %% mango index no endkey
             [];
-%%        {undefined, _, _} ->
-%%            [];
-%%        {EndKey1, <<>>, rev} when not InclusiveEnd ->
-%%            % When we iterate in reverse with
-%%            % inclusive_end=false we have to set the
-%%            % EndKeyDocId to <<255>> so that we don't
-%%            % include matching rows.
-%%            [{end_key_gt, {EndKey1, <<255>>}}];
-%%        {EndKey1, <<255>>, _} when not InclusiveEnd ->
-%%            % When inclusive_end=false we need to
-%%            % elide the default end_key_docid so as
-%%            % to not sort past the docids with the
-%%            % given end key.
-%%            [{end_key_gt, {EndKey1}}];
-%%        {EndKey1, EndKeyDocId, _} when not InclusiveEnd ->
-%%            [{end_key_gt, {EndKey1, EndKeyDocId}}];
+        {[<<255>>], _, _} ->
+            %% mango index no endkey with a $lt in selector
+            [];
         {EndKey0, EndKeyDocId, _} when InclusiveEnd ->
             EndKey1 = couch_views_encoding:encode(EndKey0, key),
             [{EndKeyName, {EndKey1, EndKeyDocId}}]
diff --git a/src/mango/src/mango_idx.erl b/src/mango/src/mango_idx.erl
index 57262f9..3a579dc 100644
--- a/src/mango/src/mango_idx.erl
+++ b/src/mango/src/mango_idx.erl
@@ -76,6 +76,7 @@ ddoc_fold_cb({row, Row}, Acc) ->
         rows := Rows
     } = Acc,
     {_, Id} = lists:keyfind(id, 1, Row),
+    io:format("VIEW ~p ~n", [Row]),
     {ok, Doc} = fabric2_db:open_doc(Db, Id),
     JSONDoc = couch_doc:to_json_obj(Doc, []),
     {Props} = JSONDoc,
diff --git a/src/mango/src/mango_indexer.erl b/src/mango/src/mango_indexer.erl
index 36eb2d3..20af5bd 100644
--- a/src/mango/src/mango_indexer.erl
+++ b/src/mango/src/mango_indexer.erl
@@ -26,6 +26,7 @@
 % Todo: Check if design doc is mango index and kick off background worker
 % to build new index
 update(Db, Change, #doc{id = <<?DESIGN_DOC_PREFIX, _/binary>>} = Doc, OldDoc) ->
+    io:format("DESIGN DOC SAVED ~p ~n", [Doc]),
     ok;
 
 update(Db, deleted, _, OldDoc)  ->
@@ -35,15 +36,23 @@ update(Db, updated, Doc, OldDoc) ->
     ok;
 
 update(Db, created, Doc, _) ->
-    #doc{id = DocId} = Doc,
-    Indexes = mango_idx:list(Db),
-    Indexes1 = filter_json_indexes(Indexes),
-    io:format("UPDATE INDEXES ~p ~n filtered ~p ~n", [Indexes, Indexes1]),
-    JSONDoc = mango_json:to_binary(couch_doc:to_json_obj(Doc, [])),
-    io:format("DOC ~p ~n", [Doc]),
-    Results = index_doc(Indexes1, JSONDoc),
-    io:format("Update ~p ~n, ~p ~n Results ~p ~n", [Doc, JSONDoc, Results]),
-    mango_fdb:write_doc(Db, DocId, Results).
+    try
+        io:format("CREATED ~p ~n", [Doc]),
+        #doc{id = DocId} = Doc,
+        Indexes = mango_idx:list(Db),
+        Indexes1 = filter_json_indexes(Indexes),
+        io:format("UPDATE INDEXES ~p ~n filtered ~p ~n", [Indexes, Indexes1]),
+        JSONDoc = mango_json:to_binary(couch_doc:to_json_obj(Doc, [])),
+        io:format("DOC ~p ~n", [Doc]),
+        Results = index_doc(Indexes1, JSONDoc),
+        io:format("Update ~p ~n, ~p ~n Results ~p ~n", [Doc, JSONDoc, Results]),
+        mango_fdb:write_doc(Db, DocId, Results)
+    catch
+        Error:Reason ->
+            io:format("ERROR ~p ~p ~p ~n", [Error, Reason, erlang:display(erlang:get_stacktrace())]),
+            ok
+    end,
+    ok.
 
 
 filter_json_indexes(Indexes) ->
@@ -54,7 +63,6 @@ filter_json_indexes(Indexes) ->
 
 index_doc(Indexes, Doc) ->
     lists:foldl(fun(Idx, Acc) ->
-        io:format("II ~p ~n", [Idx]),
         {IdxDef} = mango_idx:def(Idx),
         Results = get_index_entries(IdxDef, Doc),
         case lists:member(not_found, Results) of
diff --git a/src/mango/test/01-index-crud-test.py b/src/mango/test/01-index-crud-test.py
index 6e0208a..dd9ab1a 100644
--- a/src/mango/test/01-index-crud-test.py
+++ b/src/mango/test/01-index-crud-test.py
@@ -26,63 +26,63 @@ class IndexCrudTests(mango.DbPerClass):
     def setUp(self):
         self.db.recreate()
 
-    # def test_bad_fields(self):
-    #     bad_fields = [
-    #         None,
-    #         True,
-    #         False,
-    #         "bing",
-    #         2.0,
-    #         {"foo": "bar"},
-    #         [{"foo": 2}],
-    #         [{"foo": "asc", "bar": "desc"}],
-    #         [{"foo": "asc"}, {"bar": "desc"}],
-    #         [""],
-    #     ]
-    #     for fields in bad_fields:
-    #         try:
-    #             self.db.create_index(fields)
-    #         except Exception as e:
-    #             self.assertEqual(e.response.status_code, 400)
-    #         else:
-    #             raise AssertionError("bad create index")
-    #
-    # def test_bad_types(self):
-    #     bad_types = [
-    #         None,
-    #         True,
-    #         False,
-    #         1.5,
-    #         "foo",  # Future support
-    #         "geo",  # Future support
-    #         {"foo": "bar"},
-    #         ["baz", 3.0],
-    #     ]
-    #     for bt in bad_types:
-    #         try:
-    #             self.db.create_index(["foo"], idx_type=bt)
-    #         except Exception as e:
-    #             self.assertEqual(
-    #                 e.response.status_code, 400, (bt, e.response.status_code)
-    #             )
-    #         else:
-    #             raise AssertionError("bad create index")
-    #
-    # def test_bad_names(self):
-    #     bad_names = [True, False, 1.5, {"foo": "bar"}, [None, False]]
-    #     for bn in bad_names:
-    #         try:
-    #             self.db.create_index(["foo"], name=bn)
-    #         except Exception as e:
-    #             self.assertEqual(e.response.status_code, 400)
-    #         else:
-    #             raise AssertionError("bad create index")
-    #         try:
-    #             self.db.create_index(["foo"], ddoc=bn)
-    #         except Exception as e:
-    #             self.assertEqual(e.response.status_code, 400)
-    #         else:
-    #             raise AssertionError("bad create index")
+    def test_bad_fields(self):
+        bad_fields = [
+            None,
+            True,
+            False,
+            "bing",
+            2.0,
+            {"foo": "bar"},
+            [{"foo": 2}],
+            [{"foo": "asc", "bar": "desc"}],
+            [{"foo": "asc"}, {"bar": "desc"}],
+            [""],
+        ]
+        for fields in bad_fields:
+            try:
+                self.db.create_index(fields)
+            except Exception as e:
+                self.assertEqual(e.response.status_code, 400)
+            else:
+                raise AssertionError("bad create index")
+
+    def test_bad_types(self):
+        bad_types = [
+            None,
+            True,
+            False,
+            1.5,
+            "foo",  # Future support
+            "geo",  # Future support
+            {"foo": "bar"},
+            ["baz", 3.0],
+        ]
+        for bt in bad_types:
+            try:
+                self.db.create_index(["foo"], idx_type=bt)
+            except Exception as e:
+                self.assertEqual(
+                    e.response.status_code, 400, (bt, e.response.status_code)
+                )
+            else:
+                raise AssertionError("bad create index")
+
+    def test_bad_names(self):
+        bad_names = [True, False, 1.5, {"foo": "bar"}, [None, False]]
+        for bn in bad_names:
+            try:
+                self.db.create_index(["foo"], name=bn)
+            except Exception as e:
+                self.assertEqual(e.response.status_code, 400)
+            else:
+                raise AssertionError("bad create index")
+            try:
+                self.db.create_index(["foo"], ddoc=bn)
+            except Exception as e:
+                self.assertEqual(e.response.status_code, 400)
+            else:
+                raise AssertionError("bad create index")
 
     def test_create_idx_01(self):
         fields = ["foo", "bar"]
@@ -95,301 +95,301 @@ class IndexCrudTests(mango.DbPerClass):
             return
         raise AssertionError("index not created")
 
-#     def test_create_idx_01_exists(self):
-#         fields = ["foo", "bar"]
-#         ret = self.db.create_index(fields, name="idx_01")
-#         assert ret is True
-#         ret = self.db.create_index(fields, name="idx_01")
-#         assert ret is False
-#
-#     def test_create_idx_02(self):
-#         fields = ["baz", "foo"]
-#         ret = self.db.create_index(fields, name="idx_02")
-#         assert ret is True
-#         for idx in self.db.list_indexes():
-#             if idx["name"] != "idx_02":
-#                 continue
-#             self.assertEqual(idx["def"]["fields"], [{"baz": "asc"}, {"foo": "asc"}])
-#             return
-#         raise AssertionError("index not created")
-#
-#     def test_ignore_design_docs(self):
-#         fields = ["baz", "foo"]
-#         ret = self.db.create_index(fields, name="idx_02")
-#         assert ret is True
-#         self.db.save_doc({
-#             "_id": "_design/ignore",
-#             "views": {
-#                 "view1": {
-#                     "map": "function (doc) { emit(doc._id, 1)}"
-#                 }
-#             }
-#         })
-#         Indexes = self.db.list_indexes()
-#         self.assertEqual(len(Indexes), 2)
-#
-#     def test_read_idx_doc(self):
-#         self.db.create_index(["foo", "bar"], name="idx_01")
-#         self.db.create_index(["hello", "bar"])
-#         for idx in self.db.list_indexes():
-#             if idx["type"] == "special":
-#                 continue
-#             ddocid = idx["ddoc"]
-#             doc = self.db.open_doc(ddocid)
-#             self.assertEqual(doc["_id"], ddocid)
-#             info = self.db.ddoc_info(ddocid)
-#             self.assertEqual(info["name"], ddocid.split("_design/")[-1])
-#
-#     def test_delete_idx_escaped(self):
-#         self.db.create_index(["foo", "bar"], name="idx_01")
-#         pre_indexes = self.db.list_indexes()
-#         ret = self.db.create_index(["bing"], name="idx_del_1")
-#         assert ret is True
-#         for idx in self.db.list_indexes():
-#             if idx["name"] != "idx_del_1":
-#                 continue
-#             self.assertEqual(idx["def"]["fields"], [{"bing": "asc"}])
-#             self.db.delete_index(idx["ddoc"].replace("/", "%2F"), idx["name"])
-#         post_indexes = self.db.list_indexes()
-#         self.assertEqual(pre_indexes, post_indexes)
-#
-#     def test_delete_idx_unescaped(self):
-#         pre_indexes = self.db.list_indexes()
-#         ret = self.db.create_index(["bing"], name="idx_del_2")
-#         assert ret is True
-#         for idx in self.db.list_indexes():
-#             if idx["name"] != "idx_del_2":
-#                 continue
-#             self.assertEqual(idx["def"]["fields"], [{"bing": "asc"}])
-#             self.db.delete_index(idx["ddoc"], idx["name"])
-#         post_indexes = self.db.list_indexes()
-#         self.assertEqual(pre_indexes, post_indexes)
-#
-#     def test_delete_idx_no_design(self):
-#         pre_indexes = self.db.list_indexes()
-#         ret = self.db.create_index(["bing"], name="idx_del_3")
-#         assert ret is True
-#         for idx in self.db.list_indexes():
-#             if idx["name"] != "idx_del_3":
-#                 continue
-#             self.assertEqual(idx["def"]["fields"], [{"bing": "asc"}])
-#             self.db.delete_index(idx["ddoc"].split("/")[-1], idx["name"])
-#         post_indexes = self.db.list_indexes()
-#         self.assertEqual(pre_indexes, post_indexes)
-#
-#     def test_bulk_delete(self):
-#         fields = ["field1"]
-#         ret = self.db.create_index(fields, name="idx_01")
-#         assert ret is True
-#
-#         fields = ["field2"]
-#         ret = self.db.create_index(fields, name="idx_02")
-#         assert ret is True
-#
-#         fields = ["field3"]
-#         ret = self.db.create_index(fields, name="idx_03")
-#         assert ret is True
-#
-#         docids = []
-#
-#         for idx in self.db.list_indexes():
-#             if idx["ddoc"] is not None:
-#                 docids.append(idx["ddoc"])
-#
-#         docids.append("_design/this_is_not_an_index_name")
-#
-#         ret = self.db.bulk_delete(docids)
-#
-#         self.assertEqual(ret["fail"][0]["id"], "_design/this_is_not_an_index_name")
-#         self.assertEqual(len(ret["success"]), 3)
-#
-#         for idx in self.db.list_indexes():
-#             assert idx["type"] != "json"
-#             assert idx["type"] != "text"
-#
-#     def test_recreate_index(self):
-#         pre_indexes = self.db.list_indexes()
-#         for i in range(5):
-#             ret = self.db.create_index(["bing"], name="idx_recreate")
-#             assert ret is True
-#             for idx in self.db.list_indexes():
-#                 if idx["name"] != "idx_recreate":
-#                     continue
-#                 self.assertEqual(idx["def"]["fields"], [{"bing": "asc"}])
-#                 self.db.delete_index(idx["ddoc"], idx["name"])
-#                 break
-#             post_indexes = self.db.list_indexes()
-#             self.assertEqual(pre_indexes, post_indexes)
-#
-#     def test_delete_missing(self):
-#         # Missing design doc
-#         try:
-#             self.db.delete_index("this_is_not_a_design_doc_id", "foo")
-#         except Exception as e:
-#             self.assertEqual(e.response.status_code, 404)
-#         else:
-#             raise AssertionError("bad index delete")
-#
-#         # Missing view name
-#         ret = self.db.create_index(["fields"], name="idx_01")
-#         indexes = self.db.list_indexes()
-#         not_special = [idx for idx in indexes if idx["type"] != "special"]
-#         idx = random.choice(not_special)
-#         ddocid = idx["ddoc"].split("/")[-1]
-#         try:
-#             self.db.delete_index(ddocid, "this_is_not_an_index_name")
-#         except Exception as e:
-#             self.assertEqual(e.response.status_code, 404)
-#         else:
-#             raise AssertionError("bad index delete")
-#
-#         # Bad view type
-#         try:
-#             self.db.delete_index(ddocid, idx["name"], idx_type="not_a_real_type")
-#         except Exception as e:
-#             self.assertEqual(e.response.status_code, 404)
-#         else:
-#             raise AssertionError("bad index delete")
-#
-#     def test_limit_skip_index(self):
-#         fields = ["field1"]
-#         ret = self.db.create_index(fields, name="idx_01")
-#         assert ret is True
-#
-#         fields = ["field2"]
-#         ret = self.db.create_index(fields, name="idx_02")
-#         assert ret is True
-#
-#         fields = ["field3"]
-#         ret = self.db.create_index(fields, name="idx_03")
-#         assert ret is True
-#
-#         fields = ["field4"]
-#         ret = self.db.create_index(fields, name="idx_04")
-#         assert ret is True
-#
-#         fields = ["field5"]
-#         ret = self.db.create_index(fields, name="idx_05")
-#         assert ret is True
-#
-#         self.assertEqual(len(self.db.list_indexes(limit=2)), 2)
-#         self.assertEqual(len(self.db.list_indexes(limit=5, skip=4)), 2)
-#         self.assertEqual(len(self.db.list_indexes(skip=5)), 1)
-#         self.assertEqual(len(self.db.list_indexes(skip=6)), 0)
-#         self.assertEqual(len(self.db.list_indexes(skip=100)), 0)
-#         self.assertEqual(len(self.db.list_indexes(limit=10000000)), 6)
-#
-#         try:
-#             self.db.list_indexes(skip=-1)
-#         except Exception as e:
-#             self.assertEqual(e.response.status_code, 500)
-#
-#         try:
-#             self.db.list_indexes(limit=0)
-#         except Exception as e:
-#             self.assertEqual(e.response.status_code, 500)
-#
-#     def test_out_of_sync(self):
-#         self.db.save_docs(copy.deepcopy(DOCS))
-#         self.db.create_index(["age"], name="age")
-#
-#         selector = {"age": {"$gt": 0}}
-#         docs = self.db.find(
-#             selector, use_index="_design/a017b603a47036005de93034ff689bbbb6a873c4"
-#         )
-#         self.assertEqual(len(docs), 2)
-#
-#         self.db.delete_doc("1")
-#
-#         docs1 = self.db.find(
-#             selector,
-#             update="False",
-#             use_index="_design/a017b603a47036005de93034ff689bbbb6a873c4",
-#         )
-#         self.assertEqual(len(docs1), 1)
-#
-#
-# @unittest.skipUnless(mango.has_text_service(), "requires text service")
-# class IndexCrudTextTests(mango.DbPerClass):
-#     def setUp(self):
-#         self.db.recreate()
-#
-#     def test_create_text_idx(self):
-#         fields = [
-#             {"name": "stringidx", "type": "string"},
-#             {"name": "booleanidx", "type": "boolean"},
-#         ]
-#         ret = self.db.create_text_index(fields=fields, name="text_idx_01")
-#         assert ret is True
-#         for idx in self.db.list_indexes():
-#             if idx["name"] != "text_idx_01":
-#                 continue
-#             self.assertEqual(
-#                 idx["def"]["fields"],
-#                 [{"stringidx": "string"}, {"booleanidx": "boolean"}],
-#             )
-#             return
-#         raise AssertionError("index not created")
-#
-#     def test_create_bad_text_idx(self):
-#         bad_fields = [
-#             True,
-#             False,
-#             "bing",
-#             2.0,
-#             ["foo", "bar"],
-#             [{"name": "foo2"}],
-#             [{"name": "foo3", "type": "garbage"}],
-#             [{"type": "number"}],
-#             [{"name": "age", "type": "number"}, {"name": "bad"}],
-#             [{"name": "age", "type": "number"}, "bla"],
-#             [{"name": "", "type": "number"}, "bla"],
-#         ]
-#         for fields in bad_fields:
-#             try:
-#                 self.db.create_text_index(fields=fields)
-#             except Exception as e:
-#                 self.assertEqual(e.response.status_code, 400)
-#             else:
-#                 raise AssertionError("bad create text index")
-#
-#     def test_limit_skip_index(self):
-#         fields = ["field1"]
-#         ret = self.db.create_index(fields, name="idx_01")
-#         assert ret is True
-#
-#         fields = ["field2"]
-#         ret = self.db.create_index(fields, name="idx_02")
-#         assert ret is True
-#
-#         fields = ["field3"]
-#         ret = self.db.create_index(fields, name="idx_03")
-#         assert ret is True
-#
-#         fields = ["field4"]
-#         ret = self.db.create_index(fields, name="idx_04")
-#         assert ret is True
-#
-#         fields = [
-#             {"name": "stringidx", "type": "string"},
-#             {"name": "booleanidx", "type": "boolean"},
-#         ]
-#         ret = self.db.create_text_index(fields=fields, name="idx_05")
-#         assert ret is True
-#
-#         self.assertEqual(len(self.db.list_indexes(limit=2)), 2)
-#         self.assertEqual(len(self.db.list_indexes(limit=5, skip=4)), 2)
-#         self.assertEqual(len(self.db.list_indexes(skip=5)), 1)
-#         self.assertEqual(len(self.db.list_indexes(skip=6)), 0)
-#         self.assertEqual(len(self.db.list_indexes(skip=100)), 0)
-#         self.assertEqual(len(self.db.list_indexes(limit=10000000)), 6)
-#
-#         try:
-#             self.db.list_indexes(skip=-1)
-#         except Exception as e:
-#             self.assertEqual(e.response.status_code, 500)
-#
-#         try:
-#             self.db.list_indexes(limit=0)
-#         except Exception as e:
-#             self.assertEqual(e.response.status_code, 500)
+    def test_create_idx_01_exists(self):
+        fields = ["foo", "bar"]
+        ret = self.db.create_index(fields, name="idx_01")
+        assert ret is True
+        ret = self.db.create_index(fields, name="idx_01")
+        assert ret is False
+
+    def test_create_idx_02(self):
+        fields = ["baz", "foo"]
+        ret = self.db.create_index(fields, name="idx_02")
+        assert ret is True
+        for idx in self.db.list_indexes():
+            if idx["name"] != "idx_02":
+                continue
+            self.assertEqual(idx["def"]["fields"], [{"baz": "asc"}, {"foo": "asc"}])
+            return
+        raise AssertionError("index not created")
+
+    def test_ignore_design_docs(self):
+        fields = ["baz", "foo"]
+        ret = self.db.create_index(fields, name="idx_02")
+        assert ret is True
+        self.db.save_doc({
+            "_id": "_design/ignore",
+            "views": {
+                "view1": {
+                    "map": "function (doc) { emit(doc._id, 1)}"
+                }
+            }
+        })
+        Indexes = self.db.list_indexes()
+        self.assertEqual(len(Indexes), 2)
+
+    def test_read_idx_doc(self):
+        self.db.create_index(["foo", "bar"], name="idx_01")
+        self.db.create_index(["hello", "bar"])
+        for idx in self.db.list_indexes():
+            if idx["type"] == "special":
+                continue
+            ddocid = idx["ddoc"]
+            doc = self.db.open_doc(ddocid)
+            self.assertEqual(doc["_id"], ddocid)
+            info = self.db.ddoc_info(ddocid)
+            self.assertEqual(info["name"], ddocid.split("_design/")[-1])
+
+    def test_delete_idx_escaped(self):
+        self.db.create_index(["foo", "bar"], name="idx_01")
+        pre_indexes = self.db.list_indexes()
+        ret = self.db.create_index(["bing"], name="idx_del_1")
+        assert ret is True
+        for idx in self.db.list_indexes():
+            if idx["name"] != "idx_del_1":
+                continue
+            self.assertEqual(idx["def"]["fields"], [{"bing": "asc"}])
+            self.db.delete_index(idx["ddoc"].replace("/", "%2F"), idx["name"])
+        post_indexes = self.db.list_indexes()
+        self.assertEqual(pre_indexes, post_indexes)
+
+    def test_delete_idx_unescaped(self):
+        pre_indexes = self.db.list_indexes()
+        ret = self.db.create_index(["bing"], name="idx_del_2")
+        assert ret is True
+        for idx in self.db.list_indexes():
+            if idx["name"] != "idx_del_2":
+                continue
+            self.assertEqual(idx["def"]["fields"], [{"bing": "asc"}])
+            self.db.delete_index(idx["ddoc"], idx["name"])
+        post_indexes = self.db.list_indexes()
+        self.assertEqual(pre_indexes, post_indexes)
+
+    def test_delete_idx_no_design(self):
+        pre_indexes = self.db.list_indexes()
+        ret = self.db.create_index(["bing"], name="idx_del_3")
+        assert ret is True
+        for idx in self.db.list_indexes():
+            if idx["name"] != "idx_del_3":
+                continue
+            self.assertEqual(idx["def"]["fields"], [{"bing": "asc"}])
+            self.db.delete_index(idx["ddoc"].split("/")[-1], idx["name"])
+        post_indexes = self.db.list_indexes()
+        self.assertEqual(pre_indexes, post_indexes)
+
+    def test_bulk_delete(self):
+        fields = ["field1"]
+        ret = self.db.create_index(fields, name="idx_01")
+        assert ret is True
+
+        fields = ["field2"]
+        ret = self.db.create_index(fields, name="idx_02")
+        assert ret is True
+
+        fields = ["field3"]
+        ret = self.db.create_index(fields, name="idx_03")
+        assert ret is True
+
+        docids = []
+
+        for idx in self.db.list_indexes():
+            if idx["ddoc"] is not None:
+                docids.append(idx["ddoc"])
+
+        docids.append("_design/this_is_not_an_index_name")
+
+        ret = self.db.bulk_delete(docids)
+
+        self.assertEqual(ret["fail"][0]["id"], "_design/this_is_not_an_index_name")
+        self.assertEqual(len(ret["success"]), 3)
+
+        for idx in self.db.list_indexes():
+            assert idx["type"] != "json"
+            assert idx["type"] != "text"
+
+    def test_recreate_index(self):
+        pre_indexes = self.db.list_indexes()
+        for i in range(5):
+            ret = self.db.create_index(["bing"], name="idx_recreate")
+            assert ret is True
+            for idx in self.db.list_indexes():
+                if idx["name"] != "idx_recreate":
+                    continue
+                self.assertEqual(idx["def"]["fields"], [{"bing": "asc"}])
+                self.db.delete_index(idx["ddoc"], idx["name"])
+                break
+            post_indexes = self.db.list_indexes()
+            self.assertEqual(pre_indexes, post_indexes)
+
+    def test_delete_missing(self):
+        # Missing design doc
+        try:
+            self.db.delete_index("this_is_not_a_design_doc_id", "foo")
+        except Exception as e:
+            self.assertEqual(e.response.status_code, 404)
+        else:
+            raise AssertionError("bad index delete")
+
+        # Missing view name
+        ret = self.db.create_index(["fields"], name="idx_01")
+        indexes = self.db.list_indexes()
+        not_special = [idx for idx in indexes if idx["type"] != "special"]
+        idx = random.choice(not_special)
+        ddocid = idx["ddoc"].split("/")[-1]
+        try:
+            self.db.delete_index(ddocid, "this_is_not_an_index_name")
+        except Exception as e:
+            self.assertEqual(e.response.status_code, 404)
+        else:
+            raise AssertionError("bad index delete")
+
+        # Bad view type
+        try:
+            self.db.delete_index(ddocid, idx["name"], idx_type="not_a_real_type")
+        except Exception as e:
+            self.assertEqual(e.response.status_code, 404)
+        else:
+            raise AssertionError("bad index delete")
+
+    def test_limit_skip_index(self):
+        fields = ["field1"]
+        ret = self.db.create_index(fields, name="idx_01")
+        assert ret is True
+
+        fields = ["field2"]
+        ret = self.db.create_index(fields, name="idx_02")
+        assert ret is True
+
+        fields = ["field3"]
+        ret = self.db.create_index(fields, name="idx_03")
+        assert ret is True
+
+        fields = ["field4"]
+        ret = self.db.create_index(fields, name="idx_04")
+        assert ret is True
+
+        fields = ["field5"]
+        ret = self.db.create_index(fields, name="idx_05")
+        assert ret is True
+
+        self.assertEqual(len(self.db.list_indexes(limit=2)), 2)
+        self.assertEqual(len(self.db.list_indexes(limit=5, skip=4)), 2)
+        self.assertEqual(len(self.db.list_indexes(skip=5)), 1)
+        self.assertEqual(len(self.db.list_indexes(skip=6)), 0)
+        self.assertEqual(len(self.db.list_indexes(skip=100)), 0)
+        self.assertEqual(len(self.db.list_indexes(limit=10000000)), 6)
+
+        try:
+            self.db.list_indexes(skip=-1)
+        except Exception as e:
+            self.assertEqual(e.response.status_code, 500)
+
+        try:
+            self.db.list_indexes(limit=0)
+        except Exception as e:
+            self.assertEqual(e.response.status_code, 500)
+
+    def test_out_of_sync(self):
+        self.db.save_docs(copy.deepcopy(DOCS))
+        self.db.create_index(["age"], name="age")
+
+        selector = {"age": {"$gt": 0}}
+        docs = self.db.find(
+            selector, use_index="_design/a017b603a47036005de93034ff689bbbb6a873c4"
+        )
+        self.assertEqual(len(docs), 2)
+
+        self.db.delete_doc("1")
+
+        docs1 = self.db.find(
+            selector,
+            update="False",
+            use_index="_design/a017b603a47036005de93034ff689bbbb6a873c4",
+        )
+        self.assertEqual(len(docs1), 1)
+
+
+@unittest.skipUnless(mango.has_text_service(), "requires text service")
+class IndexCrudTextTests(mango.DbPerClass):
+    def setUp(self):
+        self.db.recreate()
+
+    def test_create_text_idx(self):
+        fields = [
+            {"name": "stringidx", "type": "string"},
+            {"name": "booleanidx", "type": "boolean"},
+        ]
+        ret = self.db.create_text_index(fields=fields, name="text_idx_01")
+        assert ret is True
+        for idx in self.db.list_indexes():
+            if idx["name"] != "text_idx_01":
+                continue
+            self.assertEqual(
+                idx["def"]["fields"],
+                [{"stringidx": "string"}, {"booleanidx": "boolean"}],
+            )
+            return
+        raise AssertionError("index not created")
+
+    def test_create_bad_text_idx(self):
+        bad_fields = [
+            True,
+            False,
+            "bing",
+            2.0,
+            ["foo", "bar"],
+            [{"name": "foo2"}],
+            [{"name": "foo3", "type": "garbage"}],
+            [{"type": "number"}],
+            [{"name": "age", "type": "number"}, {"name": "bad"}],
+            [{"name": "age", "type": "number"}, "bla"],
+            [{"name": "", "type": "number"}, "bla"],
+        ]
+        for fields in bad_fields:
+            try:
+                self.db.create_text_index(fields=fields)
+            except Exception as e:
+                self.assertEqual(e.response.status_code, 400)
+            else:
+                raise AssertionError("bad create text index")
+
+    def test_limit_skip_index(self):
+        fields = ["field1"]
+        ret = self.db.create_index(fields, name="idx_01")
+        assert ret is True
+
+        fields = ["field2"]
+        ret = self.db.create_index(fields, name="idx_02")
+        assert ret is True
+
+        fields = ["field3"]
+        ret = self.db.create_index(fields, name="idx_03")
+        assert ret is True
+
+        fields = ["field4"]
+        ret = self.db.create_index(fields, name="idx_04")
+        assert ret is True
+
+        fields = [
+            {"name": "stringidx", "type": "string"},
+            {"name": "booleanidx", "type": "boolean"},
+        ]
+        ret = self.db.create_text_index(fields=fields, name="idx_05")
+        assert ret is True
+
+        self.assertEqual(len(self.db.list_indexes(limit=2)), 2)
+        self.assertEqual(len(self.db.list_indexes(limit=5, skip=4)), 2)
+        self.assertEqual(len(self.db.list_indexes(skip=5)), 1)
+        self.assertEqual(len(self.db.list_indexes(skip=6)), 0)
+        self.assertEqual(len(self.db.list_indexes(skip=100)), 0)
+        self.assertEqual(len(self.db.list_indexes(limit=10000000)), 6)
+
+        try:
+            self.db.list_indexes(skip=-1)
+        except Exception as e:
+            self.assertEqual(e.response.status_code, 500)
+
+        try:
+            self.db.list_indexes(limit=0)
+        except Exception as e:
+            self.assertEqual(e.response.status_code, 500)
diff --git a/src/mango/test/03-operator-test.py b/src/mango/test/03-operator-test.py
index 935f470..fdcd079 100644
--- a/src/mango/test/03-operator-test.py
+++ b/src/mango/test/03-operator-test.py
@@ -190,5 +190,5 @@ class OperatorAllDocsTests(mango.UserDocsTestsNoIndexes, OperatorTests):
         doc_id = "8e1c90c0-ac18-4832-8081-40d14325bde0"
         r = self.db.find({"_id": doc_id}, explain=True, return_raw=True)
 
-        self.assertEqual(r["mrargs"]["end_key"], doc_id)
-        self.assertEqual(r["mrargs"]["start_key"], doc_id)
+        self.assertEqual(r["args"]["end_key"], doc_id)
+        self.assertEqual(r["args"]["start_key"], doc_id)
diff --git a/src/mango/test/05-index-selection-test.py b/src/mango/test/05-index-selection-test.py
index 3f7fb9f..e6d74bd 100644
--- a/src/mango/test/05-index-selection-test.py
+++ b/src/mango/test/05-index-selection-test.py
@@ -183,6 +183,7 @@ class IndexSelectionTests:
 
     # This doc will not be saved given the new ddoc validation code
     # in couch_mrview
+    @unittest.skip("need to add couch_mrview:validate_ddoc_fields")
     def test_manual_bad_view_idx01(self):
         design_doc = {
             "_id": "_design/bad_view_index",
diff --git a/src/mango/test/11-ignore-design-docs-test.py b/src/mango/test/11-ignore-design-docs-test.py
index f31dcc5..bb8cf3a 100644
--- a/src/mango/test/11-ignore-design-docs-test.py
+++ b/src/mango/test/11-ignore-design-docs-test.py
@@ -19,6 +19,8 @@ DOCS = [
     {"_id": "54af50622071121b25402dc3", "user_id": 1, "age": 11, "name": "Eddie"},
 ]
 
+# [{erlfdb_nif,erlfdb_future_get,[#Ref<0.1264327726.2786983941.139980>],[]},{erlfdb,fold_range_int,4,[{file,"src/erlfdb.erl"},{line,675}]},{fabric2_fdb,get_winning_revs_wait,2,[{file,"src/fabric2_fdb.erl"},{line,474}]},{fabric2_db,'-open_doc/3-fun-1-',5,[{file,"src/fabric2_db.erl"},{line,503}]},{mango_idx,ddoc_fold_cb,2,[{file,"src/mango_idx.erl"},{line,80}]},{fabric2_db,'-fold_docs/4-fun-0-',6,[{file,"src/fabric2_db.erl"},{line,795}]},{fabric2_fdb,fold_range_cb,2,[{file,"src/fabric2_fdb [...]
+
 
 class IgnoreDesignDocsForAllDocsIndexTests(mango.DbPerClass):
     def test_should_not_return_design_docs(self):
diff --git a/src/mango/test/13-stable-update-test.py b/src/mango/test/13-stable-update-test.py
index 348ac5e..1af63b9 100644
--- a/src/mango/test/13-stable-update-test.py
+++ b/src/mango/test/13-stable-update-test.py
@@ -12,6 +12,7 @@
 
 import copy
 import mango
+import unittest
 
 DOCS1 = [
     {
@@ -39,6 +40,7 @@ class SupportStableAndUpdate(mango.DbPerClass):
         self.db.create_index(["name"])
         self.db.save_docs(copy.deepcopy(DOCS1))
 
+    @unittest.skip("this FDB doesn't support this")
     def test_update_updates_view_when_specified(self):
         docs = self.db.find({"name": "Eddie"}, update=False)
         assert len(docs) == 0
diff --git a/src/mango/test/21-fdb-indexing.py b/src/mango/test/21-fdb-indexing.py
new file mode 100644
index 0000000..e1cfd90
--- /dev/null
+++ b/src/mango/test/21-fdb-indexing.py
@@ -0,0 +1,48 @@
+# -*- coding: latin-1 -*-
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+import mango
+import copy
+
+DOCS = [
+    {"_id": "100", "name": "Jimi", "location": "AUS", "user_id": 1, "same": "value"},
+    {"_id": "200", "name": "Eddie", "location": "BRA", "user_id": 2, "same": "value"},
+    {"_id": "300", "name": "Harry", "location": "CAN", "user_id": 3, "same": "value"},
+    {"_id": "400", "name": "Eddie", "location": "DEN", "user_id": 4, "same": "value"},
+    {"_id": "500", "name": "Jones", "location": "ETH", "user_id": 5, "same": "value"}
+]
+
+class FdbIndexingTests(mango.DbPerClass):
+    def setUp(self):
+        self.db.recreate()
+        self.db.create_index(["name"], name="name")
+        self.db.save_docs(copy.deepcopy(DOCS))
+
+    def test_doc_update(self):
+        docs = self.db.find({"name": "Eddie"})
+        self.assertEqual(len(docs), 2)
+        self.assertEqual(docs[0]["_id"], "200")
+        self.assertEqual(docs[1]["_id"], "400")
+
+        doc = self.db.open_doc("400")
+        doc["name"] = "NotEddie"
+        self.db.save_doc(doc)
+
+        docs = self.db.find({"name": "Eddie"})
+        print("DD")
+        print(docs)
+        self.assertEqual(len(docs), 1)
+        self.assertEqual(docs[0]["_id"], "200")
+
+
+
diff --git a/src/mango/test/exunit/mango_indexer_test.exs b/src/mango/test/exunit/mango_indexer_test.exs
index 16c6e49..f62f47e 100644
--- a/src/mango/test/exunit/mango_indexer_test.exs
+++ b/src/mango/test/exunit/mango_indexer_test.exs
@@ -26,7 +26,7 @@ defmodule MangoIndexerTest do
     idx_ddocs = create_indexes(db)
     docs = create_docs()
 
-    IO.inspect idx_ddocs
+    IO.inspect(idx_ddocs)
     {ok, _} = :fabric2_db.update_docs(db, ddocs ++ idx_ddocs)
     {ok, _} = :fabric2_db.update_docs(db, docs)
 
@@ -42,7 +42,7 @@ defmodule MangoIndexerTest do
     }
   end
 
-  test "create design doc through _index", context do
+  test "update doc", context do
     db = context[:db]
   end
 
@@ -59,31 +59,10 @@ defmodule MangoIndexerTest do
     {:ok, idx} = :mango_idx.new(db, opts)
     db_opts = [{:user_ctx, db["user_ctx"]}, :deleted, :ejson_body]
     {:ok, ddoc} = :mango_util.load_ddoc(db, :mango_idx.ddoc(idx), db_opts)
-    {:ok ,new_ddoc} = :mango_idx.add(ddoc, idx)
+    {:ok, new_ddoc} = :mango_idx.add(ddoc, idx)
     [new_ddoc]
   end
 
-  #    Create 1 design doc that should be filtered out and ignored
-  defp create_ddocs() do
-    views = %{
-      "_id" => "_design/bar",
-      "views" => %{
-        "dates_sum" => %{
-          "map" => """
-                function(doc) {
-                    if (doc.date) {
-                        emit(doc.date, doc.date_val);
-                    }
-                }
-          """
-        }
-      }
-    }
-
-    ddoc1 = :couch_doc.from_json_obj(:jiffy.decode(:jiffy.encode(views)))
-    []
-  end
-
   defp create_docs() do
     for i <- 1..1 do
       group =
@@ -95,12 +74,12 @@ defmodule MangoIndexerTest do
 
       :couch_doc.from_json_obj(
         {[
-          {"_id", "doc-id-#{i}"},
-          {"value", i},
-          {"val_str", Integer.to_string(i, 8)},
-          {"some", "field"},
-          {"group", group}
-        ]}
+           {"_id", "doc-id-#{i}"},
+           {"value", i},
+           {"val_str", Integer.to_string(i, 8)},
+           {"some", "field"},
+           {"group", group}
+         ]}
       )
     end
   end
diff --git a/src/mango/test/mango.py b/src/mango/test/mango.py
index 5ce4219..a39476d 100644
--- a/src/mango/test/mango.py
+++ b/src/mango/test/mango.py
@@ -110,7 +110,11 @@ class Database(object):
     def save_docs(self, docs, **kwargs):
         body = json.dumps({"docs": docs})
         r = self.sess.post(self.path("_bulk_docs"), data=body, params=kwargs)
+        print("DOC")
+        print(docs)
         r.raise_for_status()
+        print("RES")
+        print(r.json())
         for doc, result in zip(docs, r.json()):
             doc["_id"] = result["id"]
             doc["_rev"] = result["rev"]