Repository: couchdb-mango Updated Branches: refs/heads/2835-fix-index-selection [created] 62362de9a
Fix user defined index selection This fix modifies indexable_fields to remove extraneous fields that are created by the mango_selector_text:convert function so that the index can now be used when accessing arrays, $in operations, and $size operations. COUCHDB-2835 Project: http://git-wip-us.apache.org/repos/asf/couchdb-mango/repo Commit: http://git-wip-us.apache.org/repos/asf/couchdb-mango/commit/62362de9 Tree: http://git-wip-us.apache.org/repos/asf/couchdb-mango/tree/62362de9 Diff: http://git-wip-us.apache.org/repos/asf/couchdb-mango/diff/62362de9 Branch: refs/heads/2835-fix-index-selection Commit: 62362de9a2d46f392315a4399d8a7bb5e1c499e0 Parents: 090dc67 Author: Tony Sun <tony....@cloudant.com> Authored: Fri Oct 30 15:02:50 2015 -0700 Committer: Tony Sun <tony....@cloudant.com> Committed: Fri Oct 30 15:02:50 2015 -0700 ---------------------------------------------------------------------- src/mango_idx_text.erl | 20 ++++++++++++++ test/07-text-custom-field-list-test.py | 42 +++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/couchdb-mango/blob/62362de9/src/mango_idx_text.erl ---------------------------------------------------------------------- diff --git a/src/mango_idx_text.erl b/src/mango_idx_text.erl index 9ade6e2..ba92a32 100644 --- a/src/mango_idx_text.erl +++ b/src/mango_idx_text.erl @@ -283,6 +283,23 @@ indexable_fields(Fields, {op_and, Args}) when is_list(Args) -> lists:foldl(fun(Arg, Fields0) -> indexable_fields(Fields0, Arg) end, Fields, Args); +%% For queries that use array element access or $in operations, two +%% fields get generated by mango_selector_text:convert. At index +%% definition time, only one field gets defined. In this situation, we +%% remove the extra generated field so that the index can be used. For +%% all other situations, we include the fields as normal. +indexable_fields(Fields, {op_or, [{op_field, Field0}, + {op_field, {[Name | _], _}} = Field1]}) -> + case lists:member(<<"[]">>, Name) of + true -> + indexable_fields(Fields, Field1); + false -> + Fields1 = indexable_fields(Fields, Field0), + indexable_fields(Fields1, Field1) + end; +indexable_fields(Fields, {op_or, Args}) when is_list(Args) -> + lists:foldl(fun(Arg, Fields0) -> indexable_fields(Fields0, Arg) end, + Fields, Args); indexable_fields(Fields, {op_or, Args}) when is_list(Args) -> lists:foldl(fun(Arg, Fields0) -> indexable_fields(Fields0, Arg) end, Fields, Args); @@ -294,6 +311,9 @@ indexable_fields(Fields, {op_not, {ExistsQuery, Arg}}) when is_tuple(Arg) -> indexable_fields(Fields, {op_insert, Arg}) when is_binary(Arg) -> Fields; +%% fieldname.[]:length is not a user defined field. +indexable_fields(Fields, {op_field, {[_, <<":length">>], _}}) -> + Fields; indexable_fields(Fields, {op_field, {Name, _}}) -> [iolist_to_binary(Name) | Fields]; http://git-wip-us.apache.org/repos/asf/couchdb-mango/blob/62362de9/test/07-text-custom-field-list-test.py ---------------------------------------------------------------------- diff --git a/test/07-text-custom-field-list-test.py b/test/07-text-custom-field-list-test.py index f299ef7..8a8a5f8 100644 --- a/test/07-text-custom-field-list-test.py +++ b/test/07-text-custom-field-list-test.py @@ -44,6 +44,48 @@ class CustomFieldsTest(mango.UserDocsTextTests): docs = self.db.find({"age": 22, "manager": False}) assert len(docs) == 0 + def test_element_acess(self): + docs = self.db.find({"favorites.0": "Ruby"}) + assert len(docs) == 3 + for d in docs: + assert "Ruby" in d["favorites"] + + # This should throw an exception because we only index the array + # favorites.[], and not the string field favorites + def test_index_selection(self): + try: + self.db.find({"selector": {"$or": [{"favorites": "Ruby"}, + {"favorites.0":"Ruby"}]}}) + except Exception, e: + assert e.response.status_code == 400 + + def test_in_with_array(self): + vals = ["Lisp", "Python"] + docs = self.db.find({"favorites": {"$in": vals}}) + assert len(docs) == 10 + + # This should also throw an error because we only indexed + # favorites.[] of type string. For the following query to work, the + # user has to index favorites.[] of type number, and also + # favorites.[].Versions.Alpha of type string. + def test_in_different_types(self): + vals = ["Random Garbage", 52, {"Versions": {"Alpha": "Beta"}}] + try: + self.db.find({"favorites": {"$in": vals}}) + except Exception, e: + assert e.response.status_code == 400 + + # This test differs from the situation where we index everything. + # When we index everything the actual number of docs that gets + # returned is 5. That's because of the special situation where we + # have an array of an array, i.e: [["Lisp"]], because we're indexing + # specifically favorites.[] of type string. So it does not count + # the example and we only get 4 back. + def test_nin_with_array(self): + vals = ["Lisp", "Python"] + docs = self.db.find({"favorites": {"$nin": vals}}) + assert len(docs) == 4 + def test_missing(self): self.db.find({"location.state": "Nevada"})