%%% @author Aleksander Lisiecki %%% @doc %%% This module provides functions to begin, run, commit and rollback transactions, %%% and statement builder. %%% It is a main API for this library. -module(eneo4j). -export([ discovery_api/0, begin_and_commit_transaction/1, begin_and_commit_transaction/2, begin_transaction/1, begin_transaction/2, run_queries_inside_transaction/2, run_queries_inside_transaction/3, keep_alive_transaction/1, keep_alive_transaction/2, commit_transaction/2, commit_transaction/3, rollback_transaction/1, rollback_transaction/2 ]). -export([ build_statement/2, build_statement/3 ]). -ignore_xref([ {?MODULE, discovery_api, 0}, {?MODULE, begin_and_commit_transaction, 1}, {?MODULE, begin_and_commit_transaction, 2}, {?MODULE, begin_transaction, 1}, {?MODULE, begin_transaction, 2}, {?MODULE, run_queries_inside_transaction, 2}, {?MODULE, run_queries_inside_transaction, 3}, {?MODULE, keep_alive_transaction, 1}, {?MODULE, keep_alive_transaction, 2}, {?MODULE, commit_transaction, 2}, {?MODULE, commit_transaction, 3}, {?MODULE, rollback_transaction, 1}, {?MODULE, rollback_transaction, 2}, {?MODULE, build_statement, 2}, {?MODULE, build_statement, 3} ]). -export_type([response/0, response_with_commit/0]). -type discovery_api_response() :: #{binary() := binary()}. -type response() :: #{binary() := any() | nonempty_list(any())}. -type response_with_commit() :: #{binary() := any()} | #{binary() := list()}. -type query_result() :: {ok, response()} | {error, any()}. -type query_result_with_commit() :: {ok, response_with_commit()} | {error, any()}. -type statements() :: eneo4j_worker:statements(). -type statement() :: eneo4j_worker:statement(). %%% @doc %%% Use this function to extract information about neo4j version, distribution, etc. %%% %%% It may be used to check the connection and if the database wa configured correctly. %%% Following code should work if you have the right url configured: %%% ``` %%% #{ %%% <<"neo4j_edition">> := _, %%% <<"neo4j_version">> := _ %%% } = eneo4j:discovery_api() %%% ''' -spec discovery_api() -> discovery_api_response(). discovery_api() -> {ok, 200, Response} = wpool:call(eneo4j_workers_pool, discovery_api, available_worker), Response. %%% @doc %%% The same as begin_and_commit_transaction(Statements, 60000). -spec begin_and_commit_transaction(statements()) -> query_result(). begin_and_commit_transaction(Statements) -> begin_and_commit_transaction(Statements, 60000). %%% @doc %%% Use this function to begin and commit a transaction in a single HTTP request. %%% %%% ``` %%% Query = <<"CREATE (n:Person { name: $name, title: $title });">>, %%% %%% % Provide params if needed: %%% ParamsAndy = #{ %%% <<"name">> => <<"Andy">>, %%% <<"title">> => <<"Developer">> %%% }, %%% %%% % Build a statement: %%% Statement = eneo4j:build_statement(Query, ParamsAndy), %%% Execute the query %%% {ok, Result} = eneo4j:begin_and_commit_transaction([Statement], 30000). %%% ''' -spec begin_and_commit_transaction(statements(), timeout()) -> query_result(). begin_and_commit_transaction(Statements, Timeout) -> Request = {begin_and_commit_transaction, Statements}, call_wpool(Request, Timeout). %%% @doc %%% The same as begin_transaction(Statements, 60000). -spec begin_transaction(statements()) -> query_result_with_commit(). begin_transaction(Statements) -> begin_transaction(Statements, 60000). %%% @doc %%% Use this function to begin transaction %%% %%% ``` %%% QueryGetPersonsNames = <<"MATCH (n:Person) RETURN n.name">>, %%% Statement = eneo4j:build_statement(QueryGetPersonsNames, #{}), %%% {ok, Result} = eneo4j:begin_transaction([Statement]), %%% ''' -spec begin_transaction(statements(), timeout()) -> query_result_with_commit(). begin_transaction(Statements, Timeout) -> Request = {begin_transaction, Statements}, call_wpool(Request, Timeout). %%% @doc %%% The same as run_queries_inside_transaction(Statements, RunLink, 60000). -spec run_queries_inside_transaction(statements(), eneo4j_response:run_queries_link()) -> query_result_with_commit(). run_queries_inside_transaction(Statements, RunLink) -> run_queries_inside_transaction(Statements, RunLink, 60000). %%% @doc %%% Use this function to begin transaction. %%% %%% ``` %%% {ok, BeginResponse} = eneo4j:begin_transaction([]), %%% %%% Query = <<"MATCH (n:Person) RETURN n.name">>, %%% Statement = eneo4j:build_statement(Query, #{}), %%% %%% {ok, RunLink} = eneo4j_response:get_run_queries_link(BeginResponse), %%% {ok, RunResponse} = eneo4j:run_queries_inside_transaction([Statement], RunLink), %%% %%% % Run queries inside transaction many times: %%% {ok, RunLink2} = eneo4j_response:get_run_queries_link(RunResponse), %%% {ok, Result} = eneo4j:run_queries_inside_transaction([Statement], RunLink2), %%% ''' -spec run_queries_inside_transaction(statements(), eneo4j_response:run_queries_link(), timeout()) -> query_result_with_commit(). run_queries_inside_transaction(Statements, RunLink, Timeout) -> Request = {{run_queries, RunLink}, Statements}, call_wpool(Request, Timeout). %%% @doc %%% The same as keep_alive_transaction(RunLink, 60000). -spec keep_alive_transaction(eneo4j_response:run_queries_link()) -> query_result_with_commit(). keep_alive_transaction(RunLink) -> keep_alive_transaction(RunLink, 60000). %%% @doc %%% Use this function to keep transaction alive. %%% It will timeout after 60 seconds (by default). %%% %%% ``` %%% {ok, BeginResponse} = eneo4j:begin_transaction([]), %%% %%% Query = <<"MATCH (n:Person) RETURN n.name">>, %%% Statement = eneo4j:build_statement(Query, #{}), %%% %%% timer:sleep(timer:seconds(40)), %%% %%% {ok, RunLink} = eneo4j_response:get_run_queries_link(BeginResponse), %%% {ok, RunResponse} = eneo4j:keep_alive_transaction(RunLink), %%% %%% % Normally transaction would timeout here, %%% % but thanks to eneo4j:keep_alive_transaction/1 call it will remain opened. %%% %%% timer:sleep(timer:seconds(40)), %%% %%% % Run queries inside transaction many times: %%% %%% {ok, RunLink2} = eneo4j_response:get_run_queries_link(RunResponse), %%% {ok, Result} = eneo4j:run_queries_inside_transaction([Statement], RunLink2), %%% ''' -spec keep_alive_transaction(eneo4j_response:run_queries_link(), timeout()) -> query_result_with_commit(). keep_alive_transaction(RunLink, Timeout) -> run_queries_inside_transaction([], RunLink, Timeout). %%% @doc %%% The same as commit_transaction(Statements, CommitLink, 60000). -spec commit_transaction(statements(), eneo4j_response:commit_transaction_link()) -> query_result_with_commit(). commit_transaction(Statements, CommitLink) -> commit_transaction(Statements, CommitLink, 60000). %%% @doc %%% Use this function commit a transaction. %%% %%% ``` %%% {ok, Response} = eneo4j:begin_transaction([Statement]), %%% {ok, CommitLink} = eneo4j_response:get_commit_transaction_link(Response), %%% %%% % You may add statements when committing a transaction %%% %%% Statements = [], %%% {ok, Result} = eneo4j:commit_transaction(Statements, CommitLink), %%% ''' -spec commit_transaction(statements(), eneo4j_response:commit_transaction_link(), timeout()) -> query_result_with_commit(). commit_transaction(Statements, CommitLink, Timeout) -> Request = {{commit_transaction, CommitLink}, Statements}, call_wpool(Request, Timeout). %%% @doc %%% The same as rollback_transaction(RollbackLink, 60000). -spec rollback_transaction(eneo4j_response:rollback_transaction_link()) -> query_result_with_commit(). rollback_transaction(RollbackLink) -> rollback_transaction(RollbackLink, 60000). %%% @doc %%% Use this function rollback a transaction. %%% %%% ``` %%% {ok, Response} = eneo4j:begin_transaction([Statement]), %%% {ok, RollbackLink} = eneo4j_response:get_rollback_transaction_link(Response), %%% %%% % You may add statements when committing a transaction %%% %%% Statements = [], %%% {ok, Result} = eneo4j:rollback_transaction(Statements, RollbackLink), %%% ''' -spec rollback_transaction(eneo4j_response:rollback_transaction_link(), timeout()) -> query_result_with_commit(). rollback_transaction(RollbackLink, Timeout) -> Request = {rollback_transaction, RollbackLink}, call_wpool(Request, Timeout). %%% @doc %%% This function allows to build a statement and include query stats. %%% @param Query - is a binary form query %%% @param Params - is a map providing params to a query %%% @param IncludeStats - is a boolean, when `true' query stats are included in a response %%% %%% This function allows to build a statement to pass to be send as a query. %%% ``` %%% Query = <<"MATCH (n) WHERE n.name = $name RETURN n">>, %%% Params = #{<<"name">> => <<"Andy">>}, %%% Statement = eneo4j:build_statement(Query, Params, true), %%% ''' -spec build_statement(binary(), #{binary() => any()}, boolean()) -> statement(). build_statement(Query, Params, IncludeStats) -> eneo4j_worker:build_statement(Query, Params, IncludeStats). %%% @doc %%% This function works the same as eneo4j:build_statement(Query, Params, false) %%% %%% ``` %%% Query = <<"MATCH (n) WHERE n.name = $name RETURN n">>, %%% Params = #{<<"name">> => <<"Andy">>}, %%% Statement = eneo4j:build_statement(Query, Params), %%% ''' -spec build_statement(binary(), #{binary() => any()}) -> statement(). build_statement(Query, Params) -> eneo4j_worker:build_statement(Query, Params). % Private functions call_wpool(Request, Timeout) -> {ok, HttpStatusCode, Response} = wpool:call( eneo4j_workers_pool, Request, available_worker, Timeout ), process_response(Request, HttpStatusCode, Response). process_response( {rollback_transaction, _}, HttpStatusCode, Response = #{<<"results">> := [], <<"errors">> := []} ) when HttpStatusCode =/= 404 -> {ok, Response}; process_response(_, 404, Response) -> {error, {transaction_not_found, Response}}; process_response(_, _, Response) -> case eneo4j_response:is_successful(Response) of true -> {ok, Response}; Error -> Error end.