You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by da...@apache.org on 2014/02/05 00:06:20 UTC

[07/50] couch commit: updated refs/heads/import to c3116d7

Make _stats accept user-generated aggregates

Sometimes the user already has aggregate data that needs to be merged.
This patch supports that use case by allowing the user to emit a JSON
Object from the map phase containing 'sum', 'count', 'min', 'max', and
'sumsqr' keys.  Users can freely intermix raw values and precomputed
aggregates in a single view.

BugzID: 14286


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

Branch: refs/heads/import
Commit: 0da481a29fbda5dc445873d63ee330c2999a5cc3
Parents: 9ae2380
Author: Adam Kocoloski <ad...@cloudant.com>
Authored: Mon Aug 13 15:06:44 2012 -0400
Committer: Paul J. Davis <pa...@gmail.com>
Committed: Tue Feb 4 17:03:23 2014 -0600

----------------------------------------------------------------------
 src/couch_query_servers.erl | 55 +++++++++++++++++++++++++++++++++-------
 1 file changed, 46 insertions(+), 9 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/0da481a2/src/couch_query_servers.erl
----------------------------------------------------------------------
diff --git a/src/couch_query_servers.erl b/src/couch_query_servers.erl
index 984c029..fb7f354 100644
--- a/src/couch_query_servers.erl
+++ b/src/couch_query_servers.erl
@@ -228,15 +228,24 @@ sum_terms([X|Xs], [Y|Ys]) when is_number(X), is_number(Y) ->
 sum_terms(_, _) ->
     throw({invalid_value, <<"builtin _sum function requires map values to be numbers or lists of numbers">>}).
 
-builtin_stats(reduce, []) ->
-    {[]};
-builtin_stats(reduce, [[_,First]|Rest]) when is_number(First) ->
-    Stats = lists:foldl(fun([_K,V], {S,C,Mi,Ma,Sq}) when is_number(V) ->
-        {S+V, C+1, lists:min([Mi, V]), lists:max([Ma, V]), Sq+(V*V)};
-    (_, _) ->
-        throw({invalid_value,
-            <<"builtin _stats function requires map values to be numbers">>})
-    end, {First,1,First,First,First*First}, Rest),
+
+builtin_stats(reduce, [[_,First]|Rest]) ->
+    Acc0 = build_initial_accumulator(First),
+    Stats = lists:foldl(fun
+        ([_K,V], {S,C,Mi,Ma,Sq}) when is_number(V) ->
+            {S+V, C+1, erlang:min(Mi,V), erlang:max(Ma,V), Sq+(V*V)};
+        ([_K,{PreRed}], {S,C,Mi,Ma,Sq}) when is_list(PreRed) ->
+            {
+                S + get_number(sum, PreRed),
+                C + get_number(count, PreRed),
+                erlang:min(get_number(min, PreRed), Mi),
+                erlang:max(get_number(max, PreRed), Ma),
+                Sq + get_number(sumsqr, PreRed)
+            };
+        ([_K,V], _) ->
+            Msg = io_lib:format("non-numeric _stats input: ~w", [V]),
+            throw({invalid_value, iolist_to_binary(Msg)})
+    end, Acc0, Rest),
     {Sum, Cnt, Min, Max, Sqr} = Stats,
     {[{sum,Sum}, {count,Cnt}, {min,Min}, {max,Max}, {sumsqr,Sqr}]};
 
@@ -249,6 +258,34 @@ builtin_stats(rereduce, [[_,First]|Rest]) ->
     {Sum, Cnt, Min, Max, Sqr} = Stats,
     {[{sum,Sum}, {count,Cnt}, {min,Min}, {max,Max}, {sumsqr,Sqr}]}.
 
+build_initial_accumulator(X) when is_number(X) ->
+    {X, 1, X, X, X*X};
+build_initial_accumulator({Props}) ->
+    {
+        get_number(sum, Props),
+        get_number(count, Props),
+        get_number(min, Props),
+        get_number(max, Props),
+        get_number(sumsqr, Props)
+    };
+build_initial_accumulator(Else) ->
+    Msg = io_lib:format("non-numeric _stats input: ~w", [Else]),
+    throw({invalid_value, iolist_to_binary(Msg)}).
+
+get_number(Key, Props) ->
+    case couch_util:get_value(Key, Props) of
+    X when is_number(X) ->
+        X;
+    undefined ->
+        Msg = io_lib:format("user _stats input missing required field ~s",
+            [Key]),
+        throw({invalid_value, iolist_to_binary(Msg)});
+    Else ->
+        Msg = io_lib:format("non-numeric _stats input received for ~s: ~w",
+            [Key, Else]),
+        throw({invalid_value, iolist_to_binary(Msg)})
+    end.
+
 % use the function stored in ddoc.validate_doc_update to test an update.
 validate_doc_update(DDoc, EditDoc, DiskDoc, Ctx, SecObj) ->
     JsonEditDoc = couch_doc:to_json_obj(EditDoc, [revs]),