diff --git a/apps/roster/include/roster.hrl b/apps/roster/include/roster.hrl index 3d8f6cde36cb151f22053fc7b631611f277d64ae..7069adc2af6eec6b52f3b1bfeea28bab605b10a8 100644 --- a/apps/roster/include/roster.hrl +++ b/apps/roster/include/roster.hrl @@ -22,11 +22,9 @@ -record('Search', {id =[] :: [] | integer(), phone =[] :: [] | list(binary()), - names =[] :: [] | binary(), - surnames =[] :: [] | binary(), - query =[] :: [] | binary(), - status =[] :: [] | contact | qrcode | nick - | names | phone | phonebook }). + query =[] :: [] | list(term()), % [{nick, _}] + status =[] :: [] | contact | qrcode | names}). + -record(p2p, {from =[] :: [] | binary(), to =[] :: [] | binary() }). @@ -215,6 +213,8 @@ status =[] :: [] | contact | simple | list | common_groups | ban | unban | mute | unmute - | request | confirm | revoke }). + | request | confirm | revoke }). +-record('Index', {id = [] :: [] | term(), + roster = [] :: [] | list(integer())}). -endif. diff --git a/apps/roster/src/protocol/roster_roster.erl b/apps/roster/src/protocol/roster_roster.erl index 2f83a54c082c775bf31c634f30b3c1bf40763121..88933cfad9f00f749c792caccad57233ba1a8a57 100644 --- a/apps/roster/src/protocol/roster_roster.erl +++ b/apps/roster/src/protocol/roster_roster.erl @@ -9,25 +9,29 @@ info(#'Roster'{id=RosterId, status=patch}=Data, Req, #cx{params = ClientId, client_pid = C }=State) -> n2o:info(?MODULE, "~p:~p:Roster/patch:~s",[RosterId,ClientId,io_lib:format("~p",[Data])]), case kvs:get('Roster',RosterId) of - {error,Reason} -> {io,{error,Reason},<<>>}; - {ok, Roster=#'Roster'{phone=Phone}} -> + {error,Reason} -> {reply, {bert, {io,{error,Reason}},<<>>},Req, State}; + {ok, Roster=#'Roster'{phone=Phone, nick=OldNick}} -> %% Data preprocessing. Validate nickname - #'Roster'{nick = Nick} = Data, + OldLnick=string:lowercase(OldNick), + Lnick=string:lowercase(Nick), n2o:info(?MODULE, "Debug. Nick ~p",[Nick]), ValidationError = case Nick of [] -> false; - Nick -> - case kvs:index('Roster', nick, Nick) of - [] -> false; - Val -> - #'Roster'{id = TakenRosterId} = lists:last(Val), - n2o:info(?MODULE, "Debug. TakenRosterId ~p",[TakenRosterId]), - case TakenRosterId of - RosterId -> false; - _ -> true - end + _ -> + case kvs:get('Index', {nick, Lnick}) of + {ok,#'Index'{roster=[TakenRosterId]}} when TakenRosterId==RosterId-> + %#'Roster'{id = TakenRosterId} = lists:last(Val), + n2o:info(?MODULE, "Debug. TakenRosterId ~p",[TakenRosterId]), + case Lnick of + OldLnick when Nick==OldNick -> true; + _ -> n2o_async:send(system,?MODULE,{update_index,#'Roster'{id=RosterId,nick=Lnick}}), + false + end; + {ok,#'Index'{roster=[TakenRosterId]}} -> true; + _ -> n2o_async:send(system,?MODULE,{update_index,#'Roster'{id=RosterId,nick=Lnick}}), + false end end, n2o:info(?MODULE, "Debug. ValidationError ~p",[ValidationError]), @@ -100,4 +104,9 @@ proc({update_ac,#'Contact'{phone_id=PhoneId}=C}, #handler{}=H) -> roster:update_contact(FriendId, C#'Contact'{reader=R, presence=[], update=UPT}); E -> E end end || #'Contact'{phone_id=PId} <-roster:get_on_status(RosterId,friend)], + {reply, [], H}; + +proc({update_index,#'Roster'{id=RosterId, nick=Nick}}, #handler{}=H) -> + kvs:put(#'Index'{id={nick,Nick},roster=[RosterId]}), +%% @TODO: Have to be code for adding new nick into B-tree for nicks {reply, [], H}. \ No newline at end of file diff --git a/apps/roster/src/protocol/roster_search.erl b/apps/roster/src/protocol/roster_search.erl index fe8be89f21c3315fd41781599a5a4a01d758dde2..4f1d4096084dc6019f654980ef47a7213f4a96b7 100644 --- a/apps/roster/src/protocol/roster_search.erl +++ b/apps/roster/src/protocol/roster_search.erl @@ -5,17 +5,16 @@ start() -> n2o_async:start(#handler{module=?MODULE,class=system,group=roster,name=?MODULE,state=[]}). -% @TODO: Need update this code after add new field 'nick' into protocol SEARCH for search user by nick -info(#'Search'{names = Names, surnames = Surnames, phone=[], status = Status}, Req, #cx{params = <<"emqttd_",_/binary>>} = State) -> - L = [[begin - N = binary_to_list(V) ++ F, [#'Contact'{avatar = Av, names = Na, surnames = Sn} - || #'Roster'{avatar = Av, names = Na, surnames = Sn} <- kvs:index('Roster', K, list_to_binary(N))] - end || F <- ets:match(K, {binary_to_list(V) ++ '$1'})] || {K, V}<-[{names, Names}, {surnames, Surnames}, {nick, Names}], is_binary(V)], - Data = #'Roster'{userlist=lists:flatten(L), status = search}, +info(#'Search'{id=From, phone=[], query =Q, status = contact}, Req, #cx{params = <<"emqttd_",_/binary>>= ClientId} = State) -> + n2o:info(?MODULE, "~p:~p:Search/contact:~p",[From,ClientId,Q]), + L = [begin Rs=case kvs:get('Index',{K, string:lowercase(V)}) of {ok,#'Index'{roster=IDs}}-> IDs; _ ->[] end, + [ roster:to_contact([RosterId],[]) || RosterId <-Rs ] end || {K, V}<-Q ], + Data = #'Roster'{id=From, userlist=lists:flatten(L), status = search}, {reply, {bert, Data}, Req, State}; -info(#'Search'{id=From, phone=Phones, query = [], status=Status},Req, - #cx{params = <<"emqttd_",_/binary>> = ClientId, session= Token} =State) when is_list(Phones) -> + +info(#'Search'{id=From, phone=Phones, query = [], status = contact},Req, + #cx{params = <<"emqttd_",_/binary>> = ClientId} =State) when is_list(Phones) -> n2o:info(?MODULE, "~p:~p:Search/contact:~p",[From,ClientId,Phones]), Result=[ begin case roster:parts_phone_id(Phone) of @@ -25,14 +24,17 @@ info(#'Search'{id=From, phone=Phones, query = [], status=Status},Req, _ -> roster:to_contact([FriendId], []) end; E -> [] end end || Phone<-lists:usort(Phones)], - Res = #'Roster'{id=From, userlist=lists:flatten(Result),status=Status}, + Res = #'Roster'{id=From, userlist=lists:flatten(Result),status=search}, {reply, {bert, Res}, Req, State}; +info(#'Search'{id=From, phone=[], query =Q, status = names}, Req, #cx{params = <<"emqttd_",_/binary>>= ClientId} = State) -> + n2o:info(?MODULE, "~p:~p:Search/names:~p",[From,ClientId,Q]), + %% @TODO: Need to add code for search into B-tree for all matches of registred names to Q-template + {reply, {bert, <<>>}, Req, State}; + info(#'Search'{}=Data, Req, State) -> {reply, {bert, Data}, Req, State}. proc(init,#handler{name=roster_search} = Async) -> n2o:info(?MODULE, "Search ASYNC started", []), -%% [[ets:insert(K, [{binary_to_list(V)}]) || {K, V}<-[{names, Names}, {surnames, Surnames}, {nick, Nick}], is_binary(V)] -%% || #'Roster'{names = Names, surnames = Surnames, nick = Nick} <- kvs:all('Roster')], {ok,Async}. \ No newline at end of file diff --git a/apps/roster/src/roster.erl b/apps/roster/src/roster.erl index 4d476059c5b31bd31d13565a05d04be00da9b032..13345955585aa499e5ba724a5b09ab8bc8f44dce 100644 --- a/apps/roster/src/roster.erl +++ b/apps/roster/src/roster.erl @@ -42,7 +42,8 @@ tables() -> [ #table{name='Auth',fields=record_info(fields,'Auth'), keys=[dev_k #table{name=reader,fields=record_info(fields,reader), keys=[feed]}, #table{name='Room',fields=record_info(fields,'Room')}, #table{name='Member',container=chain, fields=record_info(fields,'Member')}, - #table{name='Profile',fields=record_info(fields,'Profile')} ]. + #table{name='Profile',fields=record_info(fields,'Profile')}, + #table{name='Index',fields=record_info(fields,'Index')}]. % PROFILE MANAGEMENT @@ -145,6 +146,8 @@ remove_roster(Phone, RosterId) -> [begin [_P,Id]=parts_phone_id(PhoneId), kvs:delete(writer,roster:feed_key(p2p, PId, PhoneId)), kvs:delete(reader,R), remove_contacts(Id, [PId]), remove_member(PhoneId) end || #'Contact'{phone_id=PhoneId, reader=R}<-get_contacts(RosterId)], + {ok,#'Roster'{nick=Nick}}=kvs:get('Roster',RosterId), + kvs:delete('Index',{nick,string:lowercase(Nick)}), kvs:delete('Roster',RosterId), kvs:put(Profile#'Profile'{rosters=Roster, update=now_msec()}), case Roster of diff --git a/apps/roster/src/roster_test.erl b/apps/roster/src/roster_test.erl index b06ed299361411530d5c1a541caaf21c0a6c9b83..3529cd5f025e2c1aa0d51cfc212e0168c7e0e147 100644 --- a/apps/roster/src/roster_test.erl +++ b/apps/roster/src/roster_test.erl @@ -38,6 +38,8 @@ receive_drop() -> receive _ -> receive_drop() after ?DRP_TMOUT -> skip end. roster_case(Term,State) -> case Term of + #'Roster'{} = Roster -> %n2o:info(?MODULE, "Roster/get:~p",[Roster]), + State#state{last_res = Roster}; #'Person'{id=_Id} = P -> %n2o:info(?MODULE, "~p", [P]), State#state{last_res = P}; #'Profile'{phone=_Phone} = P -> %n2o:info(?MODULE, "Profile/get:~p",[P]), @@ -459,23 +461,21 @@ test_loaded()-> #'Roster'{} = send_sync(ClientId,R) end || _Cont<-lists:seq(1,10)], stop_client(ClientId). -test_public_search() -> - Phone = <<"753">>, - {ClientId, Token} = reg_fake_user(Phone, <<"DevKey_", Phone/binary>>, 0), - start_client(ClientId, Token), - receive_drop(), - kvs:put(#'Roster'{id = 99999, names = <<"John">>, surnames = <<"Doe">>, avatar = <<"avatar">>, nick = <<"maximilian">>}), - ets:insert(nick, [{"maximilian"}]), - ets:insert(names, [{"John"}]), - ets:insert(surnames, [{"Doe"}]), - #'Roster'{userlist = [#'Contact'{names = <<"John">>}]} = send_sync(ClientId, #'Search'{names = <<"m">>, status = nick}), - #'Roster'{userlist = [#'Contact'{names = <<"John">>}]} = send_sync(ClientId, #'Search'{names = <<"ma">>, status = nick}), - #'Roster'{userlist = [#'Contact'{names = <<"John">>}]} = send_sync(ClientId, #'Search'{names = <<"max">>, status = nick}), - #'Roster'{userlist = [#'Contact'{names = <<"John">>}]} = send_sync(ClientId, #'Search'{names = <<"J">>, status = names}), - #'Roster'{userlist = [#'Contact'{names = <<"John">>}]} = send_sync(ClientId, #'Search'{names = <<"Jo">>, status = names}), - #'Roster'{userlist = [#'Contact'{names = <<"John">>}]} = send_sync(ClientId, #'Search'{surnames = <<"D">>, status = names}), - #'Roster'{userlist = [#'Contact'{names = <<"John">>}]} = send_sync(ClientId, #'Search'{surnames = <<"Do">>, status = names}), - stop_client(ClientId). +test_search() -> + Phones= [_PhoneA,PhoneB] = [<<"717">>,<<"313">>], + L=[{FromId, AClientId}, {ToId, BClientId}] = [begin + roster:purge_user(Phone), + {ClientId, Token} = reg_fake_user(Phone), + start_client(ClientId , Token), + receive_test(ClientId, #'Auth'{}, 0), + [Roster] = list_rosters(ClientId, Phone), + {roster:roster_id(Roster), ClientId} + end || Phone<-Phones], + #'Roster'{} = send_sync(AClientId,#'Roster'{id=FromId, nick= <<"Test">>, status = patch}), + #'Roster'{} = send_sync(BClientId,#'Roster'{id=ToId, nick= <<"Maximilian">>, status = patch}), + #'Roster'{userlist=[#'Contact'{}]} = send_sync(AClientId, #'Search'{id=FromId, query=[{nick, <<"maxiMilian">>}], status = contact}), + #'Roster'{userlist=[]} = send_sync(BClientId, #'Search'{id=ToId, query= [{nick,<<"Tes">>}], status = contact}), + [stop_client(ClientId) || {_, ClientId}<-L]. client_test() -> Phone = <<"753">>,