You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by va...@apache.org on 2021/09/22 16:00:54 UTC

[couchdb] branch 3.x updated: Properly sort descending=true view results when a key list is provided

This is an automated email from the ASF dual-hosted git repository.

vatamane pushed a commit to branch 3.x
in repository https://gitbox.apache.org/repos/asf/couchdb.git


The following commit(s) were added to refs/heads/3.x by this push:
     new 22189ea  Properly sort descending=true view results when a key list is provided
22189ea is described below

commit 22189ea6ad3665639bd3223d9ef915fe2561e2d1
Author: Nick Vatamaniuc <va...@gmail.com>
AuthorDate: Fri Sep 17 16:43:42 2021 -0400

    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.
---
 src/fabric/src/fabric_view_map.erl              | 18 +++++++----
 test/elixir/test/view_multi_key_design_test.exs | 40 +++++++++++++++++++++----
 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 ff6aa8b..8ab8083 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 ab57e89..c334916 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