diff options
author | iilyak <iilyak@users.noreply.github.com> | 2020-07-22 03:42:45 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-07-22 03:42:45 -0700 |
commit | 46ee05e215cad6419b642fca0bae85e36e27bd67 (patch) | |
tree | bb2ea5324330d8c0cb7b44b2c45ee82da3aad8bb | |
parent | 6fa3b1987a45a90a53fd9221de2e9dd7b82fdccc (diff) | |
parent | a0814126bdae274f4260edd87e8b23736370885e (diff) | |
download | couchdb-46ee05e215cad6419b642fca0bae85e36e27bd67.tar.gz |
Merge pull request #2904 from cloudant/support-previous-bookmark
Add support for previous bookmark
-rw-r--r-- | src/chttpd/test/exunit/pagination_test.exs | 47 | ||||
-rw-r--r-- | 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 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}}; |