couchdb-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jch...@apache.org
Subject svn commit: r931439 - in /couchdb/trunk: THANKS share/Makefile.am share/www/script/couch_tests.js share/www/script/test/view_update_seq.js src/couchdb/couch_httpd_db.erl src/couchdb/couch_httpd_show.erl src/couchdb/couch_httpd_view.erl
Date Wed, 07 Apr 2010 05:48:36 GMT
Author: jchris
Date: Wed Apr  7 05:48:36 2010
New Revision: 931439

URL: http://svn.apache.org/viewvc?rev=931439&view=rev
Log:
include update_seq in view responses, patch via Joscha Feth, (mostly) closes COUCHDB-650

Added:
    couchdb/trunk/share/www/script/test/view_update_seq.js
Modified:
    couchdb/trunk/THANKS
    couchdb/trunk/share/Makefile.am
    couchdb/trunk/share/www/script/couch_tests.js
    couchdb/trunk/src/couchdb/couch_httpd_db.erl
    couchdb/trunk/src/couchdb/couch_httpd_show.erl
    couchdb/trunk/src/couchdb/couch_httpd_view.erl

Modified: couchdb/trunk/THANKS
URL: http://svn.apache.org/viewvc/couchdb/trunk/THANKS?rev=931439&r1=931438&r2=931439&view=diff
==============================================================================
--- couchdb/trunk/THANKS (original)
+++ couchdb/trunk/THANKS Wed Apr  7 05:48:36 2010
@@ -51,5 +51,6 @@ suggesting improvements or submitting ch
  * 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.

Modified: couchdb/trunk/share/Makefile.am
URL: http://svn.apache.org/viewvc/couchdb/trunk/share/Makefile.am?rev=931439&r1=931438&r2=931439&view=diff
==============================================================================
--- couchdb/trunk/share/Makefile.am (original)
+++ couchdb/trunk/share/Makefile.am Wed Apr  7 05:48:36 2010
@@ -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 \

Modified: couchdb/trunk/share/www/script/couch_tests.js
URL: http://svn.apache.org/viewvc/couchdb/trunk/share/www/script/couch_tests.js?rev=931439&r1=931438&r2=931439&view=diff
==============================================================================
--- couchdb/trunk/share/www/script/couch_tests.js [utf-8] (original)
+++ couchdb/trunk/share/www/script/couch_tests.js [utf-8] Wed Apr  7 05:48:36 2010
@@ -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
 

Added: couchdb/trunk/share/www/script/test/view_update_seq.js
URL: http://svn.apache.org/viewvc/couchdb/trunk/share/www/script/test/view_update_seq.js?rev=931439&view=auto
==============================================================================
--- couchdb/trunk/share/www/script/test/view_update_seq.js (added)
+++ couchdb/trunk/share/www/script/test/view_update_seq.js Wed Apr  7 05:48:36 2010
@@ -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);
+
+};

Modified: couchdb/trunk/src/couchdb/couch_httpd_db.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_httpd_db.erl?rev=931439&r1=931438&r2=931439&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_httpd_db.erl (original)
+++ couchdb/trunk/src/couchdb/couch_httpd_db.erl Wed Apr  7 05:48:36 2010
@@ -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
                 }),

Modified: couchdb/trunk/src/couchdb/couch_httpd_show.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_httpd_show.erl?rev=931439&r1=931438&r2=931439&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_httpd_show.erl (original)
+++ couchdb/trunk/src/couchdb/couch_httpd_show.erl Wed Apr  7 05:48:36 2010
@@ -190,21 +190,21 @@ handle_view_list(Req, Db, DDoc, LName, {
     {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, Vi
             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, Vi
                                 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, Vi
     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,
 
 
 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.
 

Modified: couchdb/trunk/src/couchdb/couch_httpd_view.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_httpd_view.erl?rev=931439&r1=931438&r2=931439&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_httpd_view.erl (original)
+++ couchdb/trunk/src/couchdb/couch_httpd_view.erl Wed Apr  7 05:48:36 2010
@@ -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, Qu
     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, Qu
             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, Qu
                     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,
     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,
     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,
                 {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
 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,
             % 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,
         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, _
 
     (_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, _
     (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, _
 
     (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_hel
 }=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_h
     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, DocI
     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,
             {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,
     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)



Mime
View raw message