summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNick Vatamaniuc <vatamane@gmail.com>2021-09-17 16:43:42 -0400
committerNick Vatamaniuc <nickva@users.noreply.github.com>2021-09-22 12:00:44 -0400
commit22189ea6ad3665639bd3223d9ef915fe2561e2d1 (patch)
treec04fab1b9fc36d82da403c89d94cfef4af015a53
parent287112888954d51fa8d69a3d6a4f8c05c1b3289c (diff)
downloadcouchdb-22189ea6ad3665639bd3223d9ef915fe2561e2d1.tar.gz
Properly sort descending=true view results when a key list is provided
Results should now be returned in descending {key, doc_id} order. The idea is to reverse the key list before sending it to the workers, so they will emit rows in reverse order. Also, we are using the same reversed list when building the KeyDict structure on the coordinator. That way the order of the sent rows and the expected coordinator sorting order will match. For testing, enhance an existing multi-key Elixir view test to test both ascending and descending cases and actually check that the rows are in the correct order each time.
-rw-r--r--src/fabric/src/fabric_view_map.erl18
-rw-r--r--test/elixir/test/view_multi_key_design_test.exs40
2 files changed, 48 insertions, 10 deletions
diff --git a/src/fabric/src/fabric_view_map.erl b/src/fabric/src/fabric_view_map.erl
index ff6aa8b69..8ab808314 100644
--- a/src/fabric/src/fabric_view_map.erl
+++ b/src/fabric/src/fabric_view_map.erl
@@ -24,8 +24,14 @@ go(DbName, Options, GroupId, View, Args, Callback, Acc, VInfo)
{ok, DDoc} = fabric:open_doc(DbName, <<"_design/", GroupId/binary>>, []),
go(DbName, Options, DDoc, View, Args, Callback, Acc, VInfo);
-go(Db, Options, DDoc, View, Args, Callback, Acc, VInfo) ->
+go(Db, Options, DDoc, View, Args0, Callback, Acc, VInfo) ->
DbName = fabric:dbname(Db),
+ Args = case Args0 of
+ #mrargs{keys = Keys, direction = rev} when is_list(Keys) ->
+ Args0#mrargs{keys = lists:reverse(Keys)};
+ #mrargs{} ->
+ Args0
+ end,
{Shards, RingOpts} = fabric_view:get_shards(Db, Args),
{CoordArgs, WorkerArgs} = fabric_view:fix_skip_and_limit(Args),
DocIdAndRev = fabric_util:doc_id_and_rev(DDoc),
@@ -221,10 +227,12 @@ merge_row(Dir, Collation, KeyDict0, Row, Rows0) ->
IdA < IdB;
{rev, 0} ->
IdB < IdA;
- {fwd, _} ->
- dict:fetch(A, KeyDict1) < dict:fetch(B, KeyDict1);
- {rev, _} ->
- dict:fetch(B, KeyDict1) < dict:fetch(A, KeyDict1)
+ {_, _} ->
+ % We already have a reversed key dict, and sent the
+ % workers the same reversed keys list. So here we
+ % just enforce sorting according to the order in
+ % the key dict
+ dict:fetch(A, KeyDict1) < dict:fetch(B, KeyDict1)
end
end,
[Row],
diff --git a/test/elixir/test/view_multi_key_design_test.exs b/test/elixir/test/view_multi_key_design_test.exs
index ab57e89eb..c33491620 100644
--- a/test/elixir/test/view_multi_key_design_test.exs
+++ b/test/elixir/test/view_multi_key_design_test.exs
@@ -209,16 +209,38 @@ defmodule ViewMultiKeyDesignTest do
assert length(rows) == 99
end
- test "dir works", context do
+ test "dir ascending works", context do
db_name = context[:db_name]
- resp = view(db_name, "test/multi_emit", [descending: true], [1])
+ expect_rows = mk_rows(0..99, 1, &</2) ++ mk_rows(0..99, 2, &</2)
+
+ resp = view(db_name, "test/multi_emit", [descending: false], [1, 2])
rows = resp.body["rows"]
- assert length(rows) == 100
+ assert length(rows) == 200
+ assert expect_rows == rows
- resp = view(db_name, "test/multi_emit", descending: true, keys: :jiffy.encode([1]))
+ keys = :jiffy.encode([1, 2])
+ resp = view(db_name, "test/multi_emit", descending: false, keys: keys)
rows = resp.body["rows"]
- assert length(rows) == 100
+ assert length(rows) == 200
+ assert expect_rows == rows
+ end
+
+ test "dir descending works", context do
+ db_name = context[:db_name]
+
+ expect_rows = mk_rows(0..99, 2, &>/2) ++ mk_rows(0..99, 1, &>/2)
+
+ resp = view(db_name, "test/multi_emit", [descending: true], [1, 2])
+ rows = resp.body["rows"]
+ assert length(rows) == 200
+ assert expect_rows == rows
+
+ keys = :jiffy.encode([1, 2])
+ resp = view(db_name, "test/multi_emit", descending: true, keys: keys)
+ rows = resp.body["rows"]
+ assert length(rows) == 200
+ assert expect_rows == rows
end
test "argument combinations", context do
@@ -313,4 +335,12 @@ defmodule ViewMultiKeyDesignTest do
rows = resp.body["rows"]
assert length(rows) == 2
end
+
+ defp mk_rows(range, key, sort_fun) do
+ row_fun = fn(i) -> %{"id" => "#{i}", "key" => key, "value" => i} end
+ sort_mapper = fn(row) -> {row["key"], row["id"]} end
+ range
+ |> Enum.map(row_fun)
+ |> Enum.sort_by(sort_mapper, sort_fun)
+ end
end