diff options
| author | John Christopher Anderson <jchris@apache.org> | 2010-04-07 05:48:36 +0000 | 
|---|---|---|
| committer | John Christopher Anderson <jchris@apache.org> | 2010-04-07 05:48:36 +0000 | 
| commit | 2c10462d114d938b0fedb718da63ae34152ce08e (patch) | |
| tree | 8047f2678118e3fc52c91c5cb78c5fe1092b5437 | |
| parent | c43ae32b00ab64e5427b924867a4783ca0edee57 (diff) | |
include update_seq in view responses, patch via Joscha Feth, (mostly) closes COUCHDB-650
git-svn-id: https://svn.apache.org/repos/asf/couchdb/trunk@931439 13f79535-47bb-0310-9956-ffa450edef68
| -rw-r--r-- | THANKS | 1 | ||||
| -rw-r--r-- | share/Makefile.am | 1 | ||||
| -rw-r--r-- | share/www/script/couch_tests.js | 1 | ||||
| -rw-r--r-- | share/www/script/test/view_update_seq.js | 76 | ||||
| -rw-r--r-- | src/couchdb/couch_httpd_db.erl | 6 | ||||
| -rw-r--r-- | src/couchdb/couch_httpd_show.erl | 29 | ||||
| -rw-r--r-- | src/couchdb/couch_httpd_view.erl | 55 | 
7 files changed, 129 insertions, 40 deletions
| @@ -51,5 +51,6 @@ suggesting improvements or submitting changes. Some of these people are:   * Matt Lyon <matt@flowerpowered.com>   * mikeal <mikeal.rogers@gmail.com>   * Randall Leeds <randall.leeds@gmail.com> + * Joscha Feth <joscha@feth.com>  For a list of authors see the `AUTHORS` file. diff --git a/share/Makefile.am b/share/Makefile.am index a6911b12..ac4d6fb0 100644 --- a/share/Makefile.am +++ b/share/Makefile.am @@ -162,6 +162,7 @@ nobase_dist_localdata_DATA = \      www/script/test/view_multi_key_design.js \      www/script/test/view_multi_key_temp.js \      www/script/test/view_offsets.js \ +    www/script/test/view_update_seq.js \      www/script/test/view_pagination.js \      www/script/test/view_sandboxing.js \      www/script/test/view_xml.js \ diff --git a/share/www/script/couch_tests.js b/share/www/script/couch_tests.js index 943b851b..896f150b 100644 --- a/share/www/script/couch_tests.js +++ b/share/www/script/couch_tests.js @@ -91,6 +91,7 @@ loadTest("view_multi_key_temp.js");  loadTest("view_offsets.js");  loadTest("view_pagination.js");  loadTest("view_sandboxing.js"); +loadTest("view_update_seq.js");  loadTest("view_xml.js");  // keep sorted diff --git a/share/www/script/test/view_update_seq.js b/share/www/script/test/view_update_seq.js new file mode 100644 index 00000000..3935e153 --- /dev/null +++ b/share/www/script/test/view_update_seq.js @@ -0,0 +1,76 @@ +// 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. + +couchTests.view_update_seq = function(debug) { +  var db = new CouchDB("test_suite_db", {"X-Couch-Full-Commit":"false"}); +  db.deleteDb(); +  db.createDb(); +  if (debug) debugger; + +  T(db.info().update_seq == 0); + +  var designDoc = { +    _id:"_design/test", +    language: "javascript", +    views: { +      all_docs: { +        map: "function(doc) { emit(doc.integer, doc.string) }" +      }, +      summate: { +        map:"function (doc) {emit(doc.integer, doc.integer)};", +        reduce:"function (keys, values) { return sum(values); };" +      } +    } +  } +  T(db.save(designDoc).ok); + +  T(db.info().update_seq == 1); + +  var resp = db.allDocs({}); + +  T(resp.rows.length == 1); +  T(resp.update_seq == 1); + +  var docs = makeDocs(0, 100); +  db.bulkSave(docs); + +  resp = db.allDocs({limit: 1}); +  T(resp.rows.length == 1); +  T(resp.update_seq == 101); + +  resp = db.view('test/all_docs', {limit: 1}); +  T(resp.rows.length == 1); +  T(resp.update_seq == 101); + +  resp = db.view('test/summate', {}); +  T(resp.rows.length == 1); +  T(resp.update_seq == 101); + +  db.save({"id":"0"}); +  resp = db.view('test/all_docs', {limit: 1,stale: "ok"}); +  T(resp.rows.length == 1); +  T(resp.update_seq == 101); + +  resp = db.view('test/all_docs', {limit: 1}); +  T(resp.rows.length == 1); +  T(resp.update_seq == 102); + +  resp = db.view('test/all_docs',{},["0","1"]); +  T(resp.update_seq == 102); + +  resp = db.view('test/all_docs',{},["0","1"]); +  T(resp.update_seq == 102); + +  resp = db.view('test/summate',{group:true},["0","1"]); +  T(resp.update_seq == 102); + +}; diff --git a/src/couchdb/couch_httpd_db.erl b/src/couchdb/couch_httpd_db.erl index b473ff6c..463519a8 100644 --- a/src/couchdb/couch_httpd_db.erl +++ b/src/couchdb/couch_httpd_db.erl @@ -492,10 +492,10 @@ all_docs_view(Req, Db, Keys) ->          true -> EndDocId          end,          FoldAccInit = {Limit, SkipCount, undefined, []}, - +		UpdateSeq = couch_db:get_update_seq(Db),          case Keys of          nil -> -            FoldlFun = couch_httpd_view:make_view_fold_fun(Req, QueryArgs, CurrentEtag, Db, +            FoldlFun = couch_httpd_view:make_view_fold_fun(Req, QueryArgs, CurrentEtag, Db, UpdateSeq,                  TotalRowCount, #view_fold_helper_funs{                      reduce_count = fun couch_db:enum_docs_reduce_to_count/1                  }), @@ -512,7 +512,7 @@ all_docs_view(Req, Db, Keys) ->                      {if Inclusive -> end_key; true -> end_key_gt end, EndId}]),              couch_httpd_view:finish_view_fold(Req, TotalRowCount, LastOffset, FoldResult);          _ -> -            FoldlFun = couch_httpd_view:make_view_fold_fun(Req, QueryArgs, CurrentEtag, Db, +            FoldlFun = couch_httpd_view:make_view_fold_fun(Req, QueryArgs, CurrentEtag, Db, UpdateSeq,                  TotalRowCount, #view_fold_helper_funs{                      reduce_count = fun(Offset) -> Offset end                  }), diff --git a/src/couchdb/couch_httpd_show.erl b/src/couchdb/couch_httpd_show.erl index a472c97a..f40a0421 100644 --- a/src/couchdb/couch_httpd_show.erl +++ b/src/couchdb/couch_httpd_show.erl @@ -190,21 +190,21 @@ handle_view_list(Req, Db, DDoc, LName, {ViewDesignName, ViewName}, Keys) ->      {ViewType, View, Group, QueryArgs} = couch_httpd_view:load_view(Req, Db, {ViewDesignId, ViewName}, Keys),      Etag = list_etag(Req, Db, Group, {couch_httpd:doc_etag(DDoc), Keys}),          couch_httpd:etag_respond(Req, Etag, fun() -> -            output_list(ViewType, Req, Db, DDoc, LName, View, QueryArgs, Etag, Keys) +            output_list(ViewType, Req, Db, DDoc, LName, View, QueryArgs, Etag, Keys, Group)          end).      list_etag(#httpd{user_ctx=UserCtx}=Req, Db, Group, More) ->      Accept = couch_httpd:header_value(Req, "Accept"),      couch_httpd_view:view_group_etag(Group, Db, {More, Accept, UserCtx#user_ctx.roles}). -output_list(map, Req, Db, DDoc, LName, View, QueryArgs, Etag, Keys) -> -    output_map_list(Req, Db, DDoc, LName, View, QueryArgs, Etag, Keys); -output_list(reduce, Req, Db, DDoc, LName, View, QueryArgs, Etag, Keys) -> -    output_reduce_list(Req, Db, DDoc, LName, View, QueryArgs, Etag, Keys). +output_list(map, Req, Db, DDoc, LName, View, QueryArgs, Etag, Keys, Group) -> +    output_map_list(Req, Db, DDoc, LName, View, QueryArgs, Etag, Keys, Group); +output_list(reduce, Req, Db, DDoc, LName, View, QueryArgs, Etag, Keys, Group) -> +    output_reduce_list(Req, Db, DDoc, LName, View, QueryArgs, Etag, Keys, Group).  % next step:  % use with_ddoc_proc/2 to make this simpler -output_map_list(Req, Db, DDoc, LName, View, QueryArgs, Etag, Keys) -> +output_map_list(Req, Db, DDoc, LName, View, QueryArgs, Etag, Keys, Group) ->      #view_query_args{          limit = Limit,          skip = SkipCount @@ -220,11 +220,12 @@ output_map_list(Req, Db, DDoc, LName, View, QueryArgs, Etag, Keys) ->              reduce_count = fun couch_view:reduce_to_count/1,              start_response = StartListRespFun = make_map_start_resp_fun(QServer, Db, LName),              send_row = make_map_send_row_fun(QServer) -        },         +        }, +		CurrentSeq = Group#group.current_seq,          {ok, _, FoldResult} = case Keys of              nil -> -                FoldlFun = couch_httpd_view:make_view_fold_fun(Req, QueryArgs, Etag, Db, RowCount, ListFoldHelpers),         +                FoldlFun = couch_httpd_view:make_view_fold_fun(Req, QueryArgs, Etag, Db, CurrentSeq, RowCount, ListFoldHelpers),                      couch_view:fold(View, FoldlFun, FoldAccInit,                       couch_httpd_view:make_key_options(QueryArgs));              Keys -> @@ -234,7 +235,7 @@ output_map_list(Req, Db, DDoc, LName, View, QueryArgs, Etag, Keys) ->                                  start_key = Key,                                  end_key = Key                              }, -                        FoldlFun = couch_httpd_view:make_view_fold_fun(Req, QueryArgs2, Etag, Db, RowCount, ListFoldHelpers), +                        FoldlFun = couch_httpd_view:make_view_fold_fun(Req, QueryArgs2, Etag, Db, CurrentSeq, RowCount, ListFoldHelpers),                          couch_view:fold(View, FoldlFun, FoldAcc,                              couch_httpd_view:make_key_options(QueryArgs2))                      end, {ok, nil, FoldAccInit}, Keys) @@ -243,18 +244,20 @@ output_map_list(Req, Db, DDoc, LName, View, QueryArgs, Etag, Keys) ->      end). -output_reduce_list(Req, Db, DDoc, LName, View, QueryArgs, Etag, Keys) -> +output_reduce_list(Req, Db, DDoc, LName, View, QueryArgs, Etag, Keys, Group) ->      #view_query_args{          limit = Limit,          skip = SkipCount,          group_level = GroupLevel      } = QueryArgs, +	CurrentSeq = Group#group.current_seq, +      couch_query_servers:with_ddoc_proc(DDoc, fun(QServer) ->          StartListRespFun = make_reduce_start_resp_fun(QServer, Db, LName),          SendListRowFun = make_reduce_send_row_fun(QServer, Db),          {ok, GroupRowsFun, RespFun} = couch_httpd_view:make_reduce_fold_funs(Req, -            GroupLevel, QueryArgs, Etag, +            GroupLevel, QueryArgs, Etag, CurrentSeq,              #reduce_fold_helper_funs{                  start_response = StartListRespFun,                  send_row = SendListRowFun @@ -279,8 +282,8 @@ output_reduce_list(Req, Db, DDoc, LName, View, QueryArgs, Etag, Keys) ->  make_map_start_resp_fun(QueryServer, Db, LName) -> -    fun(Req, Etag, TotalRows, Offset, _Acc) -> -        Head = {[{<<"total_rows">>, TotalRows}, {<<"offset">>, Offset}]}, +    fun(Req, Etag, TotalRows, Offset, _Acc, UpdateSeq) -> +        Head = {[{<<"total_rows">>, TotalRows}, {<<"offset">>, Offset}, {<<"update_seq">>, UpdateSeq}]},          start_list_resp(QueryServer, LName, Req, Db, Head, Etag)      end. diff --git a/src/couchdb/couch_httpd_view.erl b/src/couchdb/couch_httpd_view.erl index 6419ca55..5be702ca 100644 --- a/src/couchdb/couch_httpd_view.erl +++ b/src/couchdb/couch_httpd_view.erl @@ -16,8 +16,8 @@  -export([handle_view_req/3,handle_temp_view_req/2]).  -export([get_stale_type/1, get_reduce_type/1, parse_view_params/3]). --export([make_view_fold_fun/6, finish_view_fold/4, view_row_obj/3]). --export([view_group_etag/2, view_group_etag/3, make_reduce_fold_funs/5]). +-export([make_view_fold_fun/7, finish_view_fold/4, view_row_obj/3]). +-export([view_group_etag/2, view_group_etag/3, make_reduce_fold_funs/6]).  -export([design_doc_view/5, parse_bool_param/1, doc_member/2]).  -export([make_key_options/1, load_view/4]). @@ -26,6 +26,8 @@      start_json_response/2, start_json_response/3, end_json_response/1,      send_chunked_error/2]). +-import(couch_db,[get_update_seq/1]). +  design_doc_view(Req, Db, DName, ViewName, Keys) ->      DesignId = <<"_design/", DName/binary>>,      Stale = get_stale_type(Req), @@ -111,7 +113,7 @@ output_map_view(Req, View, Group, Db, QueryArgs, nil) ->      CurrentEtag = view_group_etag(Group, Db),      couch_httpd:etag_respond(Req, CurrentEtag, fun() ->          {ok, RowCount} = couch_view:get_row_count(View), -        FoldlFun = make_view_fold_fun(Req, QueryArgs, CurrentEtag, Db, RowCount, #view_fold_helper_funs{reduce_count=fun couch_view:reduce_to_count/1}), +        FoldlFun = make_view_fold_fun(Req, QueryArgs, CurrentEtag, Db, Group#group.current_seq, RowCount, #view_fold_helper_funs{reduce_count=fun couch_view:reduce_to_count/1}),          FoldAccInit = {Limit, SkipCount, undefined, []},          {ok, LastReduce, FoldResult} = couch_view:fold(View,                   FoldlFun, FoldAccInit, make_key_options(QueryArgs)), @@ -132,7 +134,7 @@ output_map_view(Req, View, Group, Db, QueryArgs, Keys) ->              fun(Key, {_, FoldAcc}) ->                  FoldlFun = make_view_fold_fun(Req,                      QueryArgs#view_query_args{ -                    }, CurrentEtag, Db, RowCount, +                    }, CurrentEtag, Db, Group#group.current_seq, RowCount,                      #view_fold_helper_funs{                          reduce_count = fun couch_view:reduce_to_count/1                      }), @@ -140,7 +142,7 @@ output_map_view(Req, View, Group, Db, QueryArgs, Keys) ->                      make_key_options(QueryArgs#view_query_args{start_key=Key, end_key=Key})),                  {LastReduce, FoldResult}              end, {{[],[]}, FoldAccInit}, Keys), -        finish_view_fold(Req, RowCount, couch_view:reduce_to_count(LastReduce), FoldResult) +        finish_view_fold(Req, RowCount, couch_view:reduce_to_count(LastReduce), FoldResult, [{update_seq,Group#group.current_seq}])      end).  output_reduce_view(Req, Db, View, Group, QueryArgs, nil) -> @@ -151,7 +153,7 @@ output_reduce_view(Req, Db, View, Group, QueryArgs, nil) ->      } = QueryArgs,      CurrentEtag = view_group_etag(Group, Db),      couch_httpd:etag_respond(Req, CurrentEtag, fun() -> -        {ok, GroupRowsFun, RespFun} = make_reduce_fold_funs(Req, GroupLevel, QueryArgs, CurrentEtag, #reduce_fold_helper_funs{}), +        {ok, GroupRowsFun, RespFun} = make_reduce_fold_funs(Req, GroupLevel, QueryArgs, CurrentEtag, Group#group.current_seq, #reduce_fold_helper_funs{}),          FoldAccInit = {Limit, Skip, undefined, []},          {ok, {_, _, Resp, _}} = couch_view:fold_reduce(View,                  RespFun, FoldAccInit, [{key_group_fun, GroupRowsFun} | @@ -167,7 +169,7 @@ output_reduce_view(Req, Db, View, Group, QueryArgs, Keys) ->      } = QueryArgs,      CurrentEtag = view_group_etag(Group, Db, Keys),      couch_httpd:etag_respond(Req, CurrentEtag, fun() -> -        {ok, GroupRowsFun, RespFun} = make_reduce_fold_funs(Req, GroupLevel, QueryArgs, CurrentEtag, #reduce_fold_helper_funs{}), +        {ok, GroupRowsFun, RespFun} = make_reduce_fold_funs(Req, GroupLevel, QueryArgs, CurrentEtag, Group#group.current_seq, #reduce_fold_helper_funs{}),          {Resp, _RedAcc3} = lists:foldl(              fun(Key, {Resp, RedAcc}) ->                  % run the reduce once for each key in keys, with limit etc reapplied for each key @@ -180,7 +182,7 @@ output_reduce_view(Req, Db, View, Group, QueryArgs, Keys) ->                  {Resp2, RedAcc2}              end,          {undefined, []}, Keys), % Start with no comma -        finish_reduce_fold(Req, Resp) +        finish_reduce_fold(Req, Resp, [{update_seq,Group#group.current_seq}])      end).  reverse_key_default(?MIN_STR) -> ?MAX_STR; @@ -377,7 +379,7 @@ validate_view_query(include_docs, _Value, Args) ->  validate_view_query(extra, _Value, Args) ->      Args. -make_view_fold_fun(Req, QueryArgs, Etag, Db, TotalViewCount, HelperFuns) -> +make_view_fold_fun(Req, QueryArgs, Etag, Db, UpdateSeq, TotalViewCount, HelperFuns) ->      #view_fold_helper_funs{          start_response = StartRespFun,          send_row = SendRowFun, @@ -400,7 +402,7 @@ make_view_fold_fun(Req, QueryArgs, Etag, Db, TotalViewCount, HelperFuns) ->              % rendering the first row, first we start the response              Offset = ReduceCountFun(OffsetReds),              {ok, Resp2, RowFunAcc0} = StartRespFun(Req, Etag, -                TotalViewCount, Offset, RowFunAcc), +                TotalViewCount, Offset, RowFunAcc, UpdateSeq),              {Go, RowFunAcc2} = SendRowFun(Resp2, Db, {{Key, DocId}, Value},                  IncludeDocs, RowFunAcc0),              {Go, {AccLimit - 1, 0, Resp2, RowFunAcc2}}; @@ -412,7 +414,7 @@ make_view_fold_fun(Req, QueryArgs, Etag, Db, TotalViewCount, HelperFuns) ->          end      end. -make_reduce_fold_funs(Req, GroupLevel, _QueryArgs, Etag, HelperFuns) -> +make_reduce_fold_funs(Req, GroupLevel, _QueryArgs, Etag, UpdateSeq, HelperFuns) ->      #reduce_fold_helper_funs{          start_response = StartRespFun,          send_row = SendRowFun @@ -438,7 +440,7 @@ make_reduce_fold_funs(Req, GroupLevel, _QueryArgs, Etag, HelperFuns) ->      (_Key, Red, {AccLimit, 0, undefined, RowAcc0}) when GroupLevel == 0 ->          % we haven't started responding yet and group=false -        {ok, Resp2, RowAcc} = StartRespFun(Req, Etag, RowAcc0), +        {ok, Resp2, RowAcc} = StartRespFun(Req, Etag, RowAcc0,UpdateSeq),          {Go, RowAcc2} = SendRowFun(Resp2, {null, Red}, RowAcc),          {Go, {AccLimit - 1, 0, Resp2, RowAcc2}};      (_Key, Red, {AccLimit, 0, Resp, RowAcc}) when GroupLevel == 0 -> @@ -449,7 +451,7 @@ make_reduce_fold_funs(Req, GroupLevel, _QueryArgs, Etag, HelperFuns) ->      (Key, Red, {AccLimit, 0, undefined, RowAcc0})              when is_integer(GroupLevel), is_list(Key) ->          % group_level and we haven't responded yet -        {ok, Resp2, RowAcc} = StartRespFun(Req, Etag, RowAcc0), +        {ok, Resp2, RowAcc} = StartRespFun(Req, Etag, RowAcc0,UpdateSeq),          {Go, RowAcc2} = SendRowFun(Resp2, {lists:sublist(Key, GroupLevel), Red}, RowAcc),          {Go, {AccLimit - 1, 0, Resp2, RowAcc2}};      (Key, Red, {AccLimit, 0, Resp, RowAcc}) @@ -460,7 +462,7 @@ make_reduce_fold_funs(Req, GroupLevel, _QueryArgs, Etag, HelperFuns) ->      (Key, Red, {AccLimit, 0, undefined, RowAcc0}) ->          % group=true and we haven't responded yet -        {ok, Resp2, RowAcc} = StartRespFun(Req, Etag, RowAcc0), +        {ok, Resp2, RowAcc} = StartRespFun(Req, Etag, RowAcc0,UpdateSeq),          {Go, RowAcc2} = SendRowFun(Resp2, {Key, Red}, RowAcc),          {Go, {AccLimit - 1, 0, Resp2, RowAcc2}};      (Key, Red, {AccLimit, 0, Resp, RowAcc}) -> @@ -476,7 +478,7 @@ apply_default_helper_funs(#view_fold_helper_funs{  }=Helpers) ->      StartResp2 = case StartResp of -    undefined -> fun json_view_start_resp/5; +    undefined -> fun json_view_start_resp/6;      _ -> StartResp      end, @@ -496,7 +498,7 @@ apply_default_helper_funs(#reduce_fold_helper_funs{      send_row = SendRow  }=Helpers) ->      StartResp2 = case StartResp of -    undefined -> fun json_reduce_start_resp/3; +    undefined -> fun json_reduce_start_resp/4;      _ -> StartResp      end, @@ -538,10 +540,10 @@ make_end_key_option(              inclusive_end = false}) ->      [{end_key_gt, {EndKey,reverse_key_default(EndDocId)}}]. -json_view_start_resp(Req, Etag, TotalViewCount, Offset, _Acc) -> +json_view_start_resp(Req, Etag, TotalViewCount, Offset, _Acc, UpdateSeq) ->      {ok, Resp} = start_json_response(Req, 200, [{"Etag", Etag}]), -    BeginBody = io_lib:format("{\"total_rows\":~w,\"offset\":~w,\"rows\":[\r\n", -            [TotalViewCount, Offset]), +    BeginBody = io_lib:format("{\"total_rows\":~w,\"update_seq\":~w,\"offset\":~w,\"rows\":[\r\n", +            [TotalViewCount, UpdateSeq, Offset]),      {ok, Resp, BeginBody}.  send_json_view_row(Resp, Db, {{Key, DocId}, Value}, IncludeDocs, RowFront) -> @@ -549,9 +551,9 @@ send_json_view_row(Resp, Db, {{Key, DocId}, Value}, IncludeDocs, RowFront) ->      send_chunk(Resp, RowFront ++  ?JSON_ENCODE(JsonObj)),      {ok, ",\r\n"}. -json_reduce_start_resp(Req, Etag, _Acc0) -> +json_reduce_start_resp(Req, Etag, _Acc0, UpdateSeq) ->      {ok, Resp} = start_json_response(Req, 200, [{"Etag", Etag}]), -    {ok, Resp, "{\"rows\":[\r\n"}. +    {ok, Resp, io_lib:format("{\"update_seq\":~w,\"rows\":[\r\n",[UpdateSeq])}.  send_json_reduce_row(Resp, {Key, Value}, RowFront) ->      send_chunk(Resp, RowFront ++ ?JSON_ENCODE({[{key, Key}, {value, Value}]})), @@ -599,9 +601,11 @@ doc_member(Db, {DocId, Rev}) ->          _Else ->              [{doc, null}]      end. -      finish_view_fold(Req, TotalRows, Offset, FoldResult) -> +	finish_view_fold(Req, TotalRows, Offset, FoldResult, []). + +finish_view_fold(Req, TotalRows, Offset, FoldResult, Fields) ->      case FoldResult of      {_, _, undefined, _} ->          % nothing found in the view or keys, nothing has been returned @@ -610,7 +614,7 @@ finish_view_fold(Req, TotalRows, Offset, FoldResult) ->              {total_rows, TotalRows},              {offset, Offset},              {rows, []} -        ]}); +        ] ++ Fields});      {_, _, Resp, _} ->          % end the view          send_chunk(Resp, "\r\n]}"), @@ -618,11 +622,14 @@ finish_view_fold(Req, TotalRows, Offset, FoldResult) ->      end.  finish_reduce_fold(Req, Resp) -> +	finish_reduce_fold(Req, Resp, []). + +finish_reduce_fold(Req, Resp, Fields) ->      case Resp of      undefined ->          send_json(Req, 200, {[              {rows, []} -        ]}); +        ] ++ Fields});      Resp ->          send_chunk(Resp, "\r\n]}"),          end_json_response(Resp) | 
