diff options
author | Robert Newson <rnewson@apache.org> | 2020-07-20 11:16:14 +0100 |
---|---|---|
committer | Paul J. Davis <paul.joseph.davis@gmail.com> | 2020-09-09 09:44:58 -0500 |
commit | 47a9454bd6f7b2f9254621320d572e985664f4f2 (patch) | |
tree | d1062123f4e21b556035f7492aaed658af3c4454 | |
parent | 697d59948b92f8260f2dc7444ad5924e58e7aa36 (diff) | |
download | couchdb-47a9454bd6f7b2f9254621320d572e985664f4f2.tar.gz |
Add support for group_reduce in reverse order
-rw-r--r-- | src/ebtree.erl | 35 |
1 files changed, 29 insertions, 6 deletions
diff --git a/src/ebtree.erl b/src/ebtree.erl index ca020cc07..94e757b3e 100644 --- a/src/ebtree.erl +++ b/src/ebtree.erl @@ -15,6 +15,7 @@ reduce/4, full_reduce/2, group_reduce/7, + group_reduce/8, validate_tree/2 ]). @@ -248,6 +249,13 @@ do_reduce(#tree{} = Tree, MapValues, ReduceValues) when is_list(MapValues), is_l do_reduce(Tree, [], [reduce_values(Tree, MapValues, false) | ReduceValues]). +%% @equiv group_reduce(Db, Tree, StartKey, EndKey, GroupKeyFun, UserAccFun, UserAcc0, []) +-spec group_reduce(Db :: term(), Tree :: #tree{}, StartKey :: term(), EndKey :: term(), + GroupKeyFun :: fun(), UserAccFun :: fun(), UserAcc0 :: term()) -> term(). +group_reduce(Db, #tree{} = Tree, StartKey, EndKey, GroupKeyFun, UserAccFun, UserAcc0) -> + group_reduce(Db, Tree, StartKey, EndKey, GroupKeyFun, UserAccFun, UserAcc0, []). + + %% @doc Calculate the reduce value for all groups in the specified range. %% @param Db An erlfdb database or transaction. %% @param Tree The ebtree. @@ -256,19 +264,24 @@ do_reduce(#tree{} = Tree, MapValues, ReduceValues) when is_list(MapValues), is_l %% @param GroupKeyFun A function that takes a key as a parameter and returns the group key. %% @param UserAccFun A function called when a new group reduction is calculated and returns an acc. %% @param UserAcc0 The initial accumulator. +%% @param Options Currently supported options are [{dir, fwd}] and [{dir, rev}] %% @returns the final accumulator. -spec group_reduce(Db :: term(), Tree :: #tree{}, StartKey :: term(), EndKey :: term(), - GroupKeyFun :: fun(), UserAccFun :: fun(), UserAcc0 :: term()) -> term(). -group_reduce(Db, #tree{} = Tree, StartKey, EndKey, GroupKeyFun, UserAccFun, UserAcc0) -> + GroupKeyFun :: fun(), UserAccFun :: fun(), UserAcc0 :: term(), Options :: list()) -> term(). +group_reduce(Db, #tree{} = Tree, StartKey, EndKey, GroupKeyFun, UserAccFun, UserAcc0, Options) -> + Dir = proplists:get_value(dir, Options, fwd), NoGroupYet = ?MIN, Fun = fun ({visit, Key, Value}, {CurrentGroup, UserAcc, MapAcc, ReduceAcc}) -> + BeforeStart = less_than(Tree, Key, StartKey), AfterEnd = greater_than(Tree, Key, EndKey), InRange = in_range(Tree, StartKey, Key, EndKey), KeyGroup = GroupKeyFun(Key), SameGroup = CurrentGroup =:= KeyGroup, if - AfterEnd -> + Dir == fwd andalso AfterEnd -> + {stop, {CurrentGroup, UserAcc, MapAcc, ReduceAcc}}; + Dir == rev andalso BeforeStart -> {stop, {CurrentGroup, UserAcc, MapAcc, ReduceAcc}}; SameGroup -> {ok, {CurrentGroup, UserAcc, [{Key, Value} | MapAcc], ReduceAcc}}; @@ -288,9 +301,13 @@ group_reduce(Db, #tree{} = Tree, StartKey, EndKey, GroupKeyFun, UserAccFun, User FirstInRange = in_range(Tree, StartKey, FirstKey, EndKey), LastInRange = in_range(Tree, StartKey, LastKey, EndKey), if - BeforeStart -> + Dir == fwd andalso BeforeStart -> {skip, {CurrentGroup, UserAcc, MapAcc, ReduceAcc}}; - AfterEnd -> + Dir == rev andalso AfterEnd -> + {skip, {CurrentGroup, UserAcc, MapAcc, ReduceAcc}}; + Dir == fwd andalso AfterEnd -> + {stop, {CurrentGroup, UserAcc, MapAcc, ReduceAcc}}; + Dir == rev andalso BeforeStart -> {stop, {CurrentGroup, UserAcc, MapAcc, ReduceAcc}}; Whole andalso FirstInRange andalso LastInRange -> {skip, {CurrentGroup, UserAcc, MapAcc, [Reduction | ReduceAcc]}}; @@ -298,7 +315,7 @@ group_reduce(Db, #tree{} = Tree, StartKey, EndKey, GroupKeyFun, UserAccFun, User {ok, {CurrentGroup, UserAcc, MapAcc, ReduceAcc}} end end, - {CurrentGroup, UserAcc1, MapValues, ReduceValues} = fold(Db, Tree, Fun, {NoGroupYet, UserAcc0, [], []}), + {CurrentGroup, UserAcc1, MapValues, ReduceValues} = fold(Db, Tree, Fun, {NoGroupYet, UserAcc0, [], []}, Options), if MapValues /= [] orelse ReduceValues /= [] -> FinalGroup = do_reduce(Tree, MapValues, ReduceValues), @@ -1113,6 +1130,12 @@ group_reduce_level_test_() -> ?_test(?assertEqual([{[1, 0], 408}, {[1, 1], 441}, {[1, 2], 376}], group_reduce(Db, Tree, [1], [2], GroupKeyFun, UserAccFun, []))), + ?_test(?assertEqual([{[1, 0], 408}, {[1, 1], 441}, {[1, 2], 376}], + group_reduce(Db, Tree, [1], [2], GroupKeyFun, UserAccFun, [], [{dir, fwd}]))), + + ?_test(?assertEqual([{[1, 2], 376}, {[1, 1], 441}, {[1, 0], 408}], + group_reduce(Db, Tree, [1], [2], GroupKeyFun, UserAccFun, [], [{dir, rev}]))), + ?_test(?assertEqual([{[0,0],432}, {[0,1],468}, {[0,2],400}, {[1,0],408}, {[1,1],441}, {[1,2],376}, {[2,0],384}, {[2,1],416}, {[2,2],450}, {[3,0],459}, {[3,1],392}, {[3,2],424}], group_reduce(Db, Tree, ebtree:min(), ebtree:max(), GroupKeyFun, UserAccFun, []))) |