summaryrefslogtreecommitdiff
path: root/src/couch_mrview/src/couch_mrview.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/couch_mrview/src/couch_mrview.erl')
-rw-r--r--src/couch_mrview/src/couch_mrview.erl748
1 files changed, 0 insertions, 748 deletions
diff --git a/src/couch_mrview/src/couch_mrview.erl b/src/couch_mrview/src/couch_mrview.erl
deleted file mode 100644
index d8640c903..000000000
--- a/src/couch_mrview/src/couch_mrview.erl
+++ /dev/null
@@ -1,748 +0,0 @@
-% 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.
-
--module(couch_mrview).
-
--export([validate/2]).
--export([query_all_docs/2, query_all_docs/4]).
--export([query_view/3, query_view/4, query_view/6, get_view_index_pid/4]).
--export([get_info/2]).
--export([trigger_update/2, trigger_update/3]).
--export([get_view_info/3]).
--export([refresh/2]).
--export([compact/2, compact/3, cancel_compaction/2]).
--export([cleanup/1]).
-
--include_lib("couch/include/couch_db.hrl").
--include_lib("couch_mrview/include/couch_mrview.hrl").
-
--record(mracc, {
- db,
- meta_sent = false,
- total_rows,
- offset,
- limit,
- skip,
- group_level,
- doc_info,
- callback,
- user_acc,
- last_go = ok,
- reduce_fun,
- finalizer,
- update_seq,
- args
-}).
-
-validate_ddoc_fields(DDoc) ->
- MapFuncType = map_function_type(DDoc),
- lists:foreach(
- fun(Path) ->
- validate_ddoc_fields(DDoc, Path)
- end,
- [
- [{<<"filters">>, object}, {any, [object, string]}],
- [{<<"language">>, string}],
- [{<<"lists">>, object}, {any, [object, string]}],
- [{<<"options">>, object}],
- [{<<"options">>, object}, {<<"include_design">>, boolean}],
- [{<<"options">>, object}, {<<"local_seq">>, boolean}],
- [{<<"options">>, object}, {<<"partitioned">>, boolean}],
- [{<<"rewrites">>, [string, array]}],
- [{<<"shows">>, object}, {any, [object, string]}],
- [{<<"updates">>, object}, {any, [object, string]}],
- [{<<"validate_doc_update">>, string}],
- [{<<"views">>, object}, {<<"lib">>, object}],
- [{<<"views">>, object}, {any, object}, {<<"map">>, MapFuncType}],
- [{<<"views">>, object}, {any, object}, {<<"reduce">>, string}]
- ]
- ),
- require_map_function_for_views(DDoc),
- ok.
-
-require_map_function_for_views({Props}) ->
- case couch_util:get_value(<<"views">>, Props) of
- undefined ->
- ok;
- {Views} ->
- lists:foreach(
- fun
- ({<<"lib">>, _}) ->
- ok;
- ({Key, {Value}}) ->
- case couch_util:get_value(<<"map">>, Value) of
- undefined ->
- throw(
- {invalid_design_doc,
- <<"View `", Key/binary, "` must contain map function">>}
- );
- _ ->
- ok
- end
- end,
- Views
- ),
- ok
- end.
-
-validate_ddoc_fields(DDoc, Path) ->
- case validate_ddoc_fields(DDoc, Path, []) of
- ok ->
- ok;
- {error, {FailedPath0, Type0}} ->
- FailedPath = iolist_to_binary(join(FailedPath0, <<".">>)),
- Type = format_type(Type0),
- throw(
- {invalid_design_doc,
- <<"`", FailedPath/binary, "` field must have ", Type/binary, " type">>}
- )
- end.
-
-validate_ddoc_fields(undefined, _, _) ->
- ok;
-validate_ddoc_fields(_, [], _) ->
- ok;
-validate_ddoc_fields({KVS} = Props, [{any, Type} | Rest], Acc) ->
- lists:foldl(
- fun
- ({Key, _}, ok) ->
- validate_ddoc_fields(Props, [{Key, Type} | Rest], Acc);
- ({_, _}, {error, _} = Error) ->
- Error
- end,
- ok,
- KVS
- );
-validate_ddoc_fields({KVS} = Props, [{Key, Type} | Rest], Acc) ->
- case validate_ddoc_field(Props, {Key, Type}) of
- ok ->
- validate_ddoc_fields(
- couch_util:get_value(Key, KVS),
- Rest,
- [Key | Acc]
- );
- error ->
- {error, {[Key | Acc], Type}};
- {error, Key1} ->
- {error, {[Key1 | Acc], Type}}
- end.
-
-validate_ddoc_field(undefined, Type) when is_atom(Type) ->
- ok;
-validate_ddoc_field(_, any) ->
- ok;
-validate_ddoc_field(Value, Types) when is_list(Types) ->
- lists:foldl(
- fun
- (_, ok) -> ok;
- (Type, _) -> validate_ddoc_field(Value, Type)
- end,
- error,
- Types
- );
-validate_ddoc_field(Value, string) when is_binary(Value) ->
- ok;
-validate_ddoc_field(Value, array) when is_list(Value) ->
- ok;
-validate_ddoc_field({Value}, object) when is_list(Value) ->
- ok;
-validate_ddoc_field(Value, boolean) when is_boolean(Value) ->
- ok;
-validate_ddoc_field({Props}, {any, Type}) ->
- validate_ddoc_field1(Props, Type);
-validate_ddoc_field({Props}, {Key, Type}) ->
- validate_ddoc_field(couch_util:get_value(Key, Props), Type);
-validate_ddoc_field(_, _) ->
- error.
-
-validate_ddoc_field1([], _) ->
- ok;
-validate_ddoc_field1([{Key, Value} | Rest], Type) ->
- case validate_ddoc_field(Value, Type) of
- ok ->
- validate_ddoc_field1(Rest, Type);
- error ->
- {error, Key}
- end.
-
-map_function_type({Props}) ->
- case couch_util:get_value(<<"language">>, Props) of
- <<"query">> -> object;
- _ -> string
- end.
-
-format_type(Type) when is_atom(Type) ->
- ?l2b(atom_to_list(Type));
-format_type(Types) when is_list(Types) ->
- iolist_to_binary(join(lists:map(fun atom_to_list/1, Types), <<" or ">>)).
-
-join(L, Sep) ->
- join(L, Sep, []).
-join([H | []], _, Acc) ->
- [H | Acc];
-join([H | T], Sep, Acc) ->
- join(T, Sep, [Sep, H | Acc]).
-
-validate(Db, DDoc) ->
- ok = validate_ddoc_fields(DDoc#doc.body),
- GetName = fun
- (#mrview{map_names = [Name | _]}) -> Name;
- (#mrview{reduce_funs = [{Name, _} | _]}) -> Name;
- (_) -> null
- end,
- ValidateView = fun(Proc, #mrview{def = MapSrc, reduce_funs = Reds} = View) ->
- couch_query_servers:try_compile(Proc, map, GetName(View), MapSrc),
- lists:foreach(
- fun
- ({_RedName, <<"_sum", _/binary>>}) ->
- ok;
- ({_RedName, <<"_count", _/binary>>}) ->
- ok;
- ({_RedName, <<"_stats", _/binary>>}) ->
- ok;
- ({_RedName, <<"_approx_count_distinct", _/binary>>}) ->
- ok;
- ({_RedName, <<"_", _/binary>> = Bad}) ->
- Msg = ["`", Bad, "` is not a supported reduce function."],
- throw({invalid_design_doc, Msg});
- ({RedName, RedSrc}) ->
- couch_query_servers:try_compile(Proc, reduce, RedName, RedSrc)
- end,
- Reds
- )
- end,
- {ok, #mrst{
- language = Lang,
- views = Views,
- partitioned = Partitioned
- }} = couch_mrview_util:ddoc_to_mrst(couch_db:name(Db), DDoc),
-
- case {couch_db:is_partitioned(Db), Partitioned} of
- {false, true} ->
- throw(
- {invalid_design_doc, <<
- "partitioned option cannot be true in a "
- "non-partitioned database."
- >>}
- );
- {_, _} ->
- ok
- end,
-
- try Views =/= [] andalso couch_query_servers:get_os_process(Lang) of
- false ->
- ok;
- Proc ->
- try
- lists:foreach(fun(V) -> ValidateView(Proc, V) end, Views)
- after
- couch_query_servers:ret_os_process(Proc)
- end
- catch
- {unknown_query_language, _Lang} ->
- %% Allow users to save ddocs written in unknown languages
- ok
- end.
-
-query_all_docs(Db, Args) ->
- query_all_docs(Db, Args, fun default_cb/2, []).
-
-query_all_docs(Db, Args, Callback, Acc) when is_list(Args) ->
- query_all_docs(Db, to_mrargs(Args), Callback, Acc);
-query_all_docs(Db, Args0, Callback, Acc) ->
- Sig = couch_util:with_db(Db, fun(WDb) ->
- {ok, Info} = couch_db:get_db_info(WDb),
- couch_index_util:hexsig(couch_hash:md5_hash(term_to_binary(Info)))
- end),
- Args1 = Args0#mrargs{view_type = map},
- Args2 = couch_mrview_util:validate_all_docs_args(Db, Args1),
- {ok, Acc1} =
- case Args2#mrargs.preflight_fun of
- PFFun when is_function(PFFun, 2) -> PFFun(Sig, Acc);
- _ -> {ok, Acc}
- end,
- all_docs_fold(Db, Args2, Callback, Acc1).
-
-query_view(Db, DDoc, VName) ->
- query_view(Db, DDoc, VName, #mrargs{}).
-
-query_view(Db, DDoc, VName, Args) when is_list(Args) ->
- query_view(Db, DDoc, VName, to_mrargs(Args), fun default_cb/2, []);
-query_view(Db, DDoc, VName, Args) ->
- query_view(Db, DDoc, VName, Args, fun default_cb/2, []).
-
-query_view(Db, DDoc, VName, Args, Callback, Acc) when is_list(Args) ->
- query_view(Db, DDoc, VName, to_mrargs(Args), Callback, Acc);
-query_view(Db, DDoc, VName, Args0, Callback, Acc0) ->
- case couch_mrview_util:get_view(Db, DDoc, VName, Args0) of
- {ok, VInfo, Sig, Args} ->
- {ok, Acc1} =
- case Args#mrargs.preflight_fun of
- PFFun when is_function(PFFun, 2) -> PFFun(Sig, Acc0);
- _ -> {ok, Acc0}
- end,
- query_view(Db, VInfo, Args, Callback, Acc1);
- ddoc_updated ->
- Callback(ok, ddoc_updated)
- end.
-
-get_view_index_pid(Db, DDoc, ViewName, Args0) ->
- couch_mrview_util:get_view_index_pid(Db, DDoc, ViewName, Args0).
-
-query_view(Db, {Type, View, Ref}, Args, Callback, Acc) ->
- try
- case Type of
- map -> map_fold(Db, View, Args, Callback, Acc);
- red -> red_fold(Db, View, Args, Callback, Acc)
- end
- after
- erlang:demonitor(Ref, [flush])
- end.
-
-get_info(Db, DDoc) ->
- {ok, Pid} = couch_index_server:get_index(couch_mrview_index, Db, DDoc),
- couch_index:get_info(Pid).
-
-trigger_update(Db, DDoc) ->
- trigger_update(Db, DDoc, couch_db:get_update_seq(Db)).
-
-trigger_update(Db, DDoc, UpdateSeq) ->
- {ok, Pid} = couch_index_server:get_index(couch_mrview_index, Db, DDoc),
- couch_index:trigger_update(Pid, UpdateSeq).
-
-%% get informations on a view
-get_view_info(Db, DDoc, VName) ->
- {ok, {_, View, _}, _, _Args} = couch_mrview_util:get_view(
- Db,
- DDoc,
- VName,
- #mrargs{}
- ),
-
- %% get the total number of rows
- {ok, TotalRows} = couch_mrview_util:get_row_count(View),
-
- {ok, [
- {update_seq, View#mrview.update_seq},
- {purge_seq, View#mrview.purge_seq},
- {total_rows, TotalRows}
- ]}.
-
-%% @doc refresh a view index
-refresh(DbName, DDoc) when is_binary(DbName) ->
- UpdateSeq = couch_util:with_db(DbName, fun(WDb) ->
- couch_db:get_update_seq(WDb)
- end),
-
- case couch_index_server:get_index(couch_mrview_index, DbName, DDoc) of
- {ok, Pid} ->
- case catch couch_index:get_state(Pid, UpdateSeq) of
- {ok, _} -> ok;
- Error -> {error, Error}
- end;
- Error ->
- {error, Error}
- end;
-refresh(Db, DDoc) ->
- refresh(couch_db:name(Db), DDoc).
-
-compact(Db, DDoc) ->
- compact(Db, DDoc, []).
-
-compact(Db, DDoc, Opts) ->
- {ok, Pid} = couch_index_server:get_index(couch_mrview_index, Db, DDoc),
- couch_index:compact(Pid, Opts).
-
-cancel_compaction(Db, DDoc) ->
- {ok, IPid} = couch_index_server:get_index(couch_mrview_index, Db, DDoc),
- {ok, CPid} = couch_index:get_compactor_pid(IPid),
- ok = couch_index_compactor:cancel(CPid),
-
- % Cleanup the compaction file if it exists
- {ok, #mrst{sig = Sig, db_name = DbName}} = couch_index:get_state(IPid, 0),
- couch_mrview_util:delete_compaction_file(DbName, Sig),
- ok.
-
-cleanup(Db) ->
- couch_mrview_cleanup:run(Db).
-
-all_docs_fold(Db, #mrargs{keys = undefined} = Args, Callback, UAcc) ->
- ReduceFun = get_reduce_fun(Args),
- Total = get_total_rows(Db, Args),
- UpdateSeq = get_update_seq(Db, Args),
- Acc = #mracc{
- db = Db,
- total_rows = Total,
- limit = Args#mrargs.limit,
- skip = Args#mrargs.skip,
- callback = Callback,
- user_acc = UAcc,
- reduce_fun = ReduceFun,
- update_seq = UpdateSeq,
- args = Args
- },
- [Opts1] = couch_mrview_util:all_docs_key_opts(Args),
- % TODO: This is a terrible hack for now. We'll probably have
- % to rewrite _all_docs to not be part of mrview and not expect
- % a btree. For now non-btree's will just have to pass 0 or
- % some fake reductions to get an offset.
- Opts2 = [include_reductions | Opts1],
- FunName =
- case couch_util:get_value(namespace, Args#mrargs.extra) of
- <<"_design">> -> fold_design_docs;
- <<"_local">> -> fold_local_docs;
- _ -> fold_docs
- end,
- {ok, Offset, FinalAcc} = couch_db:FunName(Db, fun map_fold/3, Acc, Opts2),
- finish_fold(FinalAcc, [{total, Total}, {offset, Offset}]);
-all_docs_fold(Db, #mrargs{direction = Dir, keys = Keys0} = Args, Callback, UAcc) ->
- ReduceFun = get_reduce_fun(Args),
- Total = get_total_rows(Db, Args),
- UpdateSeq = get_update_seq(Db, Args),
- Acc = #mracc{
- db = Db,
- total_rows = Total,
- limit = Args#mrargs.limit,
- skip = Args#mrargs.skip,
- callback = Callback,
- user_acc = UAcc,
- reduce_fun = ReduceFun,
- update_seq = UpdateSeq,
- args = Args
- },
- % Backwards compatibility hack. The old _all_docs iterates keys
- % in reverse if descending=true was passed. Here we'll just
- % reverse the list instead.
- Keys =
- if
- Dir =:= fwd -> Keys0;
- true -> lists:reverse(Keys0)
- end,
-
- FoldFun = fun(Key, Acc0) ->
- DocInfo = (catch couch_db:get_doc_info(Db, Key)),
- {Doc, Acc1} =
- case DocInfo of
- {ok, #doc_info{id = Id, revs = [RevInfo | _RestRevs]} = DI} ->
- Rev = couch_doc:rev_to_str(RevInfo#rev_info.rev),
- Props =
- [{rev, Rev}] ++
- case RevInfo#rev_info.deleted of
- true -> [{deleted, true}];
- false -> []
- end,
- {{{Id, Id}, {Props}}, Acc0#mracc{doc_info = DI}};
- not_found ->
- {{{Key, error}, not_found}, Acc0}
- end,
- {_, Acc2} = map_fold(Doc, {[], [{0, 0, 0}]}, Acc1),
- Acc2
- end,
- FinalAcc = lists:foldl(FoldFun, Acc, Keys),
- finish_fold(FinalAcc, [{total, Total}]).
-
-map_fold(Db, View, Args, Callback, UAcc) ->
- {ok, Total} = couch_mrview_util:get_row_count(View),
- Acc = #mracc{
- db = Db,
- total_rows = Total,
- limit = Args#mrargs.limit,
- skip = Args#mrargs.skip,
- callback = Callback,
- user_acc = UAcc,
- reduce_fun = fun couch_mrview_util:reduce_to_count/1,
- update_seq = View#mrview.update_seq,
- args = Args
- },
- OptList = couch_mrview_util:key_opts(Args),
- {Reds, Acc2} = lists:foldl(
- fun(Opts, {_, Acc0}) ->
- {ok, R, A} = couch_mrview_util:fold(View, fun map_fold/3, Acc0, Opts),
- {R, A}
- end,
- {nil, Acc},
- OptList
- ),
- Offset = couch_mrview_util:reduce_to_count(Reds),
- finish_fold(Acc2, [{total, Total}, {offset, Offset}]).
-
-map_fold(#full_doc_info{} = FullDocInfo, OffsetReds, Acc) ->
- % matches for _all_docs and translates #full_doc_info{} -> KV pair
- case couch_doc:to_doc_info(FullDocInfo) of
- #doc_info{id = Id, revs = [#rev_info{deleted = false, rev = Rev} | _]} = DI ->
- Value = {[{rev, couch_doc:rev_to_str(Rev)}]},
- NS = couch_util:get_value(namespace, Acc#mracc.args#mrargs.extra),
- case Id of
- <<?DESIGN_DOC_PREFIX, _/binary>> when NS =:= <<"_non_design">> -> {ok, Acc};
- _ -> map_fold({{Id, Id}, Value}, OffsetReds, Acc#mracc{doc_info = DI})
- end;
- #doc_info{revs = [#rev_info{deleted = true} | _]} ->
- {ok, Acc}
- end;
-map_fold(_KV, _Offset, #mracc{skip = N} = Acc) when N > 0 ->
- {ok, Acc#mracc{skip = N - 1, last_go = ok}};
-map_fold(KV, OffsetReds, #mracc{offset = undefined} = Acc) ->
- #mracc{
- total_rows = Total,
- callback = Callback,
- user_acc = UAcc0,
- reduce_fun = Reduce,
- update_seq = UpdateSeq,
- args = Args
- } = Acc,
- Offset = Reduce(OffsetReds),
- Meta = make_meta(Args, UpdateSeq, [{total, Total}, {offset, Offset}]),
- {Go, UAcc1} = Callback(Meta, UAcc0),
- Acc1 = Acc#mracc{meta_sent = true, offset = Offset, user_acc = UAcc1, last_go = Go},
- case Go of
- ok -> map_fold(KV, OffsetReds, Acc1);
- stop -> {stop, Acc1}
- end;
-map_fold(_KV, _Offset, #mracc{limit = 0} = Acc) ->
- {stop, Acc};
-map_fold({{Key, Id}, Val}, _Offset, Acc) ->
- #mracc{
- db = Db,
- limit = Limit,
- doc_info = DI,
- callback = Callback,
- user_acc = UAcc0,
- args = Args
- } = Acc,
- Doc =
- case DI of
- #doc_info{} -> couch_mrview_util:maybe_load_doc(Db, DI, Args);
- _ -> couch_mrview_util:maybe_load_doc(Db, Id, Val, Args)
- end,
- Row = [{id, Id}, {key, Key}, {value, Val}] ++ Doc,
- {Go, UAcc1} = Callback({row, Row}, UAcc0),
- {Go, Acc#mracc{
- limit = Limit - 1,
- doc_info = undefined,
- user_acc = UAcc1,
- last_go = Go
- }};
-map_fold(#doc{id = <<"_local/", _/binary>>} = Doc, _Offset, #mracc{} = Acc) ->
- #mracc{
- limit = Limit,
- callback = Callback,
- user_acc = UAcc0,
- args = Args
- } = Acc,
- #doc{
- id = DocId,
- revs = {Pos, [RevId | _]}
- } = Doc,
- Rev = {Pos, RevId},
- Row =
- [
- {id, DocId},
- {key, DocId},
- {value, {[{rev, couch_doc:rev_to_str(Rev)}]}}
- ] ++
- if
- not Args#mrargs.include_docs -> [];
- true -> [{doc, couch_doc:to_json_obj(Doc, Args#mrargs.doc_options)}]
- end,
- {Go, UAcc1} = Callback({row, Row}, UAcc0),
- {Go, Acc#mracc{
- limit = Limit - 1,
- reduce_fun = undefined,
- doc_info = undefined,
- user_acc = UAcc1,
- last_go = Go
- }}.
-
-red_fold(Db, {NthRed, _Lang, View} = RedView, Args, Callback, UAcc) ->
- Finalizer =
- case couch_util:get_value(finalizer, Args#mrargs.extra) of
- undefined ->
- {_, FunSrc} = lists:nth(NthRed, View#mrview.reduce_funs),
- FunSrc;
- CustomFun ->
- CustomFun
- end,
- Acc = #mracc{
- db = Db,
- total_rows = null,
- limit = Args#mrargs.limit,
- skip = Args#mrargs.skip,
- group_level = Args#mrargs.group_level,
- callback = Callback,
- user_acc = UAcc,
- update_seq = View#mrview.update_seq,
- finalizer = Finalizer,
- args = Args
- },
- Grouping = {key_group_level, Args#mrargs.group_level},
- OptList = couch_mrview_util:key_opts(Args, [Grouping]),
- Acc2 = lists:foldl(
- fun(Opts, Acc0) ->
- {ok, Acc1} =
- couch_mrview_util:fold_reduce(RedView, fun red_fold/3, Acc0, Opts),
- Acc1
- end,
- Acc,
- OptList
- ),
- finish_fold(Acc2, []).
-
-red_fold({p, _Partition, Key}, Red, Acc) ->
- red_fold(Key, Red, Acc);
-red_fold(_Key, _Red, #mracc{skip = N} = Acc) when N > 0 ->
- {ok, Acc#mracc{skip = N - 1, last_go = ok}};
-red_fold(Key, Red, #mracc{meta_sent = false} = Acc) ->
- #mracc{
- args = Args,
- callback = Callback,
- user_acc = UAcc0,
- update_seq = UpdateSeq
- } = Acc,
- Meta = make_meta(Args, UpdateSeq, []),
- {Go, UAcc1} = Callback(Meta, UAcc0),
- Acc1 = Acc#mracc{user_acc = UAcc1, meta_sent = true, last_go = Go},
- case Go of
- ok -> red_fold(Key, Red, Acc1);
- _ -> {Go, Acc1}
- end;
-red_fold(_Key, _Red, #mracc{limit = 0} = Acc) ->
- {stop, Acc};
-red_fold(_Key, Red, #mracc{group_level = 0} = Acc) ->
- #mracc{
- finalizer = Finalizer,
- limit = Limit,
- callback = Callback,
- user_acc = UAcc0
- } = Acc,
- Row = [{key, null}, {value, maybe_finalize(Red, Finalizer)}],
- {Go, UAcc1} = Callback({row, Row}, UAcc0),
- {Go, Acc#mracc{user_acc = UAcc1, limit = Limit - 1, last_go = Go}};
-red_fold(Key, Red, #mracc{group_level = exact} = Acc) ->
- #mracc{
- finalizer = Finalizer,
- limit = Limit,
- callback = Callback,
- user_acc = UAcc0
- } = Acc,
- Row = [{key, Key}, {value, maybe_finalize(Red, Finalizer)}],
- {Go, UAcc1} = Callback({row, Row}, UAcc0),
- {Go, Acc#mracc{user_acc = UAcc1, limit = Limit - 1, last_go = Go}};
-red_fold(K, Red, #mracc{group_level = I} = Acc) when I > 0, is_list(K) ->
- #mracc{
- finalizer = Finalizer,
- limit = Limit,
- callback = Callback,
- user_acc = UAcc0
- } = Acc,
- Row = [{key, lists:sublist(K, I)}, {value, maybe_finalize(Red, Finalizer)}],
- {Go, UAcc1} = Callback({row, Row}, UAcc0),
- {Go, Acc#mracc{user_acc = UAcc1, limit = Limit - 1, last_go = Go}};
-red_fold(K, Red, #mracc{group_level = I} = Acc) when I > 0 ->
- #mracc{
- finalizer = Finalizer,
- limit = Limit,
- callback = Callback,
- user_acc = UAcc0
- } = Acc,
- Row = [{key, K}, {value, maybe_finalize(Red, Finalizer)}],
- {Go, UAcc1} = Callback({row, Row}, UAcc0),
- {Go, Acc#mracc{user_acc = UAcc1, limit = Limit - 1, last_go = Go}}.
-
-maybe_finalize(Red, null) ->
- Red;
-maybe_finalize(Red, RedSrc) ->
- {ok, Finalized} = couch_query_servers:finalize(RedSrc, Red),
- Finalized.
-
-finish_fold(#mracc{last_go = ok, update_seq = UpdateSeq} = Acc, ExtraMeta) ->
- #mracc{callback = Callback, user_acc = UAcc, args = Args} = Acc,
- % Possible send meta info
- Meta = make_meta(Args, UpdateSeq, ExtraMeta),
- {Go, UAcc1} =
- case Acc#mracc.meta_sent of
- false -> Callback(Meta, UAcc);
- _ -> {ok, Acc#mracc.user_acc}
- end,
- % Notify callback that the fold is complete.
- {_, UAcc2} =
- case Go of
- ok -> Callback(complete, UAcc1);
- _ -> {ok, UAcc1}
- end,
- {ok, UAcc2};
-finish_fold(#mracc{user_acc = UAcc}, _ExtraMeta) ->
- {ok, UAcc}.
-
-make_meta(Args, UpdateSeq, Base) ->
- case Args#mrargs.update_seq of
- true -> {meta, Base ++ [{update_seq, UpdateSeq}]};
- _ -> {meta, Base}
- end.
-
-get_reduce_fun(#mrargs{extra = Extra}) ->
- case couch_util:get_value(namespace, Extra) of
- <<"_local">> ->
- fun(_) -> null end;
- _ ->
- fun couch_mrview_util:all_docs_reduce_to_count/1
- end.
-
-get_total_rows(Db, #mrargs{extra = Extra}) ->
- case couch_util:get_value(namespace, Extra) of
- <<"_local">> ->
- null;
- <<"_design">> ->
- {ok, N} = couch_db:get_design_doc_count(Db),
- N;
- <<"_non_design">> ->
- {ok, N} = couch_db:get_design_doc_count(Db),
- {ok, Info} = couch_db:get_db_info(Db),
- couch_util:get_value(doc_count, Info) - N;
- _ ->
- {ok, Info} = couch_db:get_db_info(Db),
- couch_util:get_value(doc_count, Info)
- end.
-
-get_update_seq(Db, #mrargs{extra = Extra}) ->
- case couch_util:get_value(namespace, Extra) of
- <<"_local">> ->
- null;
- _ ->
- couch_db:get_update_seq(Db)
- end.
-
-default_cb(complete, Acc) ->
- {ok, lists:reverse(Acc)};
-default_cb({final, Info}, []) ->
- {ok, [Info]};
-default_cb({final, _}, Acc) ->
- {ok, Acc};
-default_cb(ok, ddoc_updated) ->
- {ok, ddoc_updated};
-default_cb(Row, Acc) ->
- {ok, [Row | Acc]}.
-
-to_mrargs(KeyList) ->
- lists:foldl(
- fun({Key, Value}, Acc) ->
- Index = lookup_index(couch_util:to_existing_atom(Key)),
- setelement(Index, Acc, Value)
- end,
- #mrargs{},
- KeyList
- ).
-
-lookup_index(Key) ->
- Index = lists:zip(
- record_info(fields, mrargs), lists:seq(2, record_info(size, mrargs))
- ),
- couch_util:get_value(Key, Index).