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
+]