This is an automated email from the ASF dual-hosted git repository.

iilyak pushed a commit to branch prototype/fdb-layer
in repository https://gitbox.apache.org/repos/asf/couchdb.git


The following commit(s) were added to refs/heads/prototype/fdb-layer by this 
push:
     new a081412  Add support for previous bookmark
     new 46ee05e  Merge pull request #2904 from 
cloudant/support-previous-bookmark
a081412 is described below

commit a0814126bdae274f4260edd87e8b23736370885e
Author: ILYA Khlopotov <[email protected]>
AuthorDate: Fri May 22 07:12:32 2020 -0700

    Add support for previous bookmark
---
 src/chttpd/test/exunit/pagination_test.exs | 47 ++++++++++++++++++++++++++++++
 src/couch_views/src/couch_views_http.erl   | 43 +++++++++++++++++++++++----
 2 files changed, 85 insertions(+), 5 deletions(-)

diff --git a/src/chttpd/test/exunit/pagination_test.exs 
b/src/chttpd/test/exunit/pagination_test.exs
index 140a5dc..7fd9623 100644
--- a/src/chttpd/test/exunit/pagination_test.exs
+++ b/src/chttpd/test/exunit/pagination_test.exs
@@ -872,6 +872,18 @@ defmodule Couch.Test.Pagination do
           assert Map.has_key?(resp.body, "next")
         end
 
+        test "first page should not return 'previous' bookmark", ctx do
+          resp =
+            Couch.Session.get(
+              ctx.session,
+              "/#{ctx.db_name}/_design/#{ctx.ddoc_id}/_view/#{ctx.view_name}",
+              query: %{page_size: ctx.page_size, descending: ctx.descending}
+            )
+
+          assert resp.status_code == 200, "got error #{inspect(resp.body)}"
+          assert not Map.has_key?(resp.body, "previous")
+        end
+
         test "total_rows matches the length of rows array", ctx do
           resp =
             Couch.Session.get(
@@ -919,6 +931,41 @@ defmodule Couch.Test.Pagination do
           assert body["total_rows"] == length(body["rows"])
           assert body["total_rows"] <= ctx.page_size
         end
+
+        test "can use 'previous' bookmark", ctx do
+          resp =
+            Couch.Session.get(
+              ctx.session,
+              "/#{ctx.db_name}/_design/#{ctx.ddoc_id}/_view/#{ctx.view_name}",
+              query: %{page_size: ctx.page_size, descending: ctx.descending}
+            )
+
+          assert resp.status_code == 200, "got error #{inspect(resp.body)}"
+          next_bookmark = resp.body["next"]
+
+          first_page_ids = Enum.map(resp.body["rows"], fn row -> row["id"] end)
+
+          resp =
+            Couch.Session.get(
+              ctx.session,
+              "/#{ctx.db_name}/_design/#{ctx.ddoc_id}/_view/#{ctx.view_name}",
+              query: %{bookmark: next_bookmark}
+            )
+
+          assert resp.status_code == 200, "got error #{inspect(resp.body)}"
+          assert Map.has_key?(resp.body, "previous")
+
+          resp =
+            Couch.Session.get(
+              ctx.session,
+              "/#{ctx.db_name}/_design/#{ctx.ddoc_id}/_view/#{ctx.view_name}",
+              query: %{bookmark: resp.body["previous"]}
+            )
+
+          assert resp.status_code == 200, "got error #{inspect(resp.body)}"
+          ids = Enum.map(resp.body["rows"], fn row -> row["id"] end)
+          assert first_page_ids == ids
+        end
       end
     end
   end
diff --git a/src/couch_views/src/couch_views_http.erl 
b/src/couch_views/src/couch_views_http.erl
index b9bc2b3..8e12b24 100644
--- a/src/couch_views/src/couch_views_http.erl
+++ b/src/couch_views/src/couch_views_http.erl
@@ -126,8 +126,9 @@ do_paginated(PageSize, QueriesArgs, KeyFun, Fun) when 
is_list(QueriesArgs) ->
             true ->
                 {OriginalLimit, Args} = set_limit(Args0#mrargs{page_size = 
Limit}),
                 {Meta, Items} = Fun(Args),
-                Result = maybe_add_bookmark(
+                Result0 = maybe_add_next_bookmark(
                     OriginalLimit, PageSize, Args, Meta, Items, KeyFun),
+                Result = maybe_add_previous_bookmark(Args, Result0, KeyFun),
                 #{total_rows := Total} = Result,
                 {Limit - Total, [Result | Acc]};
             false ->
@@ -143,8 +144,11 @@ do_paginated(PageSize, QueriesArgs, KeyFun, Fun) when 
is_list(QueriesArgs) ->
     lists:reverse(Results).
 
 
-maybe_add_bookmark(OriginalLimit, PageSize, Args0, Response, Items, KeyFun) ->
-    #mrargs{page_size = RequestedLimit} = Args0,
+maybe_add_next_bookmark(OriginalLimit, PageSize, Args0, Response, Items, 
KeyFun) ->
+    #mrargs{
+        page_size = RequestedLimit,
+        extra = Extra
+    } = Args0,
     case check_completion(OriginalLimit, RequestedLimit, Items) of
         {Rows, nil} ->
             maps:merge(Response, #{
@@ -152,12 +156,17 @@ maybe_add_bookmark(OriginalLimit, PageSize, Args0, 
Response, Items, KeyFun) ->
                 total_rows => length(Rows)
             });
         {Rows, Next} ->
+            FirstKey = first_key(KeyFun, Rows),
             NextKey = KeyFun(Next),
             if is_binary(NextKey) -> ok; true ->
                 throw("Provided KeyFun should return binary")
             end,
-            Args = Args0#mrargs{page_size = PageSize},
-            Bookmark = bookmark_encode(Args#mrargs{start_key=NextKey}),
+            Args = Args0#mrargs{
+                page_size = PageSize,
+                start_key = NextKey,
+                extra = lists:keystore(fk, 1, Extra, {fk, FirstKey})
+            },
+            Bookmark = bookmark_encode(Args),
             maps:merge(Response, #{
                 rows => Rows,
                 next => Bookmark,
@@ -166,6 +175,30 @@ maybe_add_bookmark(OriginalLimit, PageSize, Args0, 
Response, Items, KeyFun) ->
     end.
 
 
+maybe_add_previous_bookmark(#mrargs{extra = Extra} = Args, #{rows := Rows} = 
Result, KeyFun) ->
+    StartKey = couch_util:get_value(fk, Extra),
+    case first_key(KeyFun, Rows) of
+        undefined ->
+            Result;
+        EndKey ->
+            Bookmark = bookmark_encode(
+                Args#mrargs{
+                    start_key = StartKey,
+                    end_key = EndKey,
+                    inclusive_end = false
+                }
+            ),
+            maps:put(previous, Bookmark, Result)
+    end.
+
+
+first_key(_KeyFun, []) ->
+    undefined;
+
+first_key(KeyFun, [First | _]) ->
+    KeyFun(First).
+
+
 set_limit(#mrargs{page_size = PageSize, limit = Limit} = Args)
         when is_integer(PageSize) andalso Limit > PageSize ->
     {Limit, Args#mrargs{limit = PageSize + 1}};

Reply via email to