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}};