diff --git a/apps/emqx/src/emqx_map_lib.erl b/apps/emqx/src/emqx_map_lib.erl index 7eef2cd29..f5da55283 100644 --- a/apps/emqx/src/emqx_map_lib.erl +++ b/apps/emqx/src/emqx_map_lib.erl @@ -269,17 +269,50 @@ error_type_two_maps(M1, _M2) -> %% @doc Sum-merge map values. %% For bad merges, ErrorLogger is called to log the key, and value in M2 is ignored. -best_effort_recursive_sum(M1, M2, ErrorLogger) -> +best_effort_recursive_sum(M10, M20, ErrorLogger) -> + FilterF = fun(K, V) -> + case erlang:is_number(V) of + true -> + true; + false -> + ErrorLogger(#{failed_to_merge => K, bad_value => V}), + false + end + end, + M1 = deep_filter(M10, FilterF), + M2 = deep_filter(M20, FilterF), + do_best_effort_recursive_sum(M1, M2, ErrorLogger). + +do_best_effort_recursive_sum(M1, M2, ErrorLogger) -> F = fun(Key, V1, V2) -> case {erlang:is_map(V1), erlang:is_map(V2)} of {true, true} -> - best_effort_recursive_sum(V1, V2, ErrorLogger); - {false, false} when is_number(V1) andalso is_number(V2) -> - V1 + V2; - _ -> - ErrorLogger(#{failed_to_merge => Key}), - V1 + do_best_effort_recursive_sum(V1, V2, ErrorLogger); + {true, false} -> + ErrorLogger(#{failed_to_merge => Key, bad_value => V2}), + do_best_effort_recursive_sum(V1, #{}, ErrorLogger); + {false, true} -> + ErrorLogger(#{failed_to_merge => Key, bad_value => V1}), + do_best_effort_recursive_sum(V2, #{}, ErrorLogger); + {false, false} -> + true = is_number(V1), + true = is_number(V2), + V1 + V2 end end, merge_with(F, M1, M2). + +deep_filter(M, F) when is_map(M) -> + %% maps:filtermap is not available before OTP 24 + maps:from_list( + lists:filtermap( + fun + ({K, V}) when is_map(V) -> + {true, {K, deep_filter(V, F)}}; + ({K, V}) -> + F(K, V) andalso {true, {K, V}} + end, + maps:to_list(M) + ) + ). diff --git a/apps/emqx/test/emqx_map_lib_tests.erl b/apps/emqx/test/emqx_map_lib_tests.erl index 2a5a50cc3..f95e75d7b 100644 --- a/apps/emqx/test/emqx_map_lib_tests.erl +++ b/apps/emqx/test/emqx_map_lib_tests.erl @@ -49,8 +49,62 @@ best_effort_recursive_sum_test_() -> ), receive {log, Log} -> - ?assertEqual(#{failed_to_merge => bar}, Log) + ?assertEqual(#{failed_to_merge => bar, bad_value => bar}, Log) after 1000 -> error(timeout) end - end + end, + ?_assertEqual( + #{}, + emqx_map_lib:best_effort_recursive_sum( + #{foo => foo}, #{foo => bar}, DummyLogger + ) + ), + ?_assertEqual( + #{foo => 1}, + emqx_map_lib:best_effort_recursive_sum( + #{foo => 1}, #{foo => bar}, DummyLogger + ) + ), + ?_assertEqual( + #{foo => 1}, + emqx_map_lib:best_effort_recursive_sum( + #{foo => bar}, #{foo => 1}, DummyLogger + ) + ), + ?_assertEqual( + #{foo => #{bar => 1}}, + emqx_map_lib:best_effort_recursive_sum( + #{foo => #{bar => 1}}, #{foo => 1}, DummyLogger + ) + ), + ?_assertEqual( + #{foo => #{bar => 1}}, + emqx_map_lib:best_effort_recursive_sum( + #{foo => 1}, #{foo => #{bar => 1}}, DummyLogger + ) + ), + ?_assertEqual( + #{foo => #{bar => 1}}, + emqx_map_lib:best_effort_recursive_sum( + #{foo => 1, bar => ignored}, #{foo => #{bar => 1}}, DummyLogger + ) + ), + ?_assertEqual( + #{foo => #{bar => 2}, bar => #{foo => 1}}, + emqx_map_lib:best_effort_recursive_sum( + #{foo => 1, bar => #{foo => 1}}, #{foo => #{bar => 2}, bar => 2}, DummyLogger + ) + ), + ?_assertEqual( + #{foo => #{bar => 2}, bar => #{foo => 1}}, + emqx_map_lib:best_effort_recursive_sum( + #{foo => #{bar => 2}, bar => 2}, #{foo => 1, bar => #{foo => 1}}, DummyLogger + ) + ), + ?_assertEqual( + #{foo => #{bar => #{}}}, + emqx_map_lib:best_effort_recursive_sum( + #{foo => #{bar => #{foo => []}}}, #{foo => 1}, DummyLogger + ) + ) ].