summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorILYA Khlopotov <iilyak@apache.org>2020-05-22 07:12:32 -0700
committerILYA Khlopotov <iilyak@apache.org>2020-05-22 07:12:32 -0700
commita0814126bdae274f4260edd87e8b23736370885e (patch)
treec9e963b2fd543964ed215275bbe32bc3a2f2f21c
parent5bade6fde84c46c472ab64967563cb07a239f026 (diff)
downloadcouchdb-a0814126bdae274f4260edd87e8b23736370885e.tar.gz
Add support for previous bookmark
-rw-r--r--src/chttpd/test/exunit/pagination_test.exs47
-rw-r--r--src/couch_views/src/couch_views_http.erl43
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 140a5dc88..7fd962381 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 b9bc2b3c0..8e12b2476 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}};