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 2017/03/31 17:26:41 UTC

[1/2] couchdb-mango git commit: Ignore design docs when using _all_docs

Repository: couchdb-mango
Updated Branches:
  refs/heads/master 6660b37d6 -> 312e2c455


Ignore design docs when using _all_docs

This stops design docs from being returns when using the special
all_docs index


Project: http://git-wip-us.apache.org/repos/asf/couchdb-mango/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-mango/commit/a319d928
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-mango/tree/a319d928
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-mango/diff/a319d928

Branch: refs/heads/master
Commit: a319d9283872c77d1ceea8129829f78dd59a7209
Parents: 6660b37
Author: Garren Smith <ga...@gmail.com>
Authored: Wed Mar 22 14:47:31 2017 +0200
Committer: Garren Smith <ga...@gmail.com>
Committed: Fri Mar 31 19:21:23 2017 +0200

----------------------------------------------------------------------
 src/mango_cursor_view.erl     | 19 ++++++++++++++++++-
 test/02-basic-find-test.py    |  4 ++--
 test/11-ignore-design-docs.py | 39 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 59 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-mango/blob/a319d928/src/mango_cursor_view.erl
----------------------------------------------------------------------
diff --git a/src/mango_cursor_view.erl b/src/mango_cursor_view.erl
index 302acd3..2918a2d 100644
--- a/src/mango_cursor_view.erl
+++ b/src/mango_cursor_view.erl
@@ -20,6 +20,7 @@
 
 -export([
     handle_message/2,
+    handle_all_docs_message/2,
     composite_indexes/2,
     choose_best_index/2
 ]).
@@ -85,11 +86,12 @@ execute(#cursor{db = Db, index = Idx} = Cursor0, UserFun, UserAcc) ->
                 include_docs = true
             },
             Args = apply_opts(Cursor#cursor.opts, BaseArgs),
-            CB = fun ?MODULE:handle_message/2,
             {ok, LastCursor} = case mango_idx:def(Idx) of
                 all_docs ->
+                    CB = fun ?MODULE:handle_all_docs_message/2,
                     fabric:all_docs(Db, CB, Cursor, Args);
                 _ ->
+                    CB = fun ?MODULE:handle_message/2,
                     % Normal view
                     DDoc = ddocid(Idx),
                     Name = mango_idx:name(Idx),
@@ -170,6 +172,15 @@ handle_message({error, Reason}, _Cursor) ->
     {error, Reason}.
 
 
+handle_all_docs_message({row, Props}, Cursor) ->
+    case is_design_doc(Props) of
+        true -> {ok, Cursor};
+        false -> handle_message({row, Props}, Cursor)
+    end;
+handle_all_docs_message(Message, Cursor) ->
+    handle_message(Message, Cursor).
+
+
 handle_doc(#cursor{skip = S} = C, _) when S > 0 ->
     {ok, C#cursor{skip = S - 1}};
 handle_doc(#cursor{limit = L} = C, Doc) when L > 0 ->
@@ -254,3 +265,9 @@ doc_member(Db, RowProps, Opts) ->
                     Else
             end
     end.
+
+is_design_doc(RowProps) ->
+    case couch_util:get_value(id, RowProps) of
+        <<"_design/", _/binary>> -> true;
+        _ -> false
+    end.
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/couchdb-mango/blob/a319d928/test/02-basic-find-test.py
----------------------------------------------------------------------
diff --git a/test/02-basic-find-test.py b/test/02-basic-find-test.py
index c7eb348..e634ce9 100644
--- a/test/02-basic-find-test.py
+++ b/test/02-basic-find-test.py
@@ -237,8 +237,8 @@ class BasicFindTests(mango.UserDocsTests):
 
     def test_empty(self):
         docs = self.db.find({})
-        # 15 users and 9 design docs
-        assert len(docs) == 24
+        # 15 users 
+        assert len(docs) == 15
 
     def test_empty_subsel(self):
         docs = self.db.find({

http://git-wip-us.apache.org/repos/asf/couchdb-mango/blob/a319d928/test/11-ignore-design-docs.py
----------------------------------------------------------------------
diff --git a/test/11-ignore-design-docs.py b/test/11-ignore-design-docs.py
new file mode 100644
index 0000000..ea7165e
--- /dev/null
+++ b/test/11-ignore-design-docs.py
@@ -0,0 +1,39 @@
+# 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 unittest
+
+DOCS = [
+    {
+        "_id": "_design/my-design-doc",
+    },
+    {
+        "_id": "54af50626de419f5109c962f",
+        "user_id": 0,
+        "age": 10,
+        "name": "Jimi"
+    },
+    {
+        "_id": "54af50622071121b25402dc3",
+        "user_id": 1,
+        "age": 11,
+        "name": "Eddie"
+    }
+]
+
+class IgnoreDesignDocsForAllDocsIndexTests(mango.DbPerClass):
+    def test_should_not_return_design_docs(self):
+        self.db.save_docs(DOCS)
+        docs = self.db.find({"_id": {"$gte": None}})
+        assert len(docs) == 2
+


[2/2] couchdb-mango git commit: Add `$allMatch` selector

Posted by ga...@apache.org.
Add `$allMatch` selector

This selector is similar to the existing `$elemMatch` one but requires
all elements of an array value to match the inner selector.


Project: http://git-wip-us.apache.org/repos/asf/couchdb-mango/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-mango/commit/312e2c45
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-mango/tree/312e2c45
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-mango/diff/312e2c45

Branch: refs/heads/master
Commit: 312e2c45535913c190cdef51f6ea65066ccd89dc
Parents: a319d92
Author: Lucas Satabin <lu...@gnieh.org>
Authored: Tue Feb 7 17:40:09 2017 +0100
Committer: Garren Smith <ga...@gmail.com>
Committed: Fri Mar 31 19:25:17 2017 +0200

----------------------------------------------------------------------
 README.md                              |  1 +
 src/mango_selector.erl                 | 37 +++++++++++++++++++++++--
 src/mango_selector_text.erl            |  3 ++
 test/03-operator-test.py               | 40 +++++++++++++++++++++++++++
 test/06-basic-text-test.py             | 43 +++++++++++++++++++++++++++++
 test/07-text-custom-field-list-test.py | 11 ++++++++
 test/friend_docs.py                    | 36 +++++++++++++++++++++++-
 7 files changed, 167 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-mango/blob/312e2c45/README.md
----------------------------------------------------------------------
diff --git a/README.md b/README.md
index e9d4a66..4c4bb60 100644
--- a/README.md
+++ b/README.md
@@ -272,6 +272,7 @@ The list of combining characters:
 * "$nor" - array argument
 * "$all" - array argument (special operator for array values)
 * "$elemMatch" - single argument (special operator for array values)
+* "$allMatch" - single argument (special operator for array values)
 
 ### Condition Operators
 

http://git-wip-us.apache.org/repos/asf/couchdb-mango/blob/312e2c45/src/mango_selector.erl
----------------------------------------------------------------------
diff --git a/src/mango_selector.erl b/src/mango_selector.erl
index c6004cd..691aac7 100644
--- a/src/mango_selector.erl
+++ b/src/mango_selector.erl
@@ -127,6 +127,11 @@ norm_ops({[{<<"$elemMatch">>, {_}=Arg}]}) ->
 norm_ops({[{<<"$elemMatch">>, Arg}]}) ->
     ?MANGO_ERROR({bad_arg, '$elemMatch', Arg});
 
+norm_ops({[{<<"$allMatch">>, {_}=Arg}]}) ->
+    {[{<<"$allMatch">>, norm_ops(Arg)}]};
+norm_ops({[{<<"$allMatch">>, Arg}]}) ->
+    ?MANGO_ERROR({bad_arg, '$allMatch', Arg});
+
 norm_ops({[{<<"$size">>, Arg}]}) when is_integer(Arg), Arg >= 0 ->
     {[{<<"$size">>, Arg}]};
 norm_ops({[{<<"$size">>, Arg}]}) ->
@@ -209,8 +214,9 @@ norm_ops(Value) ->
 % Its important to note that we can only normalize
 % field names like this through boolean operators where
 % we can gaurantee commutativity. We can't necessarily
-% do the same through the '$elemMatch' operators but we
-% can apply the same algorithm to its arguments.
+% do the same through the '$elemMatch' or '$allMatch'
+% operators but we can apply the same algorithm to its
+% arguments.
 norm_fields({[]}) ->
     {[]};
 norm_fields(Selector) ->
@@ -237,6 +243,10 @@ norm_fields({[{<<"$elemMatch">>, Arg}]}, Path) ->
     Cond = {[{<<"$elemMatch">>, norm_fields(Arg)}]},
     {[{Path, Cond}]};
 
+norm_fields({[{<<"$allMatch">>, Arg}]}, Path) ->
+    Cond = {[{<<"$allMatch">>, norm_fields(Arg)}]},
+    {[{Path, Cond}]};
+
 
 % The text operator operates against the internal
 % $default field. This also asserts that the $default
@@ -315,6 +325,9 @@ norm_negations({[{<<"$or">>, Args}]}) ->
 norm_negations({[{<<"$elemMatch">>, Arg}]}) ->
     {[{<<"$elemMatch">>, norm_negations(Arg)}]};
 
+norm_negations({[{<<"$allMatch">>, Arg}]}) ->
+    {[{<<"$allMatch">>, norm_negations(Arg)}]};
+
 % All other conditions can't introduce negations anywhere
 % further down the operator tree.
 norm_negations(Cond) ->
@@ -411,7 +424,7 @@ match({[{<<"$all">>, Args}]}, Values, _Cmp) when is_list(Values) ->
 match({[{<<"$all">>, _Args}]}, _Values, _Cmp) ->
     false;
 
-%% This is for $elemMatch and possibly $in because of our normalizer.
+%% This is for $elemMatch, $allMatch, and possibly $in because of our normalizer.
 %% A selector such as {"field_name": {"$elemMatch": {"$gte": 80, "$lt": 85}}}
 %% gets normalized to:
 %% {[{<<"field_name">>,
@@ -446,6 +459,24 @@ match({[{<<"$elemMatch">>, Arg}]}, Values, Cmp) when is_list(Values) ->
 match({[{<<"$elemMatch">>, _Arg}]}, _Value, _Cmp) ->
     false;
 
+% Matches when all elements in values match the
+% sub-selector Arg.
+match({[{<<"$allMatch">>, Arg}]}, Values, Cmp) when is_list(Values) ->
+    try
+        lists:foreach(fun(V) ->
+            case match(Arg, V, Cmp) of
+              false -> throw(unmatched);
+              _ -> ok
+            end
+        end, Values),
+        true
+    catch
+        _:_ ->
+            false
+    end;
+match({[{<<"$allMatch">>, _Arg}]}, _Value, _Cmp) ->
+    false;
+
 % Our comparison operators are fairly straight forward
 match({[{<<"$lt">>, Arg}]}, Value, Cmp) ->
     Cmp(Value, Arg) < 0;

http://git-wip-us.apache.org/repos/asf/couchdb-mango/blob/312e2c45/src/mango_selector_text.erl
----------------------------------------------------------------------
diff --git a/src/mango_selector_text.erl b/src/mango_selector_text.erl
index b6e1f09..cfa3baf 100644
--- a/src/mango_selector_text.erl
+++ b/src/mango_selector_text.erl
@@ -86,6 +86,9 @@ convert(Path, {[{<<"$all">>, Args}]}) ->
 convert(Path, {[{<<"$elemMatch">>, Arg}]}) ->
     convert([<<"[]">> | Path], Arg);
 
+convert(Path, {[{<<"$allMatch">>, Arg}]}) ->
+    convert([<<"[]">> | Path], Arg);
+
 % Our comparison operators are fairly straight forward
 convert(Path, {[{<<"$lt">>, Arg}]}) when is_list(Arg); is_tuple(Arg);
         Arg =:= null ->

http://git-wip-us.apache.org/repos/asf/couchdb-mango/blob/312e2c45/test/03-operator-test.py
----------------------------------------------------------------------
diff --git a/test/03-operator-test.py b/test/03-operator-test.py
index 50d5bd2..56c2862 100644
--- a/test/03-operator-test.py
+++ b/test/03-operator-test.py
@@ -63,6 +63,46 @@ class OperatorTests(mango.UserDocsTests):
         assert len(docs) == 1
         assert docs[0]["user_id"] == "b"
 
+    def test_all_match(self):
+        amdocs = [
+            {
+                "user_id": "a",
+                "bang": [
+                    {
+                        "foo": 1,
+                        "bar": 2
+                    },
+                    {
+                        "foo": 3,
+                        "bar": 4
+                    }
+                ]
+            },
+            {
+                "user_id": "b",
+                "bang": [
+                    {
+                        "foo": 1,
+                        "bar": 2
+                    },
+                    {
+                        "foo": 4,
+                        "bar": 4
+                    }
+                ]
+            }
+        ]
+        self.db.save_docs(amdocs, w=3)
+        docs = self.db.find({
+            "_id": {"$gt": None},
+            "bang": {"$allMatch": {
+                "foo": {"$mod": [2,1]},
+                "bar": {"$mod": [2,0]}
+            }}
+        })
+        assert len(docs) == 1
+        assert docs[0]["user_id"] == "a"
+
     def test_in_operator_array(self):
         docs = self.db.find({
                 "manager": True,

http://git-wip-us.apache.org/repos/asf/couchdb-mango/blob/312e2c45/test/06-basic-text-test.py
----------------------------------------------------------------------
diff --git a/test/06-basic-text-test.py b/test/06-basic-text-test.py
index 1e3d5df..7f5ce63 100644
--- a/test/06-basic-text-test.py
+++ b/test/06-basic-text-test.py
@@ -571,6 +571,49 @@ class ElemMatchTests(mango.FriendDocsTextTests):
         for d in docs:
             assert d["user_id"] in (10, 11,12)
 
+@unittest.skipUnless(mango.has_text_service(), "requires text service")
+class AllMatchTests(mango.FriendDocsTextTests):
+
+    def test_all_match(self):
+        q = {"friends": {
+                "$allMatch":
+                    {"type": "personal"}
+            }
+        }
+        docs = self.db.find(q)
+        assert len(docs) == 2
+        for d in docs:
+            assert d["user_id"] in (8, 5)
+
+        # Check that we can do logic in allMatch
+        q = {
+            "friends": {
+                "$allMatch": {
+                    "name.first": "Ochoa",
+                    "$or": [
+                        {"type": "work"},
+                        {"type": "personal"}
+                    ]
+                }
+            }
+        }
+        docs = self.db.find(q)
+        assert len(docs) == 1
+        assert docs[0]["user_id"] == 15
+
+        # Same as last, but using $in
+        q = {
+            "friends": {
+                "$allMatch": {
+                    "name.first": "Ochoa",
+                    "type": {"$in": ["work", "personal"]}
+                }
+            }
+        }
+        docs = self.db.find(q)
+        assert len(docs) == 1
+        assert docs[0]["user_id"] == 15
+
 
 # Test numeric strings for $text
 @unittest.skipUnless(mango.has_text_service(), "requires text service")

http://git-wip-us.apache.org/repos/asf/couchdb-mango/blob/312e2c45/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 029c91c..4db11a5 100644
--- a/test/07-text-custom-field-list-test.py
+++ b/test/07-text-custom-field-list-test.py
@@ -145,3 +145,14 @@ class CustomFieldsTest(mango.UserDocsTextTests):
             {"location.state": "Don't Exist"}]})
         assert len(docs) == 1
         assert docs[0]["user_id"] == 10
+
+    def test_all_match(self):
+        docs = self.db.find({
+            "favorites": {
+                "$allMatch": {
+                    "$eq": "Erlang"
+                }
+            }
+        })
+        assert len(docs) == 1
+        assert docs[0]["user_id"] == 10

http://git-wip-us.apache.org/repos/asf/couchdb-mango/blob/312e2c45/test/friend_docs.py
----------------------------------------------------------------------
diff --git a/test/friend_docs.py b/test/friend_docs.py
index e0cf60e..0757961 100644
--- a/test/friend_docs.py
+++ b/test/friend_docs.py
@@ -566,5 +566,39 @@ DOCS =  [
                 "type": "work"
             }
         ]
+    },
+    {
+        "_id": "589f32af493145f890e1b051",
+        "user_id": 15,
+        "name": {
+            "first": "Tanisha",
+            "last": "Bowers"
+        },
+        "friends": [
+            {
+                "id": 0,
+                "name": {
+                    "first": "Ochoa",
+                    "last": "Pratt"
+                },
+                "type": "personal"
+            },
+            {
+                "id": 1,
+                "name": {
+                    "first": "Ochoa",
+                    "last": "Romero"
+                },
+                "type": "personal"
+            },
+            {
+                "id": 2,
+                "name": {
+                    "first": "Ochoa",
+                    "last": "Bowman"
+                },
+                "type": "work"
+            }
+        ]
     }
-]
\ No newline at end of file
+]