%%%------------------------------------------------------------------- %%% @doc Lookup пользователей для typeahead (specialist invite). %%% @end %%%------------------------------------------------------------------- -module(logic_user_lookup). -include("records.hrl"). -export([lookup/1]). -define(MAX_RESULTS, 20). -define(MIN_Q, 2). -spec lookup(Q :: binary()) -> {ok, [map()]} | {error, bad_request}. lookup(Q0) when is_binary(Q0) -> Q = string:trim(Q0), case byte_size(Q) < ?MIN_Q of true -> {error, bad_request}; false -> QLower = string:lowercase(Q), Users = [U || U <- mnesia:dirty_match_object(#user{_ = '_'}), U#user.status =:= active], ExactEmail = [U || U <- Users, string:lowercase(U#user.email) =:= QLower], NickPrefix = [U || U <- Users, is_nick_prefix(QLower, U#user.nickname)], Merged = unique_by_id(ExactEmail ++ NickPrefix), Limited = lists:sublist(Merged, ?MAX_RESULTS), {ok, [to_public(U, string:lowercase(U#user.email) =:= QLower) || U <- Limited]} end; lookup(_) -> {error, bad_request}. is_nick_prefix(QLower, Nick) when is_binary(Nick) -> Lower = string:lowercase(Nick), byte_size(Lower) >= byte_size(QLower) andalso binary:part(Lower, 0, byte_size(QLower)) =:= QLower; is_nick_prefix(_, _) -> false. unique_by_id(Users) -> maps:values(lists:foldl(fun(U, Acc) -> maps:put(U#user.id, U, Acc) end, #{}, Users)). to_public(#user{id = Id, nickname = Nick, email = Email}, true) -> #{id => Id, nickname => Nick, email => Email}; to_public(#user{id = Id, nickname = Nick, email = Email}, false) -> #{id => Id, nickname => Nick, email => mask_email(Email)}. mask_email(<<>>) -> <<>>; mask_email(Email) -> case binary:split(Email, <<"@">>) of [Local, Domain] when byte_size(Local) > 0 -> First = binary:part(Local, 0, 1), <>; _ -> <<"***">> end.