summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNick Vatamaniuc <vatamane@apache.org>2021-10-13 01:52:00 -0400
committerJan Lehnardt <jan@apache.org>2021-11-01 16:47:31 +0100
commit77b44024f5058dc44074ac0a67de0a8a39c90ccc (patch)
tree8d2f78e7b55b0ae14f1a519dd290effb442d128e
parent5a7f1d40c4f0f9327a9f4b19ce8c33a672fe818d (diff)
downloadcouchdb-77b44024f5058dc44074ac0a67de0a8a39c90ccc.tar.gz
Fix reduce view row collation with unicode equivalent keys
Previously, view reduce collation with keys relied on the keys in the rows returned from the view shards to exactly match (=:=) the keys specified in the args. However, in the case when there are multiple rows which compare equal with the unicode collator, that may not always be the case. In that case when the rows are fetched from the row dict by key, they should be matched using the same collation algorithm as the one used on the view shards.
-rw-r--r--src/fabric/src/fabric_view.erl21
1 files changed, 18 insertions, 3 deletions
diff --git a/src/fabric/src/fabric_view.erl b/src/fabric/src/fabric_view.erl
index bd5e42f09..3048e8987 100644
--- a/src/fabric/src/fabric_view.erl
+++ b/src/fabric/src/fabric_view.erl
@@ -242,9 +242,8 @@ get_next_row(#collector{reducer = RedSrc} = St) when RedSrc =/= undefined ->
collation = Collation
} = St,
{Key, RestKeys} = find_next_key(Keys, Dir, Collation, RowDict),
- case dict:find(Key, RowDict) of
- {ok, Records} ->
- NewRowDict = dict:erase(Key, RowDict),
+ case reduce_row_dict_take(Key, RowDict, Collation) of
+ {Records, NewRowDict} ->
Counters = lists:foldl(fun(#view_row{worker={Worker,From}}, CntrsAcc) ->
case From of
{Pid, _} when is_pid(Pid) ->
@@ -269,6 +268,22 @@ get_next_row(State) ->
Counters1 = fabric_dict:update_counter(Worker, -1, Counters0),
{Row, State#collector{rows = Rest, counters=Counters1}}.
+reduce_row_dict_take(Key, Dict, <<"raw">>) ->
+ dict:take(Key, Dict);
+reduce_row_dict_take(Key, Dict, _Collation) ->
+ IsEq = fun(K, _) -> couch_ejson_compare:less(K, Key) =:= 0 end,
+ KVs = dict:to_list(dict:filter(IsEq, Dict)),
+ case KVs of
+ [] ->
+ error;
+ [_ | _] ->
+ {Keys, Vals} = lists:unzip(KVs),
+ NewDict = lists:foldl(fun(K, Acc) ->
+ dict:erase(K, Acc)
+ end, Dict, Keys),
+ {lists:flatten(Vals), NewDict}
+ end.
+
%% TODO: rectify nil <-> undefined discrepancies
find_next_key(nil, Dir, Collation, RowDict) ->
find_next_key(undefined, Dir, Collation, RowDict);