summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNick Vatamaniuc <vatamane@gmail.com>2021-09-17 16:43:42 -0400
committerNick Vatamaniuc <vatamane@gmail.com>2021-09-17 17:19:45 -0400
commita16df249b892f6361392ed0b6f11d1c25e055980 (patch)
treec04fab1b9fc36d82da403c89d94cfef4af015a53
parent287112888954d51fa8d69a3d6a4f8c05c1b3289c (diff)
downloadcouchdb-fix-descending-true-keys-view-query.tar.gz
Properly sort descending=true view results when a key list is providedfix-descending-true-keys-view-query
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