summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgarren smith <garren.smith@gmail.com>2017-09-21 17:30:49 +0200
committerJoan Touzet <wohali@users.noreply.github.com>2017-10-19 19:13:25 -0400
commitb98de40c8b75a012e7c565fcb4803e8fcc91acd5 (patch)
tree1d26059b73283eff7685c23aa5e5f3ecf9bb522e
parent31b0abb8cbd49fb399767222ed9f58f15aaa38db (diff)
downloadcouchdb-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.erl8
-rw-r--r--src/mango/src/mango_idx.erl61
-rw-r--r--src/mango/src/mango_idx_text.erl8
-rw-r--r--src/mango/src/mango_idx_view.erl4
-rw-r--r--src/mango/src/mango_native_proc.erl23
-rw-r--r--src/mango/test/16-index-selectors.py111
-rw-r--r--src/mango/test/mango.py14
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: