This is an automated email from the ASF dual-hosted git repository. davisp pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/couchdb.git
commit ef6b60b760a79542df31df9c7ad64d737b860c92 Author: Paul J. Davis <paul.joseph.da...@gmail.com> AuthorDate: Fri Sep 18 11:05:00 2020 -0500 Upgrade legacy views --- src/couch_views/include/couch_views.hrl | 4 + src/couch_views/src/couch_views_fdb.erl | 180 ++++++++-- src/couch_views/src/couch_views_indexer.erl | 8 +- src/couch_views/test/couch_views_upgrade_test.erl | 400 ++++++++++++++++++++++ 4 files changed, 551 insertions(+), 41 deletions(-) diff --git a/src/couch_views/include/couch_views.hrl b/src/couch_views/include/couch_views.hrl index 3882191..92b8f46 100644 --- a/src/couch_views/include/couch_views.hrl +++ b/src/couch_views/include/couch_views.hrl @@ -10,6 +10,9 @@ % License for the specific language governing permissions and limitations under % the License. +% Current implementation version +-define(CURRENT_VIEW_IMPL_VERSION, 1). + % Index info/data subspaces -define(VIEW_INFO, 0). -define(VIEW_DATA, 1). @@ -21,6 +24,7 @@ -define(VIEW_KV_SIZE, 2). -define(VIEW_BUILD_STATUS, 3). -define(VIEW_CREATION_VS, 4). +-define(VIEW_IMPL_VERSION, 5). % Data keys -define(VIEW_ID_RANGE, 0). diff --git a/src/couch_views/src/couch_views_fdb.erl b/src/couch_views/src/couch_views_fdb.erl index f238a8f..28a60b8 100644 --- a/src/couch_views/src/couch_views_fdb.erl +++ b/src/couch_views/src/couch_views_fdb.erl @@ -13,6 +13,8 @@ -module(couch_views_fdb). -export([ + get_view_state/2, + new_interactive_index/3, new_creation_vs/3, get_creation_vs/2, @@ -40,52 +42,89 @@ -include_lib("fabric/include/fabric2.hrl"). -new_interactive_index(Db, Mrst, VS) -> - couch_views_fdb:new_creation_vs(Db, Mrst, VS), - couch_views_fdb:set_build_status(Db, Mrst, ?INDEX_BUILDING). +get_view_state(Db, #mrst{} = Mrst) -> + get_view_state(Db, Mrst#mrst.sig); + +get_view_state(Db, Sig) when is_binary(Sig) -> + #{ + tx := Tx + } = Db, + + VersionF = erlfdb:get(Tx, version_key(Db, Sig)), + ViewSeqF = erlfdb:get(Tx, seq_key(Db, Sig)), + ViewVSF = erlfdb:get(Tx, creation_vs_key(Db, Sig)), + BuildStatusF = erlfdb:get(Tx, build_status_key(Db, Sig)), + + Version = case erlfdb:wait(VersionF) of + not_found -> not_found; + VsnVal -> element(1, erlfdb_tuple:unpack(VsnVal)) + end, + + ViewSeq = case erlfdb:wait(ViewSeqF) of + not_found -> <<>>; + SeqVal -> SeqVal + end, + + ViewVS = case erlfdb:wait(ViewVSF) of + not_found -> not_found; + VSVal -> element(1, erlfdb_tuple:unpack(VSVal)) + end, + + State = #{ + version => Version, + view_seq => ViewSeq, + view_vs => ViewVS, + build_status => erlfdb:wait(BuildStatusF) + }, + + maybe_upgrade_view(Db, Sig, State). + + +new_interactive_index(Db, #mrst{} = Mrst, VS) -> + new_interactive_index(Db, Mrst#mrst.sig, VS); + +new_interactive_index(Db, Sig, VS) -> + set_version(Db, Sig), + new_creation_vs(Db, Sig, VS), + set_build_status(Db, Sig, ?INDEX_BUILDING). %Interactive View Creation Versionstamp %(<db>, ?DB_VIEWS, ?VIEW_INFO, ?VIEW_CREATION_VS, Sig) = VS new_creation_vs(TxDb, #mrst{} = Mrst, VS) -> + new_creation_vs(TxDb, Mrst#mrst.sig, VS); + +new_creation_vs(TxDb, Sig, VS) -> #{ tx := Tx } = TxDb, - Key = creation_vs_key(TxDb, Mrst#mrst.sig), + Key = creation_vs_key(TxDb, Sig), Value = erlfdb_tuple:pack_vs({VS}), ok = erlfdb:set_versionstamped_value(Tx, Key, Value). -get_creation_vs(TxDb, #mrst{} = Mrst) -> - get_creation_vs(TxDb, Mrst#mrst.sig); - -get_creation_vs(TxDb, Sig) -> +get_creation_vs(TxDb, MrstOrSig) -> #{ - tx := Tx - } = TxDb, - Key = creation_vs_key(TxDb, Sig), - case erlfdb:wait(erlfdb:get(Tx, Key)) of - not_found -> - not_found; - EK -> - {VS} = erlfdb_tuple:unpack(EK), - VS - end. + view_vs := ViewVS + } = get_view_state(TxDb, MrstOrSig), + ViewVS. %Interactive View Build Status %(<db>, ?DB_VIEWS, ?VIEW_INFO, ?VIEW_BUILD_STATUS, Sig) = INDEX_BUILDING | INDEX_READY -get_build_status(TxDb, #mrst{sig = Sig}) -> +get_build_status(TxDb, MrstOrSig) -> #{ - tx := Tx - } = TxDb, - Key = build_status_key(TxDb, Sig), - erlfdb:wait(erlfdb:get(Tx, Key)). + build_status := BuildStatus + } = get_view_state(TxDb, MrstOrSig), + BuildStatus. -set_build_status(TxDb, #mrst{sig = Sig}, State) -> +set_build_status(TxDb, #mrst{} = Mrst, State) -> + set_build_status(TxDb, Mrst#mrst.sig, State); + +set_build_status(TxDb, Sig, State) -> #{ tx := Tx } = TxDb, @@ -98,24 +137,18 @@ set_build_status(TxDb, #mrst{sig = Sig}, State) -> % (<db>, ?DB_VIEWS, Sig, ?VIEW_UPDATE_SEQ) = Sequence -get_update_seq(TxDb, #mrst{sig = Sig}) -> +get_update_seq(TxDb, MrstOrSig) -> #{ - tx := Tx, - db_prefix := DbPrefix - } = TxDb, - - case erlfdb:wait(erlfdb:get(Tx, seq_key(DbPrefix, Sig))) of - not_found -> <<>>; - UpdateSeq -> UpdateSeq - end. + view_seq := ViewSeq + } = get_view_state(TxDb, MrstOrSig), + ViewSeq. set_update_seq(TxDb, Sig, Seq) -> #{ - tx := Tx, - db_prefix := DbPrefix + tx := Tx } = TxDb, - ok = erlfdb:set(Tx, seq_key(DbPrefix, Sig), Seq). + ok = erlfdb:set(Tx, seq_key(TxDb, Sig), Seq). list_signatures(Db) -> @@ -139,7 +172,10 @@ clear_index(Db, Signature) -> % Get view size to remove from global counter SizeTuple = {?DB_VIEWS, ?VIEW_INFO, ?VIEW_KV_SIZE, Signature}, SizeKey = erlfdb_tuple:pack(SizeTuple, DbPrefix), - ViewSize = ?bin2uint(erlfdb:wait(erlfdb:get(Tx, SizeKey))), + ViewSize = case erlfdb:wait(erlfdb:get(Tx, SizeKey)) of + not_found -> 0; + SizeVal -> ?bin2uint(SizeVal) + end, % Clear index info keys Keys = [ @@ -207,7 +243,75 @@ update_kv_size(TxDb, Sig, OldSize, NewSize) -> erlfdb:add(Tx, DbKey, NewSize - OldSize). -seq_key(DbPrefix, Sig) -> +maybe_upgrade_view(_Db, _Sig, #{version := ?CURRENT_VIEW_IMPL_VERSION} = St) -> + St; +maybe_upgrade_view(Db, Sig, #{version := not_found, view_seq := <<>>} = St) -> + % If we haven't started building the view yet + % then we don't change view_vs and build_status + % as they're still correct. + set_version(Db, Sig), + St#{ + version => ?CURRENT_VIEW_IMPL_VERSION, + view_seq => <<>> + }; +maybe_upgrade_view(Db, Sig, #{version := not_found} = St) -> + clear_index(Db, Sig), + set_version(Db, Sig), + {ViewVS, BuildStatus} = reset_interactive_index(Db, Sig, St), + #{ + version => ?CURRENT_VIEW_IMPL_VERSION, + view_seq => <<>>, + view_vs => ViewVS, + build_status => BuildStatus + }. + + +set_version(Db, Sig) -> + #{ + tx := Tx + } = Db, + Key = version_key(Db, Sig), + Val = erlfdb_tuple:pack({?CURRENT_VIEW_IMPL_VERSION}), + erlfdb:set(Tx, Key, Val). + + +reset_interactive_index(_Db, _Sig, #{view_vs := not_found}) -> + % Not an interactive index + {not_found, not_found}; +reset_interactive_index(Db, Sig, _St) -> + % We have to reset the creation versionstamp + % to the current update seq of the database + % or else we'll not have indexed any documents + % inserted since the creation of the interactive + % index. + #{ + tx := Tx + } = Db, + + DbSeq = fabric2_db:get_update_seq(Db), + VS = fabric2_fdb:seq_to_vs(DbSeq), + Key = creation_vs_key(Db, Sig), + Val = erlfdb_tuple:pack({VS}), + ok = erlfdb:set(Tx, Key, Val), + + set_build_status(Db, Sig, ?INDEX_BUILDING), + + {VS, ?INDEX_BUILDING}. + + + +version_key(Db, Sig) -> + #{ + db_prefix := DbPrefix + } = Db, + Key = {?DB_VIEWS, ?VIEW_INFO, ?VIEW_IMPL_VERSION, Sig}, + erlfdb_tuple:pack(Key, DbPrefix). + + +seq_key(Db, Sig) -> + #{ + db_prefix := DbPrefix + } = Db, Key = {?DB_VIEWS, ?VIEW_INFO, ?VIEW_UPDATE_SEQ, Sig}, erlfdb_tuple:pack(Key, DbPrefix). diff --git a/src/couch_views/src/couch_views_indexer.erl b/src/couch_views/src/couch_views_indexer.erl index da23939..2735f66 100644 --- a/src/couch_views/src/couch_views_indexer.erl +++ b/src/couch_views/src/couch_views_indexer.erl @@ -200,8 +200,8 @@ do_update(Db, Mrst0, State0) -> tx := Tx } = TxDb, - Mrst1 = couch_views_trees:open(TxDb, Mrst0), State1 = get_update_start_state(TxDb, Mrst0, State0), + Mrst1 = couch_views_trees:open(TxDb, Mrst0), {ok, State2} = fold_changes(State1), @@ -259,8 +259,10 @@ maybe_set_build_status(TxDb, Mrst1, _ViewVS, State) -> % In the first iteration of update we need % to populate our db and view sequences get_update_start_state(TxDb, Mrst, #{db_seq := undefined} = State) -> - ViewVS = couch_views_fdb:get_creation_vs(TxDb, Mrst), - ViewSeq = couch_views_fdb:get_update_seq(TxDb, Mrst), + #{ + view_vs := ViewVS, + view_seq := ViewSeq + } = couch_views_fdb:get_view_state(TxDb, Mrst), State#{ tx_db := TxDb, diff --git a/src/couch_views/test/couch_views_upgrade_test.erl b/src/couch_views/test/couch_views_upgrade_test.erl new file mode 100644 index 0000000..556a762 --- /dev/null +++ b/src/couch_views/test/couch_views_upgrade_test.erl @@ -0,0 +1,400 @@ +% 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_views_upgrade_test). + +-include_lib("eunit/include/eunit.hrl"). +-include_lib("couch/include/couch_db.hrl"). +-include_lib("couch/include/couch_eunit.hrl"). +-include_lib("couch_mrview/include/couch_mrview.hrl"). +-include_lib("couch_views/include/couch_views.hrl"). +-include_lib("fabric/include/fabric2.hrl"). +-include_lib("fabric/test/fabric2_test.hrl"). + + +-define(MAP_FUN1, <<"map_fun1">>). +-define(MAP_FUN2, <<"map_fun2">>). + + +upgrade_test_() -> + { + "Test view upgrades", + { + setup, + fun setup/0, + fun cleanup/1, + { + foreach, + fun foreach_setup/0, + fun foreach_teardown/1, + [ + ?TDEF_FE(empty_state), + ?TDEF_FE(indexed_state), + ?TDEF_FE(upgrade_non_interactive), + ?TDEF_FE(upgrade_unbuilt_interactive), + ?TDEF_FE(upgrade_partially_built_interactive), + ?TDEF_FE(upgrade_built_interactive) + ] + } + } + }. + + +setup() -> + Ctx = test_util:start_couch([ + fabric, + couch_jobs, + couch_js, + couch_views + ]), + Ctx. + + +cleanup(Ctx) -> + test_util:stop_couch(Ctx). + + +foreach_setup() -> + {ok, Db} = fabric2_db:create(?tempdb(), [{user_ctx, ?ADMIN_USER}]), + Db. + + +foreach_teardown(Db) -> + meck:unload(), + config:delete("couch_views", "change_limit"), + ok = fabric2_db:delete(fabric2_db:name(Db), []). + + +empty_state(Db) -> + DDoc = create_ddoc(), + {ok, Mrst} = couch_views_util:ddoc_to_mrst(fabric2_db:name(Db), DDoc), + State = fabric2_fdb:transactional(Db, fun(TxDb) -> + couch_views_fdb:get_view_state(TxDb, Mrst) + end), + + Expect = #{ + version => ?CURRENT_VIEW_IMPL_VERSION, + view_seq => <<>>, + view_vs => not_found, + build_status => not_found + }, + ?assertEqual(Expect, State), + assert_fdb_state(Db, Mrst, Expect). + + +indexed_state(Db) -> + DDoc = create_ddoc(), + Doc1 = doc(0), + + {ok, _} = fabric2_db:update_doc(Db, DDoc, []), + {ok, _} = fabric2_db:update_doc(Db, Doc1, []), + + {ok, Out} = run_query(Db, DDoc, ?MAP_FUN1), + ?assertEqual([row(<<"0">>, 0, 0)], Out), + + assert_fdb_state(Db, DDoc, #{ + version => ?CURRENT_VIEW_IMPL_VERSION, + view_seq => fabric2_db:get_update_seq(Db), + view_vs => not_found, + build_status => not_found + }). + + +upgrade_non_interactive(Db) -> + DDoc = create_ddoc(), + Doc1 = doc(0), + + {ok, _} = fabric2_db:update_docs(Db, [DDoc, Doc1], []), + DbSeq = fabric2_db:get_update_seq(Db), + + init_fdb_state(Db, DDoc, #{view_seq => DbSeq}), + + {ok, Out} = run_query(Db, DDoc, ?MAP_FUN1), + ?assertEqual([row(<<"0">>, 0, 0)], Out), + + assert_fdb_state(Db, DDoc, #{ + version => ?CURRENT_VIEW_IMPL_VERSION, + view_seq => DbSeq, + view_vs => not_found, + build_status => not_found + }). + + +upgrade_unbuilt_interactive(Db) -> + DDoc = create_ddoc(), + {ok, Mrst} = couch_views_util:ddoc_to_mrst(fabric2_db:name(Db), DDoc), + Doc1 = doc(0), + + {ok, _} = fabric2_db:update_docs(Db, [DDoc, Doc1], []), + DbSeq = fabric2_db:get_update_seq(Db), + + init_fdb_state(Db, DDoc, #{ + view_vs => fabric2_fdb:seq_to_vs(DbSeq), + build_status => ?INDEX_BUILDING + }), + + % Trigger an upgrade + fabric2_fdb:transactional(Db, fun(TxDb) -> + couch_views_fdb:get_view_state(TxDb, Mrst) + end), + + assert_fdb_state(Db, DDoc, #{ + version => ?CURRENT_VIEW_IMPL_VERSION, + view_seq => <<>>, + view_vs => fabric2_fdb:seq_to_vs(DbSeq), + build_status => ?INDEX_BUILDING + }), + + % Build the view + {ok, Out} = run_query(Db, DDoc, ?MAP_FUN1), + ?assertEqual([row(<<"0">>, 0, 0)], Out), + + assert_fdb_state(Db, DDoc, #{ + version => ?CURRENT_VIEW_IMPL_VERSION, + view_seq => DbSeq, + view_vs => fabric2_fdb:seq_to_vs(DbSeq), + build_status => ?INDEX_READY + }). + + +upgrade_partially_built_interactive(Db) -> + DDoc = create_ddoc(), + {ok, Mrst} = couch_views_util:ddoc_to_mrst(fabric2_db:name(Db), DDoc), + {ok, _} = fabric2_db:update_doc(Db, DDoc, []), + + MidSeq = fabric2_db:get_update_seq(Db), + + Doc1 = doc(0), + {ok, _} = fabric2_db:update_doc(Db, Doc1, []), + + DbSeq = fabric2_db:get_update_seq(Db), + + init_fdb_state(Db, DDoc, #{ + view_seq => MidSeq, + view_vs => fabric2_fdb:seq_to_vs(DbSeq), + build_status => ?INDEX_BUILDING + }), + + % Trigger an upgrade + fabric2_fdb:transactional(Db, fun(TxDb) -> + couch_views_fdb:get_view_state(TxDb, Mrst) + end), + + assert_fdb_state(Db, DDoc, #{ + version => ?CURRENT_VIEW_IMPL_VERSION, + view_seq => <<>>, + view_vs => fabric2_fdb:seq_to_vs(DbSeq), + build_status => ?INDEX_BUILDING + }), + + % Build the view + {ok, Out} = run_query(Db, DDoc, ?MAP_FUN1), + ?assertEqual([row(<<"0">>, 0, 0)], Out), + + assert_fdb_state(Db, DDoc, #{ + version => ?CURRENT_VIEW_IMPL_VERSION, + view_seq => DbSeq, + view_vs => fabric2_fdb:seq_to_vs(DbSeq), + build_status => ?INDEX_READY + }). + + +upgrade_built_interactive(Db) -> + DDoc = create_ddoc(), + Doc1 = doc(0), + + {ok, Mrst} = couch_views_util:ddoc_to_mrst(fabric2_db:name(Db), DDoc), + {ok, _} = fabric2_db:update_doc(Db, DDoc, []), + {ok, _} = fabric2_db:update_doc(Db, Doc1, []), + + DbSeq = fabric2_db:get_update_seq(Db), + + init_fdb_state(Db, DDoc, #{ + view_seq => DbSeq, + view_vs => fabric2_fdb:seq_to_vs(DbSeq), + build_status => ?INDEX_READY + }), + + % Trigger an upgrade + fabric2_fdb:transactional(Db, fun(TxDb) -> + couch_views_fdb:get_view_state(TxDb, Mrst) + end), + + assert_fdb_state(Db, DDoc, #{ + version => ?CURRENT_VIEW_IMPL_VERSION, + view_seq => <<>>, + view_vs => fabric2_fdb:seq_to_vs(DbSeq), + build_status => ?INDEX_BUILDING + }), + + % Build the view + {ok, Out} = run_query(Db, DDoc, ?MAP_FUN1), + ?assertEqual([row(<<"0">>, 0, 0)], Out), + + assert_fdb_state(Db, DDoc, #{ + version => ?CURRENT_VIEW_IMPL_VERSION, + view_seq => DbSeq, + view_vs => fabric2_fdb:seq_to_vs(DbSeq), + build_status => ?INDEX_READY + }). + + +init_fdb_state(Db, #doc{} = DDoc, Values) -> + {ok, Mrst} = couch_views_util:ddoc_to_mrst(fabric2_db:name(Db), DDoc), + init_fdb_state(Db, Mrst, Values); +init_fdb_state(Db, #mrst{sig = Sig}, Values) -> + init_fdb_state(Db, Sig, Values); +init_fdb_state(Db, Sig, Values) -> + VersionRow = case maps:get(version, Values, undefined) of + undefined -> []; + Version -> [{pack(Db, key(version, Sig)), pack({Version})}] + end, + + SeqRow = case maps:get(view_seq, Values, undefined) of + undefined -> []; + Seq -> [{pack(Db, key(seq, Sig)), Seq}] + end, + + VSRow = case maps:get(view_vs, Values, undefined) of + undefined -> []; + VS -> [{pack(Db, key(vs, Sig)), pack({VS})}] + end, + + BSRow = case maps:get(build_status, Values, undefined) of + undefined -> []; + BS -> [{pack(Db, key(bs, Sig)), BS}] + end, + + Rows = VersionRow ++ SeqRow ++ VSRow ++ BSRow, + + fabric2_fdb:transactional(Db, fun(TxDb) -> + #{ + tx := Tx + } = TxDb, + lists:foreach(fun({K, V}) -> + erlfdb:set(Tx, K, V) + end, Rows) + end). + + +assert_fdb_state(Db, #doc{} = DDoc, Expect) -> + {ok, Mrst} = couch_views_util:ddoc_to_mrst(fabric2_db:name(Db), DDoc), + assert_fdb_state(Db, Mrst, Expect); +assert_fdb_state(Db, #mrst{sig = Sig}, Expect) -> + assert_fdb_state(Db, Sig, Expect); +assert_fdb_state(Db, Sig, Expect) -> + #{ + version := Version, + view_seq := ViewSeq, + view_vs := ViewVS, + build_status := BuildStatus + } = Expect, + + VersionRow = case Version of + not_found -> []; + _ -> [{pack(Db, key(version, Sig)), pack({Version})}] + end, + + SeqRow = case ViewSeq of + <<>> -> []; + _ -> [{pack(Db, key(seq, Sig)), ViewSeq}] + end, + + VSRow = case ViewVS of + not_found -> []; + _ -> [{pack(Db, key(vs, Sig)), pack({ViewVS})}] + end, + + BSRow = case BuildStatus of + not_found -> []; + _ -> [{pack(Db, key(bs, Sig)), BuildStatus}] + end, + + ExpectRows = lists:sort(VersionRow ++ SeqRow ++ VSRow ++ BSRow), + + RawExistingRows = fabric2_fdb:transactional(Db, fun(TxDb) -> + #{ + tx := Tx, + db_prefix := DbPrefix + } = TxDb, + RangePrefix = erlfdb_tuple:pack({?DB_VIEWS, ?VIEW_INFO}, DbPrefix), + erlfdb:wait(erlfdb:get_range_startswith(Tx, RangePrefix)) + end), + + % Ignore the KV size key in the view info rows + KVSizeKey = pack(Db, key(kv_size, Sig)), + ExistingRows = lists:keydelete(KVSizeKey, 1, RawExistingRows), + + ?assertEqual(ExpectRows, ExistingRows). + + +key(version, Sig) -> {?DB_VIEWS, ?VIEW_INFO, ?VIEW_IMPL_VERSION, Sig}; +key(seq, Sig) -> {?DB_VIEWS, ?VIEW_INFO, ?VIEW_UPDATE_SEQ, Sig}; +key(kv_size, Sig) -> {?DB_VIEWS, ?VIEW_INFO, ?VIEW_KV_SIZE, Sig}; +key(vs, Sig) -> {?DB_VIEWS, ?VIEW_INFO, ?VIEW_CREATION_VS, Sig}; +key(bs, Sig) -> {?DB_VIEWS, ?VIEW_INFO, ?VIEW_BUILD_STATUS, Sig}. + + +pack(Db, Key) -> + #{ + db_prefix := DbPrefix + } = Db, + erlfdb_tuple:pack(Key, DbPrefix). + + +pack(Value) -> + erlfdb_tuple:pack(Value). + + +row(Id, Key, Value) -> + {row, [ + {id, Id}, + {key, Key}, + {value, Value} + ]}. + + +fold_fun({meta, _Meta}, Acc) -> + {ok, Acc}; +fold_fun({row, _} = Row, Acc) -> + {ok, [Row | Acc]}; +fold_fun(complete, Acc) -> + {ok, lists:reverse(Acc)}. + + +create_ddoc() -> + couch_doc:from_json_obj({[ + {<<"_id">>, <<"_design/bar">>}, + {<<"views">>, {[ + {?MAP_FUN1, {[ + {<<"map">>, <<"function(doc) {emit(doc.val, doc.val);}">>} + ]}}, + {?MAP_FUN2, {[ + {<<"map">>, <<"function(doc) {}">>} + ]}} + ]}} + ]}). + + +doc(Id) -> + doc(Id, Id). + + +doc(Id, Val) -> + couch_doc:from_json_obj({[ + {<<"_id">>, list_to_binary(integer_to_list(Id))}, + {<<"val">>, Val} + ]}). + + +run_query(#{} = Db, DDoc, <<_/binary>> = View) -> + couch_views:query(Db, DDoc, View, fun fold_fun/2, [], #mrargs{}). \ No newline at end of file