couchdb-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From dav...@apache.org
Subject [16/50] git commit: Make _stats accept user-generated aggregates
Date Sat, 18 Jan 2014 00:47:41 GMT
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/ead76caa
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch/tree/ead76caa
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch/diff/ead76caa

Branch: refs/heads/import
Commit: ead76caa077f50b4abbaeeee078c2507d68eea08
Parents: ced3a69
Author: Adam Kocoloski <adam@cloudant.com>
Authored: Mon Aug 13 15:06:44 2012 -0400
Committer: Paul J. Davis <paul.joseph.davis@gmail.com>
Committed: Fri Jan 17 16:44:30 2014 -0800

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


http://git-wip-us.apache.org/repos/asf/couchdb-couch/blob/ead76caa/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]),


Mime
View raw message