From cf5e1ce91477fce731d0c4f504cad55141b09698 Mon Sep 17 00:00:00 2001 From: chan Date: Mon, 21 Jul 2014 16:39:17 +0800 Subject: [PATCH 1/8] use mnesia:select instead of find/7 for boss_db:count/3 --- src/db_adapters/boss_db_adapter_mnesia.erl | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/db_adapters/boss_db_adapter_mnesia.erl b/src/db_adapters/boss_db_adapter_mnesia.erl index 131c3a44..b7b5a2d0 100644 --- a/src/db_adapters/boss_db_adapter_mnesia.erl +++ b/src/db_adapters/boss_db_adapter_mnesia.erl @@ -157,8 +157,18 @@ test_rec(Rec,{Key, 'contains_none', Values}) when is_list(Values) -> lists:any(fun (Ele) -> not lists:member(Ele, apply(Rec,Key,[])) end, Values). % ----- -count(Conn, Type, Conditions) -> - length(find(Conn, Type, Conditions, all, 0, id, ascending)). + +count(_, Type, Conditions) -> + {Pattern, _} = build_query(Type, Conditions), + MatchSpec = [{list_to_tuple([Type|Pattern]), [], [1]}], + FunCount = fun() -> mnesia:select(Type, MatchSpec) end, + case mnesia:transaction(FunCount) of + {atomic, Result} -> lists:sum(Result); + {aborted, Err} -> Err + end. + +%% count(Conn, Type, Conditions) -> +%% length(find(Conn, Type, Conditions, all, 0, id, ascending)). % ----- counter(Conn, Id) when is_list(Id) -> From b800ee41b5920981f92c96059a5235fa0aa64ffb Mon Sep 17 00:00:00 2001 From: chan Date: Mon, 21 Jul 2014 16:40:16 +0800 Subject: [PATCH 2/8] add boss_db:paginate/{3,4} --- src/boss_db.erl | 11 +++++++++++ src/boss_db_adapter.erl | 3 ++- src/boss_db_controller.erl | 3 +++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/boss_db.erl b/src/boss_db.erl index f13f9083..5345512f 100644 --- a/src/boss_db.erl +++ b/src/boss_db.erl @@ -5,6 +5,8 @@ -export([start/1, stop/0]). -export([ + paginate/3, + paginate/4, migrate/1, migrate/2, find/1, @@ -140,6 +142,15 @@ migrate({Tag, Fun}, Direction) -> Fun(Direction), db_call({migration_done, Tag, Direction}). + +%% {page_num, PageNum}, {default_page_size, DefaultPageSize} {page_size, PageSize} to control which page to fetch, +%% and how many results per page. Page size defaults to 10. +paginate(Type, Conditions, Opts) -> + paginate(Type, Conditions, Opts, ?DEFAULT_TIMEOUT). + +paginate(Type, Conditions, Opts, Timeout) -> + db_call({paginate, Type, Conditions, Opts}, Timeout). + %% @spec find(Id::string()) -> Value | {error, Reason} %% @doc Find a BossRecord with the specified `Id' (e.g. "employee-42") or a value described %% by a dot-separated path (e.g. "employee-42.manager.name"). diff --git a/src/boss_db_adapter.erl b/src/boss_db_adapter.erl index c193d970..8e28fe6d 100644 --- a/src/boss_db_adapter.erl +++ b/src/boss_db_adapter.erl @@ -6,7 +6,8 @@ behaviour_info(callbacks) -> [ {start, 1}, {stop, 0}, {init, 1}, {terminate, 1}, {find, 2}, {find, 7}, {count, 3}, - {delete, 2}, {counter, 2}, {incr, 3}, {save_record, 2} + {delete, 2}, {counter, 2}, {incr, 3}, {save_record, 2}, + {paginate, 4} ]; behaviour_info(_Other) -> undefined. diff --git a/src/boss_db_controller.erl b/src/boss_db_controller.erl index e3e3b576..00fdd864 100644 --- a/src/boss_db_controller.erl +++ b/src/boss_db_controller.erl @@ -99,6 +99,9 @@ init(Options) -> handle_call(_Anything, _Anyone, State) when State#state.connection_state /= connected -> {reply, db_connection_down, State}; +handle_call({paginate, Type, Conditions, Opts}, _From, #state{ cache_enable = false } = State) -> + {Adapter, Conn, _} = db_for_type(Type, State), + {reply, Adapter:paginate(Conn, Type, Conditions, Opts), State}; handle_call({find, Key}, From, #state{ cache_enable = true, cache_prefix = Prefix } = State) -> CacheResult = boss_cache:get(Prefix, Key), find_by_key(Key, From, Prefix, State, CacheResult); From ce6f961718667ba205e2403b3f408b938a64f17a Mon Sep 17 00:00:00 2001 From: chan Date: Mon, 21 Jul 2014 16:41:43 +0800 Subject: [PATCH 3/8] add paginate to mnesia driver --- src/db_adapters/boss_db_adapter_mnesia.erl | 45 ++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/db_adapters/boss_db_adapter_mnesia.erl b/src/db_adapters/boss_db_adapter_mnesia.erl index b7b5a2d0..43d08657 100644 --- a/src/db_adapters/boss_db_adapter_mnesia.erl +++ b/src/db_adapters/boss_db_adapter_mnesia.erl @@ -5,6 +5,7 @@ -export([transaction/2]). -export([table_exists/2, get_migrations_table/1, migration_done/3]). +-define(DEFAULT_PAGE_SIZE, 10). %-define(TRILLION, (1000 * 1000 * 1000 * 1000)). start(_) -> @@ -22,6 +23,7 @@ init(_Options) -> terminate(_) -> ok. + % ----- find(_, Id) when is_list(Id) -> Type = infer_type_from_id(Id), @@ -264,6 +266,8 @@ infer_type_from_id(Id) when is_list(Id) -> list_to_atom(hd(string:tokens(Id, "-"))). %----- +build_query(Type, Conditions) -> + build_query(Type, Conditions, none, none, none, none). build_query(Type, Conditions, _Max, _Skip, _Sort, _SortOrder) -> % a Query is a {Pattern, Filter} combo Fldnames = mnesia:table_info(Type, attributes), BlankPattern = [ {Fld, '_'} || Fld <- Fldnames], @@ -283,3 +287,44 @@ build_conditions1([First|Rest], Pattern, Filter) -> build_conditions1(Rest, Pattern, [First|Filter]). +limit (Tab, Offset, Number, MatchSpec) -> + Fun = fun() -> + seek (Offset, + Number, + mnesia:select (Tab, + MatchSpec, + Number, + read) + ) + end, + mnesia:transaction(Fun). + + +seek (_Offset, _Number, '$end_of_table') -> + []; +seek (Offset, Number, X) when Offset =< 0 -> + read (Number, X, []); +seek (Offset, Number, { Results, Cont }) -> + NumResults = length (Results), + case Offset > NumResults of + true -> + seek (Offset - NumResults, Number, mnesia:select (Cont)); + false -> + { _, DontDrop } = lists:split (Offset, Results), + Keep = lists:sublist (DontDrop, Number), + read (Number - length (Keep), mnesia:select (Cont), [ Keep ]) + end. + +read (Number, _, Acc) when Number =< 0 -> + lists:foldl (fun erlang:'++'/2, [], Acc); +read (_Number, '$end_of_table', Acc) -> + lists:foldl (fun erlang:'++'/2, [], Acc); +read (Number, { Results, Cont }, Acc) -> + NumResults = length (Results), + case Number > NumResults of + true -> + read (Number - NumResults, mnesia:select (Cont), [ Results | Acc ]); + false -> + { Keep, _ } = lists:split (Number, Results), + lists:foldl (fun erlang:'++'/2, Keep, Acc) + end. From 7ee7319279f084a1292a9bc60b871e9ee10ac45a Mon Sep 17 00:00:00 2001 From: chan Date: Mon, 21 Jul 2014 17:00:18 +0800 Subject: [PATCH 4/8] add paginate function --- src/db_adapters/boss_db_adapter_mnesia.erl | 23 ++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/db_adapters/boss_db_adapter_mnesia.erl b/src/db_adapters/boss_db_adapter_mnesia.erl index 43d08657..672ba01d 100644 --- a/src/db_adapters/boss_db_adapter_mnesia.erl +++ b/src/db_adapters/boss_db_adapter_mnesia.erl @@ -4,6 +4,7 @@ -export([count/3, counter/2, incr/3, delete/2, save_record/2]). -export([transaction/2]). -export([table_exists/2, get_migrations_table/1, migration_done/3]). +-export([paginate/4]). -define(DEFAULT_PAGE_SIZE, 10). %-define(TRILLION, (1000 * 1000 * 1000 * 1000)). @@ -23,6 +24,28 @@ init(_Options) -> terminate(_) -> ok. +%% @spec paginate( Model::atom(), Conditions, Opts ) -> Value | {error, Reason} +%% @doc Paginate through the results matching the conditions. Use `Opts' {page, +%% PageNum} and {page_size, PageSize} to control which page to fetch, +%% and how many results per page. Page size defaults to 10. +paginate(_, Model, Conditions, Opts) -> + {Pattern, _Filter} = build_query(Model, Conditions), %% don't know if _Filter is usefull here ?? + Return = proplists:get_value(return, Opts, all), + Page = proplists:get_value(page, Opts, 1), + PageSize = proplists:get_value(page_size, Opts, ?DEFAULT_PAGE_SIZE), + Offset = PageSize * (Page - 1), + Total = boss_db:count(Model, Conditions), + TotalPages = (Total div PageSize) + (case Total rem PageSize of + 0 -> 0; + _ -> 1 + end), + MatchSpec = case Return of + all -> [{list_to_tuple([Model|Pattern]), [], ['$_']}]; + id -> [_|T] = Pattern, + [{list_to_tuple([Model, '$1'|T]), [], ['$1']}] + end, + {atomic, Result} = limit(Model, Offset, PageSize, MatchSpec), + {Page, TotalPages, Result}. % ----- find(_, Id) when is_list(Id) -> From 54cd1570b3987db72c67987f25dcd54f61239af0 Mon Sep 17 00:00:00 2001 From: chan Date: Wed, 23 Jul 2014 11:35:10 +0800 Subject: [PATCH 5/8] add doc --- src/boss_db.erl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/boss_db.erl b/src/boss_db.erl index 5345512f..f80f53a9 100644 --- a/src/boss_db.erl +++ b/src/boss_db.erl @@ -143,8 +143,10 @@ migrate({Tag, Fun}, Direction) -> db_call({migration_done, Tag, Direction}). -%% {page_num, PageNum}, {default_page_size, DefaultPageSize} {page_size, PageSize} to control which page to fetch, -%% and how many results per page. Page size defaults to 10. +%% @spec paginate(Model::atom(), Conditions, Opts::proplists()) -> {Page::integer(), TotalPage::integer(), Total::integer(), Result::list()] | {error, Reason} +%% @doc Paginate through the results matching the conditions. Use `Opts' [{page, +%% PageNum}, {page_size, PageSize}] to control which page to fetch, +%% and how many results per page. Page size defaults to 10. paginate(Type, Conditions, Opts) -> paginate(Type, Conditions, Opts, ?DEFAULT_TIMEOUT). From c54961475b3d1598909b2e22a6447cb186b423c4 Mon Sep 17 00:00:00 2001 From: chan Date: Wed, 23 Jul 2014 11:37:25 +0800 Subject: [PATCH 6/8] return total item, matching condition --- src/db_adapters/boss_db_adapter_mnesia.erl | 32 ++++++++++------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/src/db_adapters/boss_db_adapter_mnesia.erl b/src/db_adapters/boss_db_adapter_mnesia.erl index 672ba01d..56aada84 100644 --- a/src/db_adapters/boss_db_adapter_mnesia.erl +++ b/src/db_adapters/boss_db_adapter_mnesia.erl @@ -24,29 +24,25 @@ init(_Options) -> terminate(_) -> ok. -%% @spec paginate( Model::atom(), Conditions, Opts ) -> Value | {error, Reason} -%% @doc Paginate through the results matching the conditions. Use `Opts' {page, -%% PageNum} and {page_size, PageSize} to control which page to fetch, -%% and how many results per page. Page size defaults to 10. paginate(_, Model, Conditions, Opts) -> {Pattern, _Filter} = build_query(Model, Conditions), %% don't know if _Filter is usefull here ?? - Return = proplists:get_value(return, Opts, all), - Page = proplists:get_value(page, Opts, 1), - PageSize = proplists:get_value(page_size, Opts, ?DEFAULT_PAGE_SIZE), - Offset = PageSize * (Page - 1), - Total = boss_db:count(Model, Conditions), + Page = proplists:get_value(page, Opts, 1), + PageSize = proplists:get_value(page_size, Opts, ?DEFAULT_PAGE_SIZE), + Offset = PageSize * (Page - 1), + Total = boss_db:count(Model, Conditions), TotalPages = (Total div PageSize) + (case Total rem PageSize of 0 -> 0; _ -> 1 end), - MatchSpec = case Return of - all -> [{list_to_tuple([Model|Pattern]), [], ['$_']}]; - id -> [_|T] = Pattern, - [{list_to_tuple([Model, '$1'|T]), [], ['$1']}] - end, - {atomic, Result} = limit(Model, Offset, PageSize, MatchSpec), - {Page, TotalPages, Result}. - + MatchSpec = [{list_to_tuple([Model|Pattern]), [], ['$_']}], + case limit(Model, Offset, PageSize, MatchSpec) of + {atomic, Result} -> + {Page, TotalPages, Total, Result}; + + {aborted, Reason} -> + {error, Reason} + end, + % ----- find(_, Id) when is_list(Id) -> Type = infer_type_from_id(Id), @@ -189,7 +185,7 @@ count(_, Type, Conditions) -> FunCount = fun() -> mnesia:select(Type, MatchSpec) end, case mnesia:transaction(FunCount) of {atomic, Result} -> lists:sum(Result); - {aborted, Err} -> Err + {aborted, Err} -> Err end. %% count(Conn, Type, Conditions) -> From 3b7590257d12b005cc8a1df1ca74a5a34038e3b9 Mon Sep 17 00:00:00 2001 From: chan Date: Wed, 23 Jul 2014 11:52:30 +0800 Subject: [PATCH 7/8] return {error, Reason} now for count/3 --- src/db_adapters/boss_db_adapter_mnesia.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db_adapters/boss_db_adapter_mnesia.erl b/src/db_adapters/boss_db_adapter_mnesia.erl index 56aada84..24e579c7 100644 --- a/src/db_adapters/boss_db_adapter_mnesia.erl +++ b/src/db_adapters/boss_db_adapter_mnesia.erl @@ -185,7 +185,7 @@ count(_, Type, Conditions) -> FunCount = fun() -> mnesia:select(Type, MatchSpec) end, case mnesia:transaction(FunCount) of {atomic, Result} -> lists:sum(Result); - {aborted, Err} -> Err + {aborted, Reason} -> {error, Reason} end. %% count(Conn, Type, Conditions) -> From 07a7f150435baca9e66c76b0aeae2a08bf2d0a73 Mon Sep 17 00:00:00 2001 From: mihawk Date: Mon, 17 Aug 2015 11:45:06 +0800 Subject: [PATCH 8/8] fix syntax error --- src/db_adapters/boss_db_adapter_mnesia.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db_adapters/boss_db_adapter_mnesia.erl b/src/db_adapters/boss_db_adapter_mnesia.erl index 24e579c7..92d773d0 100644 --- a/src/db_adapters/boss_db_adapter_mnesia.erl +++ b/src/db_adapters/boss_db_adapter_mnesia.erl @@ -41,7 +41,7 @@ paginate(_, Model, Conditions, Opts) -> {aborted, Reason} -> {error, Reason} - end, + end. % ----- find(_, Id) when is_list(Id) ->