erldis / src / erldis.erl

-module(erldis).

-compile(export_all).
-define(EOL, "\r\n").

%% helpers
flatten({error, Message}) ->
	{error, Message};
flatten(List) when is_list(List)->	 
	lists:flatten(List).

%% exposed API
connect() ->
	erldis_client:connect().
connect(Host) ->
	erldis_client:connect(Host).
connect(Host, Port) ->
	erldis_client:connect(Host, Port).
connect(Host, Port, Options) ->
	erldis_client:connect(Host, Port, Options).

get_all_results(Client) -> gen_server2:call(Client, get_all_results).

set_pipelining(Client, Bool) -> gen_server2:cast(Client, {pipelining, Bool}).

quit(Client) -> erldis_client:stop(Client).

%% Commands operating on string values
internal_set_like(Client, Command, Key, Value) when is_binary(Key), is_binary(Value) ->
	Size = list_to_binary(integer_to_list(size(Value))),
	Cmd = <<Command/binary, Key/binary, " ", Size/binary, ?EOL, Value/binary>>,
	internal_set_like_reply(erldis_client:call(Client, Cmd));
internal_set_like(Client, Command, Key, Value) when is_binary(Value) ->
	Reply = erldis_client:call(Client, Command, [[Key, size(Value)], [Value]]),
	internal_set_like_reply(Reply);
internal_set_like(Client, Command, Key, Value) when not is_binary(Key) ->
	internal_set_like(Client, Command, erldis_binaries:to_binary(Key), Value);
internal_set_like(Client, Command, Key, Value) when not is_binary(Value) ->
	internal_set_like(Client, Command, Key, erldis_binaries:to_binary(Value));
internal_set_like(_, _, _, _) ->
	{error, badarg}.

internal_zset_like(Client, Command, Key, Score, Value) when is_binary(Value) ->
	Reply = erldis_client:call(Client, Command, [[Key, Score, size(Value)], [Value]]),
	internal_set_like_reply(Reply).

internal_set_like_reply([{error, _}=Error]) -> Error;
internal_set_like_reply([R]) when is_atom(R) -> R;
internal_set_like_reply(R) -> R.

auth(Client, Password) ->
	erldis_client:scall(Client, <<"auth ", Password/binary>>).

exec(Client, Fun) ->
	case erldis_client:sr_scall(Client, <<"multi ">>) of
		ok ->
			set_pipelining(Client, true),
			Fun(Client),
			get_all_results(Client),
			set_pipelining(Client, false),
			erldis_client:scall(Client, <<"exec ">>);
		_ ->
			{error, unsupported}
	end.

numeric(false) -> 0;
numeric(true) -> 1;
numeric(nil) -> 0;
numeric(I) when is_binary(I) -> numeric(binary_to_list(I));
numeric(I) when is_list(I) ->
	try list_to_integer(I)
	catch
		error:badarg ->
			try list_to_float(I)
			catch error:badarg -> I
			end
	end;
numeric(I) -> I.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Commands operating on every value %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

exists(Client, Key) when not is_binary(Key) ->
	exists(Client, erldis_binaries:to_binary(Key));
exists(Client, Key) ->
	erldis_client:sr_scall(Client, <<"exists ", Key/binary>>).

del(Client, Key) when not is_binary(Key) ->
	del(Client, erldis_binaries:to_binary(Key));
del(Client, Key) ->
	erldis_client:sr_scall(Client, <<"del ", Key/binary>>).

type(Client, Key) when not is_binary(Key) ->
	type(Client, erldis_binaries:to_binary(Key));
type(Client, Key) ->
	erldis_client:sr_scall(Client, <<"type ", Key/binary>>).

keys(Client, Pattern) when not is_binary(Pattern) ->
	keys(Client, erldis_binaries:to_binary(Pattern));
keys(Client, Pattern) ->
	% TODO: tokenize the binary directly (if is faster)
	% NOTE: with binary-list conversion, timer:tc says 26000-30000 microseconds
	case erldis_client:scall(Client, <<"keys ">>, [Pattern]) of
		[] -> [];
		[B] -> [list_to_binary(S) || S <- string:tokens(binary_to_list(B), " ")]
	end.

randomkey(Client, Key) when not is_binary(Key) ->
	randomkey(Client, erldis_binaries:to_binary(Key));
randomkey(Client, Key) ->
	erldis_client:sr_scall(Client, <<"randomkey ", Key/binary>>).

rename(Client, OldKey, NewKey) when not is_binary(OldKey) ->
	rename(Client, erldis_binaries:to_binary(OldKey), NewKey);
rename(Client, OldKey, NewKey) when not is_binary(NewKey) ->
	rename(Client, OldKey, erldis_binaries:to_binary(NewKey));
rename(Client, OldKey, NewKey) ->
	erldis_client:sr_scall(Client, <<"rename ", OldKey/binary, " ", NewKey/binary>>).

renamenx(Client, OldKey, NewKey) when not is_binary(OldKey) ->
	renamenx(Client, erldis_binaries:to_binary(OldKey), NewKey);
renamenx(Client, OldKey, NewKey) when not is_binary(NewKey) ->
	renamenx(Client, OldKey, erldis_binaries:to_binary(NewKey));
renamenx(Client, OldKey, NewKey) ->
	erldis_client:sr_scall(Client, <<"renamenx ", OldKey/binary, " ", NewKey/binary>>).

dbsize(Client) -> numeric(erldis_client:sr_scall(Client, <<"dbsize ">>)).

expire(Client, Key, Seconds) when not is_binary(Key) ->
	expire(Client, erldis_binaries:to_binary(Key), Seconds);
expire(Client, Key, Seconds) ->
	erldis_client:sr_scall(Client, <<"expire ", Key/binary, " ", Seconds/binary>>).

ttl(Client, Key) -> erldis_client:sr_scall(Client, <<"ttl ", Key/binary>>).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Commands operating on string values %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

set(Client, Key, Value) -> internal_set_like(Client, <<"set ">>, Key, Value).

get(Client, Key) when not is_binary(Key) ->
	get(Client, erldis_binaries:to_binary(Key));
get(Client, Key) ->
	erldis_client:sr_scall(Client, <<"get ", Key/binary>>).

getset(Client, Key, Value) -> internal_set_like(Client, <<"getset ">>, Key, Value).

mget(Client, Keys) -> erldis_client:scall(Client, <<"mget ">>, Keys).

setnx(Client, Key, Value) -> internal_set_like(Client, <<"setnx ">>, Key, Value).

incr(Client, Key) when not is_binary(Key) ->
	incr(Client, erldis_binaries:to_binary(Key));
incr(Client, Key) ->
	numeric(erldis_client:sr_scall(Client, <<"incr ", Key/binary>>)).

incrby(Client, Key, By) ->
	numeric(erldis_client:sr_scall(Client, <<"incrby ">>, [Key, By])).

decr(Client, Key) when not is_binary(Key) ->
	decr(Client, erldis_binaries:to_binary(Key));
decr(Client, Key) ->
	numeric(erldis_client:sr_scall(Client, <<"decr ", Key/binary>>)).

decrby(Client, Key, By) ->
	numeric(erldis_client:sr_scall(Client, <<"decrby ">>, [Key, By])).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Commands operating on lists %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

rpush(Client, Key, Value) -> internal_set_like(Client, <<"rpush ">>, Key, Value).

lpush(Client, Key, Value) -> internal_set_like(Client, <<"lpush ">>, Key, Value).

llen(Client, Key) -> numeric(erldis_client:sr_scall(Client, <<"llen ">>, [Key])).

lrange(Client, Key, Start, End) ->
	erldis_client:scall(Client, <<"lrange ">>, [Key, Start, End]).

ltrim(Client, Key, Start, End) ->
	erldis_client:scall(Client, <<"ltrim ">>, [Key, Start, End]).
	
lindex(Client, Key, Index) ->
	erldis_client:sr_scall(Client, <<"lindex ">>, [Key, Index]).

lset(Client, Key, Index, Value) when not is_binary(Value) ->
	lset(Client, Key, Index, erldis_binaries:to_binary(Value));
lset(Client, Key, Index, Value) ->
	erldis_client:call(Client, <<"lset ">>, [[Key, Index, size(Value)], [Value]]).

lrem(Client, Key, Number, Value) when not is_binary(Value) ->
	lrem(Client, Key, Number, erldis_binaries:to_binary(Value));
lrem(Client, Key, Number, Value) ->
	% TODO: needs sr_scall like single results
	numeric(hd(erldis_client:call(Client, <<"lrem ">>, [[Key, Number, size(Value)], [Value]]))).

lpop(Client, Key) -> erldis_client:sr_scall(Client, <<"lpop ">>, [Key]).

rpop(Client, Key) -> erldis_client:sr_scall(Client, <<"rpop ">>, [Key]).

blpop(Client, Keys) -> erldis_client:bcall(Client, <<"blpop ">>, Keys, infinity).
blpop(Client, Keys, Timeout) -> erldis_client:bcall(Client, <<"blpop ">>, Keys, Timeout).

brpop(Client, Keys) -> erldis_client:bcall(Client, <<"brpop ">>, Keys, infinity).
brpop(Client, Keys, Timeout) -> erldis_client:bcall(Client, <<"brpop ">>, Keys, Timeout).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Commands operating on sets %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

sadd(Client, Key, Member) -> internal_set_like(Client, <<"sadd ">>, Key, Member).

srem(Client, Key, Member) -> internal_set_like(Client, <<"srem ">>, Key, Member).

smove(Client, SrcKey, DstKey, Member) when not is_binary(Member) ->
	smove(Client, SrcKey, DstKey, erldis_binaries:to_binary(Member));
smove(Client, SrcKey, DstKey, Member) ->
	erldis_client:call(Client, <<"smove ">>, [[SrcKey, DstKey, size(Member)], [Member]]).

scard(Client, Key) -> numeric(erldis_client:sr_scall(Client, <<"scard ">>, [Key])).

sismember(Client, Key, Member) -> internal_set_like(Client, <<"sismember ">>, Key, Member).

sintersect(Client, Keys) -> erldis_client:scall(Client, <<"sinter ">>, Keys).

sinter(Client, Keys) -> sintersect(Client, Keys).

sinterstore(Client, DstKey, Keys) ->
	erldis_client:scall(Client, <<"sinterstore ">>, [DstKey|Keys]).

sunion(Client, Keys) -> erldis_client:scall(Client, <<"sunion ">>, Keys).

sunionstore(Client, DstKey, Keys) ->
	erldis_client:scall(Client, <<"sunionstore ">>, [DstKey|Keys]).

sdiff(Client, Keys) -> erldis_client:scall(Client, <<"sdiff ">>, Keys).

sdiffstore(Client, DstKey, Keys) ->
	erldis_client:scall(Client, <<"sdiffstore ">>, [DstKey|Keys]).

smembers(Client, Key) -> erldis_client:scall(Client, <<"smembers ">>, [Key]).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Commands operating on ordered sets %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

zadd(Client, Key, Score, Member) ->
	erldis_client:sr_scall(Client, bulk_cmd([<<"zadd">>, Key, Score], Member)).

zrem(Client, Key, Member) ->
	erldis_client:sr_scall(Client, bulk_cmd([<<"zrem">>, Key], Member)).

zincrby(Client, Key, By, Member) ->
	numeric(erldis_client:sr_scall(Client, bulk_cmd([<<"zincrby">>, Key, By], Member))).

zrange(Client, Key, Start, End) ->
	erldis_client:scall(Client, inline_cmd([<<"zrange">>, Key, Start, End])).

% TODO: return [{member, score}] for withscores functions
%zrange_withscores(Client, Key, Start, End) ->
%	erldis_client:scall(Client, <<"zrange ">>, [Key, Start, End, <<"withscores">>]).

zrevrange(Client, Key, Start, End) ->
	erldis_client:scall(Client, inline_cmd([<<"zrevrange">>, Key, Start, End])).

%zrevrange_withscores(Client, Key, Start, End) ->
%	erldis_client:scall(Client, <<"zrevrange ">>, [Key, Start, End, <<"withscores">>]).

zrangebyscore(Client, Key, Min, Max) ->
	erldis_client:scall(Client, inline_cmd([<<"zrangebyscore ">>, Key, Min, Max])).

zrangebyscore(Client, Key, Min, Max, Offset, Count) ->
	Cmd = inline_cmd([<<"zrangebyscore ">>, Key, Min, Max, <<"limit">>, Offset, Count]),
	erldis_client:scall(Client, Cmd).

zcard(Client, Key) ->
	numeric(erldis_client:sr_scall(Client, inline_cmd(<<"zcard">>, Key))).

zscore(Client, Key, Member) ->
	numeric(erldis_client:sr_scall(Client, bulk_cmd([<<"zscore">>, Key], Member))).

zremrangebyscore(Client, Key, Min, Max) ->
	Cmd = inline_cmd([<<"zremrangebyscore ">>, Key, Min, Max]),
	numeric(erldis_client:sr_scall(Client, Cmd)).

% TODO: use these for other commands, especially to replace internal_set_like
% and reduce number of erldis_client:*call functions
inline_cmd(Args) -> make_cmd([Args]).

inline_cmd(Cmd, Key) -> inline_cmd([Cmd, Key]).

bulk_cmd(Line1, Bulk) ->
	Bin = erldis_binaries:to_binary(Bulk),
	make_cmd([Line1 ++ [size(Bin)], [Bin]]).

make_cmd(Lines) ->
	BLines = [erldis_binaries:join(Line, <<" ">>) || Line <- Lines],
	erldis_binaries:join(BLines, <<"\r\n">>).

%%%%%%%%%%%%%
%% Sorting %%
%%%%%%%%%%%%%

sort(Client, Key) -> erldis_client:scall(Client, <<"sort ">>, [Key]).

sort(Client, Key, Extra) -> erldis_client:scall(Client, <<"sort ">>, [Key, Extra]).	

%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Multiple DB commands %%
%%%%%%%%%%%%%%%%%%%%%%%%%%

select(Client, Index) -> erldis_client:sr_scall(Client, <<"select ">>, [Index]).

move(Client, Key, DBIndex) ->
	erldis_client:scall(Client, <<"move ">>, [Key, DBIndex]).

flushdb(Client) -> erldis_client:sr_scall(Client, <<"flushdb ">>).

flushall(Client) -> erldis_client:sr_scall(Client, <<"flushall ">>).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Persistence control commands %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

save(Client) -> erldis_client:scall(Client, <<"save ">>).

bgsave(Client) -> erldis_client:scall(Client, <<"bgsave ">>).

lastsave(Client) -> erldis_client:scall(Client, <<"lastsave ">>).

shutdown(Client) -> erldis_client:scall(Client, <<"shutdown ">>).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Remote server control commands %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

info(Client) -> erldis_client:scall(Client, <<"info ">>).

slaveof(Client, Host, Port) ->
	erldis_client:scall(Client, <<"slaveof ">>, [Host, Port]).

slaveof(Client) -> erldis_client:scall(Client, <<"slaveof ">>, ["no one"]).
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.