summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNick Vatamaniuc <vatamane@apache.org>2021-10-13 01:52:00 -0400
committerNick Vatamaniuc <vatamane@gmail.com>2021-10-21 11:14:00 -0400
commit4da057a91337590b44dd5ca303b365ebdda65867 (patch)
treea3b5f8e3ae16eb2056dbb2516b5cfdd78656ff37
parent24bc0ce338a5aae91f1f3e8714178360e10ba24b (diff)
downloadcouchdb-4da057a91337590b44dd5ca303b365ebdda65867.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);