%%% @doc Query builder for Surreal. -module(surreal_query). -export([make_one/1, make_all/1]). -type string_format() :: binary() | atom() | bitstring(). -type query() :: {block, Inside :: list(query())} | {select} | {select, Field :: string_format()} | {from, Target :: string_format()} | {from, Targets :: list(string_format())} | {where, {Operator :: string_format(), Left :: string_format(), Right :: term()}} | {where, {Left :: string_format(), Right :: term()}} | {where, Conditions :: list( {Operator :: string_format(), Left :: string_format(), Right :: term()} | {Left :: string_format(), Right :: term()} )} | {create, Something :: string_format()} | {set, {Key :: string_format(), Value :: term()}} | {set, list({Key :: string_format(), Value :: term()})} | {var, {Key :: string_format(), Value :: term()}} | {sleep, Millis :: integer()} | {delete, Something :: string_format()} | {update, Something :: string_format()} | {use, {namespace, Namespace :: string_format()}} | {use, {database, Database :: string_format()}} | {use, {Namespace :: string_format(), Database :: string_format()}}. %% Generate a query string from list of queries (list of operations). -spec make_all(Queries :: list(list(query()))) -> string(). make_all(Queries) -> Results = lists:map(fun make_one/1, Queries), WithFormatted = lists:join("\n", Results), lists:flatten(WithFormatted). %% Generate a query string from list of operations. -spec make_one(Query :: list(query())) -> string(). make_one(Query) -> Results = lists:map(fun make_one_command/1, Query), WithFormatted = lists:join(" ", Results) ++ ";", lists:flatten(WithFormatted). %% @private %% BLOCK Builders make_one_command({block, Statements}) when is_list(Statements) -> Results = lists:map(fun make_one_command/1, Statements), WithFormatted = lists:join(" ", Results), io_lib:format("(~s)", [WithFormatted]); %% SELECT Builders make_one_command({select}) -> "SELECT *"; make_one_command({select, Field}) when is_binary(Field); is_atom(Field); is_bitstring(Field) -> io_lib:format("SELECT ~s", [Field]); make_one_command({select, Fields}) when is_list(Fields) -> ToString = lists:map(fun atom_to_list/1, Fields), WithFormatted = lists:flatten(lists:join(", ", ToString)), io_lib:format("SELECT ~s", [WithFormatted]); %% FROM Builders make_one_command({from, Target}) when is_binary(Target); is_atom(Target); is_bitstring(Target) -> io_lib:format("FROM ~s", [Target]); make_one_command({from, Targets}) when is_list(Targets) -> ToString = lists:map(fun atom_to_list/1, Targets), WithFormatted = lists:flatten(lists:join(", ", ToString)), io_lib:format("FROM ~s", [WithFormatted]); %% WHERE Builders make_one_command({where, {Operator, Left, Right}}) -> io_lib:format("WHERE ~s ~s ~p", [Left, Operator, Right]); make_one_command({where, {Left, Right}}) -> io_lib:format("WHERE ~s = ~p", [Left, Right]); make_one_command({where, Conditions}) when is_list(Conditions) -> NewConditions = lists:map(fun(Condition) -> {where, Condition} end, Conditions), WithConcat = lists:map( fun(Condition) -> "WHERE " ++ Actual = make_one_command(Condition), Actual end, NewConditions ), WithFormatted = lists:flatten(lists:join(", ", WithConcat)), io_lib:format("WHERE ~s", [WithFormatted]); %% CREATE Builders make_one_command({create, Something}) -> io_lib:format("CREATE ~s", [Something]); %% SET Builders make_one_command({set, {Key, Value}}) -> io_lib:format("SET ~s = ~p", [Key, Value]); make_one_command({set, AllKv}) when is_list(AllKv) -> NewKv = lists:map(fun(Kv) -> {set, Kv} end, AllKv), WithConcat = lists:map( fun(Condition) -> "SET " ++ Actual = make_one_command(Condition), Actual end, NewKv ), WithFormatted = lists:flatten(lists:join(", ", WithConcat)), io_lib:format("SET ~s", [WithFormatted]); %% LET Builders make_one_command({var, {Key, Value}}) -> io_lib:format("LET $~s = ~p", [Key, Value]); %% SLEEP Builders make_one_command({sleep, MsDuration}) -> io_lib:format("SLEEP ~pms", [MsDuration]); %% DELETE Builders make_one_command({delete, Something}) -> io_lib:format("DELETE ~s", [Something]); %% UPDATE Builders make_one_command({update, Something}) -> io_lib:format("UPDATE ~s", [Something]); %% USE Builders make_one_command({use, {namespace, Namespace}}) -> io_lib:format("USE NS ~s", [Namespace]); make_one_command({use, {database, Database}}) -> io_lib:format("USE DB ~s", [Database]); make_one_command({use, {Namespace, Database}}) -> io_lib:format("USE NS ~s DB ~s", [Namespace, Database]); make_one_command(_Other) -> "".