You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by to...@apache.org on 2015/10/30 23:04:31 UTC

couchdb-mango git commit: Fix user defined index selection

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 <to...@cloudant.com>
Authored: Fri Oct 30 15:02:50 2015 -0700
Committer: Tony Sun <to...@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"})