Repository: couchdb-couch-mrview Updated Branches: refs/heads/master 2341c38c6 -> 6ec35971e
Refactor to add an interface for view_state of #mrheader The goal of this refactoring is to make it easier to understand and maintain view_state data structure of #mrheader. Unlike record #mrview in #mrst, the data structure that represents view_states in #mrheader is a tuple (of tuples) and its definition and application spreads across all couch_mrview_util.erl module. This makes it hard to follow and understand how it used or troubleshoot mrview in case of the problems. This change introduces an interface for generation and accessing view_state and also separates generation of reduction closures from mrview's `reduce`, `reduce_to_count` and `fold_reduce` functions as they rely on view_state's structure. Note that this refactoring also removes explicit set of generic ordering function on collation option "raw" as it is a default of couch_btree:open on missing option for `less` function. Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch-mrview/repo Commit: http://git-wip-us.apache.org/repos/asf/couchdb-couch-mrview/commit/6ec35971 Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch-mrview/tree/6ec35971 Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch-mrview/diff/6ec35971 Branch: refs/heads/master Commit: 6ec35971ebd96133cc6a34fa29f1d364049a7ecd Parents: 2341c38 Author: Eric Avdey <e...@eiri.ca> Authored: Fri Jan 15 16:12:55 2016 -0400 Committer: Eric Avdey <e...@eiri.ca> Committed: Tue Jan 19 11:39:40 2016 -0400 ---------------------------------------------------------------------- src/couch_mrview_util.erl | 235 +++++++++++++++++++++++++---------------- 1 file changed, 143 insertions(+), 92 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/couchdb-couch-mrview/blob/6ec35971/src/couch_mrview_util.erl ---------------------------------------------------------------------- diff --git a/src/couch_mrview_util.erl b/src/couch_mrview_util.erl index c30460c..51e51d8 100644 --- a/src/couch_mrview_util.erl +++ b/src/couch_mrview_util.erl @@ -200,7 +200,7 @@ init_state(Db, Fd, #mrst{views=Views}=State, nil) -> purge_seq=couch_db:get_purge_seq(Db), id_btree_state=nil, log_btree_state=nil, - view_states=[{nil, nil, nil, 0, 0} || _ <- Views] + view_states=[make_view_state(#mrview{}) || _ <- Views] }, init_state(Db, Fd, State, Header); % read <= 1.2.x header record and transpile it to >=1.3.x @@ -215,8 +215,8 @@ init_state(Db, Fd, State, #index_header{ purge_seq=PurgeSeq, id_btree_state=IdBtreeState, log_btree_state=nil, - view_states=[{Bt, nil, nil, USeq, PSeq} || {Bt, USeq, PSeq} <- ViewStates] - }); + view_states=[make_view_state(V) || V <- ViewStates] + }); init_state(Db, Fd, State, Header) -> #mrst{ language=Lang, @@ -232,12 +232,6 @@ init_state(Db, Fd, State, Header) -> view_states=ViewStates } = Header, - StateUpdate = fun - ({_, _, _, _, _}=St) -> St; - (St) -> {St, nil, nil, 0, 0} - end, - ViewStates2 = lists:map(StateUpdate, ViewStates), - IdBtOpts = [{compression, couch_db:compression(Db)}], {ok, IdBtree} = couch_btree:open(IdBtreeState, Fd, IdBtOpts), {ok, LogBtree} = case SeqIndexed orelse KeySeqIndexed of @@ -246,7 +240,7 @@ init_state(Db, Fd, State, Header) -> end, OpenViewFun = fun(St, View) -> open_view(Db, Fd, Lang, St, View) end, - Views2 = lists:zipwith(OpenViewFun, ViewStates2, Views), + Views2 = lists:zipwith(OpenViewFun, ViewStates, Views), State#mrst{ fd=Fd, @@ -258,45 +252,33 @@ init_state(Db, Fd, State, Header) -> views=Views2 }. -open_view(Db, Fd, Lang, {BTState, SeqBTState, KSeqBTState, USeq, PSeq}, View) -> - FunSrcs = [FunSrc || {_Name, FunSrc} <- View#mrview.reduce_funs], - ReduceFun = - fun(reduce, KVs) -> - KVs2 = detuple_kvs(expand_dups(KVs, []), []), - {ok, Result} = couch_query_servers:reduce(Lang, FunSrcs, KVs2), - {length(KVs2), Result}; - (rereduce, Reds) -> - Count = lists:sum([Count0 || {Count0, _} <- Reds]), - UsrReds = [UsrRedsList || {_, UsrRedsList} <- Reds], - {ok, Result} = couch_query_servers:rereduce(Lang, FunSrcs, UsrReds), - {Count, Result} - end, - - Less = case couch_util:get_value(<<"collation">>, View#mrview.options) of - <<"raw">> -> fun(A, B) -> A < B end; - _ -> fun couch_ejson_compare:less_json_ids/2 - end, - +open_view(Db, Fd, Lang, ViewState, View) -> + ReduceFun = make_reduce_fun(Lang, View#mrview.reduce_funs), + LessFun = maybe_define_less_fun(View), + Compression = couch_db:compression(Db), + BTState = get_key_btree_state(ViewState), ViewBtOpts = [ - {less, Less}, + {less, LessFun}, {reduce, ReduceFun}, - {compression, couch_db:compression(Db)} + {compression, Compression} ], {ok, Btree} = couch_btree:open(BTState, Fd, ViewBtOpts), BySeqReduceFun = fun couch_db_updater:btree_by_seq_reduce/2, {ok, SeqBtree} = if View#mrview.seq_indexed -> + SeqBTState = get_seq_btree_state(ViewState), ViewSeqBtOpts = [{reduce, BySeqReduceFun}, - {compression, couch_db:compression(Db)}], + {compression, Compression}], couch_btree:open(SeqBTState, Fd, ViewSeqBtOpts); true -> {ok, nil} end, {ok, KeyBySeqBtree} = if View#mrview.keyseq_indexed -> - KeyBySeqBtOpts = [{less, Less}, + KSeqBTState = get_kseq_btree_state(ViewState), + KeyBySeqBtOpts = [{less, LessFun}, {reduce, BySeqReduceFun}, - {compression, couch_db:compression(Db)}], + {compression, Compression}], couch_btree:open(KSeqBTState, Fd, KeyBySeqBtOpts); true -> {ok, nil} @@ -305,8 +287,8 @@ open_view(Db, Fd, Lang, {BTState, SeqBTState, KSeqBTState, USeq, PSeq}, View) -> View#mrview{btree=Btree, seq_btree=SeqBtree, key_byseq_btree=KeyBySeqBtree, - update_seq=USeq, - purge_seq=PSeq}. + update_seq=get_update_seq(ViewState), + purge_seq=get_purge_seq(ViewState)}. temp_view_to_ddoc({Props}) -> @@ -341,18 +323,9 @@ all_docs_reduce_to_count(Reductions) -> reduce_to_count(nil) -> 0; reduce_to_count(Reductions) -> - Reduce = fun - (reduce, KVs) -> - Counts = [ - case V of {dups, Vals} -> length(Vals); _ -> 1 end - || {_,V} <- KVs - ], - {lists:sum(Counts), []}; - (rereduce, Reds) -> - {lists:sum([Count0 || {Count0, _} <- Reds]), []} - end, - {Count, _} = couch_btree:final_reduce(Reduce, Reductions), - Count. + CountReduceFun = fun count_reduce/2, + FinalReduction = couch_btree:final_reduce(CountReduceFun, Reductions), + get_count(FinalReduction). %% @doc get all changes for a view get_view_changes_count(View) -> @@ -414,25 +387,13 @@ fold_reduce({NthRed, Lang, View}, Fun, Acc, Options) -> btree=Bt, reduce_funs=RedFuns } = View, - LPad = lists:duplicate(NthRed - 1, []), - RPad = lists:duplicate(length(RedFuns) - NthRed, []), - {_Name, FunSrc} = lists:nth(NthRed,RedFuns), - ReduceFun = fun - (reduce, KVs0) -> - KVs1 = detuple_kvs(expand_dups(KVs0, []), []), - {ok, Red} = couch_query_servers:reduce(Lang, [FunSrc], KVs1), - {0, LPad ++ Red ++ RPad}; - (rereduce, Reds) -> - ExtractRed = fun({_, UReds0}) -> [lists:nth(NthRed, UReds0)] end, - UReds = lists:map(ExtractRed, Reds), - {ok, Red} = couch_query_servers:rereduce(Lang, [FunSrc], UReds), - {0, LPad ++ Red ++ RPad} - end, + ReduceFun = make_user_reds_reduce_fun(Lang, RedFuns, NthRed), WrapperFun = fun({GroupedKey, _}, PartialReds, Acc0) -> - {_, Reds} = couch_btree:final_reduce(ReduceFun, PartialReds), - Fun(GroupedKey, lists:nth(NthRed, Reds), Acc0) + FinalReduction = couch_btree:final_reduce(ReduceFun, PartialReds), + UserReductions = get_user_reds(FinalReduction), + Fun(GroupedKey, lists:nth(NthRed, UserReductions), Acc0) end, couch_btree:fold_reduce(Bt, WrapperFun, Acc, Options). @@ -610,37 +571,12 @@ make_header(State) -> views=Views } = State, - ViewStates = lists:foldr(fun(V, Acc) -> - SeqBtState = case V#mrview.seq_indexed of - true -> - couch_btree:get_state(V#mrview.seq_btree); - _ -> - nil - end, - KSeqBtState = case V#mrview.keyseq_indexed of - true -> - couch_btree:get_state(V#mrview.key_byseq_btree); - _ -> - nil - end, - [{couch_btree:get_state(V#mrview.btree), - SeqBtState, - KSeqBtState, - V#mrview.update_seq, - V#mrview.purge_seq} | Acc] - end, [], Views), - - LogBtreeState = case LogBtree of - nil -> nil; - _ -> couch_btree:get_state(LogBtree) - end, - #mrheader{ seq=Seq, purge_seq=PurgeSeq, - id_btree_state=couch_btree:get_state(IdBtree), - log_btree_state= LogBtreeState, - view_states=ViewStates + id_btree_state=get_btree_state(IdBtree), + log_btree_state=get_btree_state(LogBtree), + view_states=[make_view_state(V) || V <- Views] }. @@ -1004,6 +940,121 @@ old_view_format(View) -> %% End of <= 1.2.x upgrade code. +make_view_state(#mrview{} = View) -> + BTState = get_btree_state(View#mrview.btree), + SeqBTState = case View#mrview.seq_indexed of + true -> + get_btree_state(View#mrview.seq_btree); + _ -> + nil + end, + KSeqBTState = case View#mrview.keyseq_indexed of + true -> + get_btree_state(View#mrview.key_byseq_btree); + _ -> + nil + end, + { + BTState, + SeqBTState, + KSeqBTState, + View#mrview.update_seq, + View#mrview.purge_seq + }; +make_view_state({BTState, UpdateSeq, PurgeSeq}) -> + {BTState, nil, nil, UpdateSeq, PurgeSeq}; +make_view_state(nil) -> + {nil, nil, nil, 0, 0}. + + +get_key_btree_state(ViewState) -> + element(1, ViewState). + +get_seq_btree_state(ViewState) -> + element(2, ViewState). + +get_kseq_btree_state(ViewState) -> + element(3, ViewState). + +get_update_seq(ViewState) -> + element(4, ViewState). + +get_purge_seq(ViewState) -> + element(5, ViewState). + +get_count(Reduction) -> + element(1, Reduction). + +get_user_reds(Reduction) -> + element(2, Reduction). + + +make_reduce_fun(Lang, ReduceFuns) -> + FunSrcs = [FunSrc || {_, FunSrc} <- ReduceFuns], + fun + (reduce, KVs0) -> + KVs = detuple_kvs(expand_dups(KVs0, []), []), + {ok, Result} = couch_query_servers:reduce(Lang, FunSrcs, KVs), + {length(KVs), Result}; + (rereduce, Reds) -> + ExtractFun = fun(Red, {CountsAcc0, URedsAcc0}) -> + CountsAcc = CountsAcc0 + get_count(Red), + URedsAcc = lists:append(URedsAcc0, [get_user_reds(Red)]), + {CountsAcc, URedsAcc} + end, + {Counts, UReds} = lists:foldl(ExtractFun, {0, []}, Reds), + {ok, Result} = couch_query_servers:rereduce(Lang, FunSrcs, UReds), + {Counts, Result} + end. + + +maybe_define_less_fun(#mrview{options = Options}) -> + case couch_util:get_value(<<"collation">>, Options) of + <<"raw">> -> undefined; + _ -> fun couch_ejson_compare:less_json_ids/2 + end. + + +count_reduce(reduce, KVs) -> + CountFun = fun + ({_, {dups, Vals}}, Acc) -> Acc + length(Vals); + (_, Acc) -> Acc + 1 + end, + Count = lists:foldl(CountFun, 0, KVs), + {Count, []}; +count_reduce(rereduce, Reds) -> + CountFun = fun(Red, Acc) -> + Acc + get_count(Red) + end, + Count = lists:foldl(CountFun, 0, Reds), + {Count, []}. + + +make_user_reds_reduce_fun(Lang, ReduceFuns, NthRed) -> + LPad = lists:duplicate(NthRed - 1, []), + RPad = lists:duplicate(length(ReduceFuns) - NthRed, []), + {_, FunSrc} = lists:nth(NthRed, ReduceFuns), + fun + (reduce, KVs0) -> + KVs = detuple_kvs(expand_dups(KVs0, []), []), + {ok, Result} = couch_query_servers:reduce(Lang, [FunSrc], KVs), + {0, LPad ++ Result ++ RPad}; + (rereduce, Reds) -> + ExtractFun = fun(Reds0) -> + [lists:nth(NthRed, get_user_reds(Reds0))] + end, + UReds = lists:map(ExtractFun, Reds), + {ok, Result} = couch_query_servers:rereduce(Lang, [FunSrc], UReds), + {0, LPad ++ Result ++ RPad} + end. + + +get_btree_state(nil) -> + nil; +get_btree_state(#btree{} = Btree) -> + couch_btree:get_state(Btree). + + extract_view_reduce({red, {N, _Lang, #mrview{reduce_funs=Reds}}, _Ref}) -> {_Name, FunSrc} = lists:nth(N, Reds), FunSrc.