diff options
author | garren smith <garren.smith@gmail.com> | 2017-09-21 17:30:49 +0200 |
---|---|---|
committer | Joan Touzet <wohali@users.noreply.github.com> | 2017-10-19 19:13:25 -0400 |
commit | b98de40c8b75a012e7c565fcb4803e8fcc91acd5 (patch) | |
tree | 1d26059b73283eff7685c23aa5e5f3ecf9bb522e | |
parent | 31b0abb8cbd49fb399767222ed9f58f15aaa38db (diff) | |
download | couchdb-b98de40c8b75a012e7c565fcb4803e8fcc91acd5.tar.gz |
Rename selector to partialfilterselector in indexes (#818)
To make it easier to distinguish between a selector in _find and a
selector in _index. Rename the selector in the _index to
partialfilterselector. It also gives a bit more of an explanation of
what this selector does.
-rw-r--r-- | src/mango/src/mango_cursor.erl | 8 | ||||
-rw-r--r-- | src/mango/src/mango_idx.erl | 61 | ||||
-rw-r--r-- | src/mango/src/mango_idx_text.erl | 8 | ||||
-rw-r--r-- | src/mango/src/mango_idx_view.erl | 4 | ||||
-rw-r--r-- | src/mango/src/mango_native_proc.erl | 23 | ||||
-rw-r--r-- | src/mango/test/16-index-selectors.py | 111 | ||||
-rw-r--r-- | src/mango/test/mango.py | 14 |
7 files changed, 192 insertions, 37 deletions
diff --git a/src/mango/src/mango_cursor.erl b/src/mango/src/mango_cursor.erl index f36febdfc..e0792b737 100644 --- a/src/mango/src/mango_cursor.erl +++ b/src/mango/src/mango_cursor.erl @@ -90,9 +90,9 @@ execute(#cursor{index=Idx}=Cursor, UserFun, UserAcc) -> maybe_filter_indexes_by_ddoc(Indexes, Opts) -> case lists:keyfind(use_index, 1, Opts) of {use_index, []} -> - %We remove any indexes that have a selector + % We remove any indexes that have a selector % since they are only used when specified via use_index - remove_indexes_with_selector(Indexes); + remove_indexes_with_partial_filter_selector(Indexes); {use_index, [DesignId]} -> filter_indexes(Indexes, DesignId); {use_index, [DesignId, ViewName]} -> @@ -117,9 +117,9 @@ filter_indexes(Indexes0, DesignId, ViewName) -> lists:filter(FiltFun, Indexes). -remove_indexes_with_selector(Indexes) -> +remove_indexes_with_partial_filter_selector(Indexes) -> FiltFun = fun(Idx) -> - case mango_idx:get_idx_selector(Idx) of + case mango_idx:get_partial_filter_selector(Idx) of undefined -> true; _ -> false end diff --git a/src/mango/src/mango_idx.erl b/src/mango/src/mango_idx.erl index b8122517d..04f84c8df 100644 --- a/src/mango/src/mango_idx.erl +++ b/src/mango/src/mango_idx.erl @@ -44,7 +44,7 @@ to_json/1, delete/4, get_usable_indexes/3, - get_idx_selector/1 + get_partial_filter_selector/1 ]). @@ -368,13 +368,66 @@ filter_opts([Opt | Rest]) -> [Opt | filter_opts(Rest)]. -get_idx_selector(#idx{def = Def}) when Def =:= all_docs; Def =:= undefined -> +get_partial_filter_selector(#idx{def = Def}) when Def =:= all_docs; Def =:= undefined -> undefined; -get_idx_selector(#idx{def = {Def}}) -> +get_partial_filter_selector(#idx{def = {Def}}) -> + case proplists:get_value(<<"partial_filter_selector">>, Def) of + undefined -> get_legacy_selector(Def); + {[]} -> undefined; + Selector -> Selector + end. + + +% Partial filter selectors is supported in text indexes via the selector field +% This adds backwards support for existing indexes that might have a selector in it +get_legacy_selector(Def) -> case proplists:get_value(<<"selector">>, Def) of undefined -> undefined; {[]} -> undefined; Selector -> Selector end. - +-ifdef(TEST). +-include_lib("eunit/include/eunit.hrl"). + +index(SelectorName, Selector) -> + { + idx,<<"mango_test_46418cd02081470d93290dc12306ebcb">>, + <<"_design/57e860dee471f40a2c74ea5b72997b81dda36a24">>, + <<"Selected">>,<<"json">>, + {[{<<"fields">>,{[{<<"location">>,<<"asc">>}]}}, + {SelectorName,{Selector}}]}, + [{<<"def">>,{[{<<"fields">>,[<<"location">>]}]}}] + }. + +get_partial_filter_all_docs_test() -> + Idx = #idx{def = all_docs}, + ?assertEqual(undefined, get_partial_filter_selector(Idx)). + +get_partial_filter_undefined_def_test() -> + Idx = #idx{def = undefined}, + ?assertEqual(undefined, get_partial_filter_selector(Idx)). + +get_partial_filter_selector_default_test() -> + Idx = index(<<"partial_filter_selector">>, []), + ?assertEqual(undefined, get_partial_filter_selector(Idx)). + +get_partial_filter_selector_missing_test() -> + Idx = index(<<"partial_filter_selector">>, []), + ?assertEqual(undefined, get_partial_filter_selector(Idx)). + +get_partial_filter_selector_with_selector_test() -> + Selector = [{<<"location">>,{[{<<"$gt">>,<<"FRA">>}]}}], + Idx = index(<<"partial_filter_selector">>, Selector), + ?assertEqual({Selector}, get_partial_filter_selector(Idx)). + +get_partial_filter_selector_with_legacy_selector_test() -> + Selector = [{<<"location">>,{[{<<"$gt">>,<<"FRA">>}]}}], + Idx = index(<<"selector">>, Selector), + ?assertEqual({Selector}, get_partial_filter_selector(Idx)). + +get_partial_filter_selector_with_legacy_default_selector_test() -> + Idx = index(<<"selector">>, []), + ?assertEqual(undefined, get_partial_filter_selector(Idx)). + +-endif. diff --git a/src/mango/src/mango_idx_text.erl b/src/mango/src/mango_idx_text.erl index f90ac7fac..3ccb017ac 100644 --- a/src/mango/src/mango_idx_text.erl +++ b/src/mango/src/mango_idx_text.erl @@ -223,7 +223,13 @@ opts() -> {optional, true}, {default, {[]}} ]}, - {<<"selector">>, [ + {<<"partial_filter_selector">>, [ + {tag, partial_filter_selector}, + {optional, true}, + {default, {[]}}, + {validator, fun mango_opts:validate_selector/1} + ]}, + {<<"selector">>, [ {tag, selector}, {optional, true}, {default, {[]}}, diff --git a/src/mango/src/mango_idx_view.erl b/src/mango/src/mango_idx_view.erl index 7e9e6464c..4cb039c4a 100644 --- a/src/mango/src/mango_idx_view.erl +++ b/src/mango/src/mango_idx_view.erl @@ -199,8 +199,8 @@ opts() -> {tag, fields}, {validator, fun mango_opts:validate_sort/1} ]}, - {<<"selector">>, [ - {tag, selector}, + {<<"partial_filter_selector">>, [ + {tag, partial_filter_selector}, {optional, true}, {default, {[]}}, {validator, fun mango_opts:validate_selector/1} diff --git a/src/mango/src/mango_native_proc.erl b/src/mango/src/mango_native_proc.erl index 82081a976..61d79b7ec 100644 --- a/src/mango/src/mango_native_proc.erl +++ b/src/mango/src/mango_native_proc.erl @@ -135,7 +135,7 @@ index_doc(#st{indexes=Indexes}, Doc) -> get_index_entries({IdxProps}, Doc) -> {Fields} = couch_util:get_value(<<"fields">>, IdxProps), - Selector = get_index_selector(IdxProps), + Selector = get_index_partial_filter_selector(IdxProps), case should_index(Selector, Doc) of false -> []; @@ -159,7 +159,7 @@ get_index_values(Fields, Doc) -> get_text_entries({IdxProps}, Doc) -> - Selector = get_index_selector(IdxProps), + Selector = get_index_partial_filter_selector(IdxProps), case should_index(Selector, Doc) of true -> get_text_entries0(IdxProps, Doc); @@ -168,10 +168,21 @@ get_text_entries({IdxProps}, Doc) -> end. -get_index_selector(IdxProps) -> - case couch_util:get_value(<<"selector">>, IdxProps) of - [] -> {[]}; - Else -> Else +get_index_partial_filter_selector(IdxProps) -> + case couch_util:get_value(<<"partial_filter_selector">>, IdxProps) of + undefined -> + % this is to support legacy text indexes that had the partial_filter_selector + % set as selector + case couch_util:get_value(<<"selector">>, IdxProps, []) of + [] -> + {[]}; + Else -> + Else + end; + [] -> + {[]}; + Else -> + Else end. diff --git a/src/mango/test/16-index-selectors.py b/src/mango/test/16-index-selectors.py index b18945609..cc83251c9 100644 --- a/src/mango/test/16-index-selectors.py +++ b/src/mango/test/16-index-selectors.py @@ -73,20 +73,84 @@ DOCS = [ }, ] +oldschoolddoc = { + "_id": "_design/oldschool", + "language": "query", + "views": { + "oldschool": { + "map": { + "fields": { + "location": "asc" + }, + "selector": { + "location": {"$gte": "FRA"} + } + }, + "reduce": "_count", + "options": { + "def": { + "fields": [ + "location" + ] + } + } + } + } +} + +oldschoolddoctext = { + "_id": "_design/oldschooltext", + "language": "query", + "indexes": { + "oldschooltext": { + "index": { + "default_analyzer": "keyword", + "default_field": {}, + "selector": { + "location": {"$gte": "FRA"} + }, + "fields": [ + { + "name": "location", + "type": "string" + } + ], + "index_array_lengths": True + }, + "analyzer": { + "name": "perfield", + "default": "keyword", + "fields": { + "$default": "standard" + } + } + } + } +} + class IndexSelectorJson(mango.DbPerClass): def setUp(self): self.db.recreate() self.db.save_docs(copy.deepcopy(DOCS)) - def test_saves_selector_in_index(self): + def test_saves_partial_filter_selector_in_index(self): selector = {"location": {"$gte": "FRA"}} - self.db.create_index(["location"], selector=selector) + self.db.create_index(["location"], partial_filter_selector=selector) indexes = self.db.list_indexes() - self.assertEqual(indexes[1]["def"]["selector"], selector) + self.assertEqual(indexes[1]["def"]["partial_filter_selector"], selector) + + def test_saves_selector_in_index_throws(self): + selector = {"location": {"$gte": "FRA"}} + try: + self.db.create_index(["location"], selector=selector) + except Exception, e: + assert e.response.status_code == 400 + else: + raise AssertionError("bad index creation") def test_uses_partial_index_for_query_selector(self): selector = {"location": {"$gte": "FRA"}} - self.db.create_index(["location"], selector=selector, ddoc="Selected", name="Selected") + self.db.create_index(["location"], partial_filter_selector=selector, ddoc="Selected", name="Selected") resp = self.db.find(selector, explain=True, use_index='Selected') self.assertEqual(resp["index"]["name"], "Selected") docs = self.db.find(selector, use_index='Selected') @@ -95,7 +159,7 @@ class IndexSelectorJson(mango.DbPerClass): def test_uses_partial_index_with_different_selector(self): selector = {"location": {"$gte": "FRA"}} selector2 = {"location": {"$gte": "A"}} - self.db.create_index(["location"], selector=selector, ddoc="Selected", name="Selected") + self.db.create_index(["location"], partial_filter_selector=selector, ddoc="Selected", name="Selected") resp = self.db.find(selector2, explain=True, use_index='Selected') self.assertEqual(resp["index"]["name"], "Selected") docs = self.db.find(selector2, use_index='Selected') @@ -103,28 +167,36 @@ class IndexSelectorJson(mango.DbPerClass): def test_doesnot_use_selector_when_not_specified(self): selector = {"location": {"$gte": "FRA"}} - self.db.create_index(["location"], selector=selector, ddoc="Selected", name="Selected") + self.db.create_index(["location"], partial_filter_selector=selector, ddoc="Selected", name="Selected") resp = self.db.find(selector, explain=True) self.assertEqual(resp["index"]["name"], "_all_docs") def test_doesnot_use_selector_when_not_specified_with_index(self): selector = {"location": {"$gte": "FRA"}} - self.db.create_index(["location"], selector=selector, ddoc="Selected", name="Selected") + self.db.create_index(["location"], partial_filter_selector=selector, ddoc="Selected", name="Selected") self.db.create_index(["location"], name="NotSelected") resp = self.db.find(selector, explain=True) self.assertEqual(resp["index"]["name"], "NotSelected") + def test_old_selector_still_supported(self): + selector = {"location": {"$gte": "FRA"}} + self.db.save_doc(oldschoolddoc) + resp = self.db.find(selector, explain=True, use_index='oldschool') + self.assertEqual(resp["index"]["name"], "oldschool") + docs = self.db.find(selector, use_index='oldschool') + self.assertEqual(len(docs), 3) + @unittest.skipUnless(mango.has_text_service(), "requires text service") - def test_text_saves_selector_in_index(self): + def test_text_saves_partialfilterselector_in_index(self): selector = {"location": {"$gte": "FRA"}} - self.db.create_text_index(fields=[{"name":"location", "type":"string"}], selector=selector) + self.db.create_text_index(fields=[{"name":"location", "type":"string"}], partial_filter_selector=selector) indexes = self.db.list_indexes() - self.assertEqual(indexes[1]["def"]["selector"], selector) + self.assertEqual(indexes[1]["def"]["partial_filter_selector"], selector) @unittest.skipUnless(mango.has_text_service(), "requires text service") def test_text_uses_partial_index_for_query_selector(self): selector = {"location": {"$gte": "FRA"}} - self.db.create_text_index(fields=[{"name":"location", "type":"string"}], selector=selector, ddoc="Selected", name="Selected") + self.db.create_text_index(fields=[{"name":"location", "type":"string"}], partial_filter_selector=selector, ddoc="Selected", name="Selected") resp = self.db.find(selector, explain=True, use_index='Selected') self.assertEqual(resp["index"]["name"], "Selected") docs = self.db.find(selector, use_index='Selected', fields=['_id', 'location']) @@ -134,7 +206,7 @@ class IndexSelectorJson(mango.DbPerClass): def test_text_uses_partial_index_with_different_selector(self): selector = {"location": {"$gte": "FRA"}} selector2 = {"location": {"$gte": "A"}} - self.db.create_text_index(fields=[{"name":"location", "type":"string"}], selector=selector, ddoc="Selected", name="Selected") + self.db.create_text_index(fields=[{"name":"location", "type":"string"}], partial_filter_selector=selector, ddoc="Selected", name="Selected") resp = self.db.find(selector2, explain=True, use_index='Selected') self.assertEqual(resp["index"]["name"], "Selected") docs = self.db.find(selector2, use_index='Selected') @@ -143,14 +215,23 @@ class IndexSelectorJson(mango.DbPerClass): @unittest.skipUnless(mango.has_text_service(), "requires text service") def test_text_doesnot_use_selector_when_not_specified(self): selector = {"location": {"$gte": "FRA"}} - self.db.create_text_index(fields=[{"name":"location", "type":"string"}], selector=selector, ddoc="Selected", name="Selected") + self.db.create_text_index(fields=[{"name":"location", "type":"string"}], partial_filter_selector=selector, ddoc="Selected", name="Selected") resp = self.db.find(selector, explain=True) self.assertEqual(resp["index"]["name"], "_all_docs") @unittest.skipUnless(mango.has_text_service(), "requires text service") def test_text_doesnot_use_selector_when_not_specified_with_index(self): selector = {"location": {"$gte": "FRA"}} - self.db.create_text_index(fields=[{"name":"location", "type":"string"}], selector=selector, ddoc="Selected", name="Selected") + self.db.create_text_index(fields=[{"name":"location", "type":"string"}], partial_filter_selector=selector, ddoc="Selected", name="Selected") self.db.create_text_index(fields=[{"name":"location", "type":"string"}], name="NotSelected") resp = self.db.find(selector, explain=True) - self.assertEqual(resp["index"]["name"], "NotSelected")
\ No newline at end of file + self.assertEqual(resp["index"]["name"], "NotSelected") + + @unittest.skipUnless(mango.has_text_service(), "requires text service") + def test_text_old_selector_still_supported(self): + selector = {"location": {"$gte": "FRA"}} + self.db.save_doc(oldschoolddoctext) + resp = self.db.find(selector, explain=True, use_index='oldschooltext') + self.assertEqual(resp["index"]["name"], "oldschooltext") + docs = self.db.find(selector, use_index='oldschooltext') + self.assertEqual(len(docs), 3)
\ No newline at end of file diff --git a/src/mango/test/mango.py b/src/mango/test/mango.py index 2c8971485..342d5fdc8 100644 --- a/src/mango/test/mango.py +++ b/src/mango/test/mango.py @@ -84,7 +84,8 @@ class Database(object): r.raise_for_status() return r.json() - def create_index(self, fields, idx_type="json", name=None, ddoc=None, selector=None): + def create_index(self, fields, idx_type="json", name=None, ddoc=None, + partial_filter_selector=None, selector=None): body = { "index": { "fields": fields @@ -98,6 +99,8 @@ class Database(object): body["ddoc"] = ddoc if selector is not None: body["index"]["selector"] = selector + if partial_filter_selector is not None: + body["index"]["partial_filter_selector"] = partial_filter_selector body = json.dumps(body) r = self.sess.post(self.path("_index"), data=body) r.raise_for_status() @@ -105,8 +108,9 @@ class Database(object): assert r.json()["name"] is not None return r.json()["result"] == "created" - def create_text_index(self, analyzer=None, selector=None, idx_type="text", - default_field=None, fields=None, name=None, ddoc=None,index_array_lengths=None): + def create_text_index(self, analyzer=None, idx_type="text", + partial_filter_selector=None, default_field=None, fields=None, + name=None, ddoc=None,index_array_lengths=None): body = { "index": { }, @@ -121,8 +125,8 @@ class Database(object): body["index"]["default_field"] = default_field if index_array_lengths is not None: body["index"]["index_array_lengths"] = index_array_lengths - if selector is not None: - body["index"]["selector"] = selector + if partial_filter_selector is not None: + body["index"]["partial_filter_selector"] = partial_filter_selector if fields is not None: body["index"]["fields"] = fields if ddoc is not None: |