Skip to content

Commit b9b280d

Browse files
committed
Support for pre-caching belongs_to associations
1 parent 8f589ef commit b9b280d

File tree

3 files changed

+45
-26
lines changed

3 files changed

+45
-26
lines changed

src/boss_db.erl

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
find_first/3,
1313
find_last/2,
1414
find_last/3,
15-
get/1,
1615
count/1,
1716
count/2,
1817
counter/1,
@@ -65,25 +64,20 @@ db_call(Msg) ->
6564
Reply
6665
end.
6766

68-
%% @spec find(Id::string()) -> BossRecord | {error, Reason}
69-
%% @doc Find a BossRecord with the specified `Id'.
67+
%% @spec find(Id::string()) -> Value | {error, Reason}
68+
%% @doc Find a BossRecord with the specified `Id' (e.g. "employee-42") or a value described
69+
%% by a dot-separated path (e.g. "employee-42.manager.name").
7070
find("") -> undefined;
7171
find(Key) when is_list(Key) ->
72-
db_call({find, Key});
72+
[IdToken|Rest] = string:tokens(Key, "."),
73+
case db_call({find, IdToken}) of
74+
undefined -> undefined;
75+
{error, Reason} -> {error, Reason};
76+
BossRecord -> BossRecord:get(string:join(Rest, "."))
77+
end;
7378
find(_) ->
7479
{error, invalid_id}.
7580

76-
%% @spec get(Path::string()) -> Value | {error, Reason} | undefined
77-
%% @doc Find a BossRecord or value described by the dot-separated `Path' (e.g., "employee-42.manager.name").
78-
get(Path) when is_list(Path) ->
79-
[IdToken|Rest] = string:tokens(Path, "."),
80-
case find(IdToken) of
81-
{error, Reason} ->
82-
{error, Reason};
83-
BossRecord ->
84-
BossRecord:get(string:join(Rest, "."))
85-
end.
86-
8781
%% @spec find(Type::atom(), Conditions) -> [ BossRecord ]
8882
%% @doc Query for BossRecords. Returns all BossRecords of type
8983
%% `Type' matching all of the given `Conditions'
@@ -94,8 +88,9 @@ find(Type, Conditions) ->
9488
%% @doc Query for BossRecords. Returns BossRecords of type
9589
%% `Type' matching all of the given `Conditions'. Options may include
9690
%% `limit' (maximum number of records to return), `offset' (number of records
97-
%% to skip), `order_by' (attribute to sort on), and `descending' (whether to
98-
%% sort the values from highest to lowest)
91+
%% to skip), `order_by' (attribute to sort on), `descending' (whether to
92+
%% sort the values from highest to lowest), and `include' (list of belongs_to
93+
%% associations to pre-cache)
9994
find(Type, Conditions, Options) ->
10095
Max = proplists:get_value(limit, Options, all),
10196
Skip = proplists:get_value(offset, Options, 0),
@@ -104,7 +99,8 @@ find(Type, Conditions, Options) ->
10499
true -> descending;
105100
_ -> ascending
106101
end,
107-
db_call({find, Type, normalize_conditions(Conditions), Max, Skip, Sort, SortOrder}).
102+
Include = proplists:get_value(include, Options, []),
103+
db_call({find, Type, normalize_conditions(Conditions), Max, Skip, Sort, SortOrder, Include}).
108104

109105
%% @spec find_first( Type::atom(), Conditions ) -> Record | undefined
110106
%% @doc Query for the first BossRecord of type `Type' matching all of the given `Conditions'

src/boss_db_controller.erl

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,14 +71,32 @@ handle_call({find, Key}, _From, #state{ cache_enable = false } = State) ->
7171
{Adapter, Conn} = db_for_key(Key, State),
7272
{reply, Adapter:find(Conn, Key), State};
7373

74-
handle_call({find, Type, Conditions, Max, Skip, Sort, SortOrder} = Cmd, From,
74+
handle_call({find, Type, Conditions, Max, Skip, Sort, SortOrder, Include} = Cmd, From,
7575
#state{ cache_enable = true, cache_prefix = Prefix } = State) ->
7676
Key = {Type, Conditions, Max, Skip, Sort, SortOrder},
7777
case boss_cache:get(Prefix, Key) of
7878
undefined ->
7979
{reply, Res, _} = handle_call(Cmd, From, State#state{ cache_enable = false }),
8080
case is_list(Res) of
8181
true ->
82+
DummyRecord = boss_record_lib:dummy_record(Type),
83+
BelongsToNames = DummyRecord:belongs_to_names(),
84+
IncludedRecords = lists:foldl(fun
85+
({RelationshipName, InnerInclude}, Acc) ->
86+
RecordList = case lists:member(RelationshipName, BelongsToNames) of
87+
true ->
88+
IdList = lists:map(fun(Record) -> Record:id() end, Res),
89+
handle_call({find, RelationshipName,
90+
[{'id', 'in', IdList}], all, 0, id, ascending,
91+
InnerInclude}, From, State);
92+
_ ->
93+
[]
94+
end,
95+
RecordList ++ Acc
96+
end, [], lists:map(fun({R, I}) -> {R, I}; (R) -> {R, []} end, Include)),
97+
lists:map(fun(Rec) ->
98+
boss_cache:set(Prefix, Rec:id(), Rec, State#state.cache_ttl)
99+
end, IncludedRecords),
82100
boss_cache:set(Prefix, Key, Res, State#state.cache_ttl),
83101
WatchString = lists:concat([inflector:pluralize(atom_to_list(Type)), ", ", Type, "-*.*"]),
84102
boss_news:set_watch(Key, WatchString, fun boss_db_cache:handle_collection_news/3,
@@ -90,7 +108,7 @@ handle_call({find, Type, Conditions, Max, Skip, Sort, SortOrder} = Cmd, From,
90108
boss_news:extend_watch(Key),
91109
{reply, CachedValue, State}
92110
end;
93-
handle_call({find, Type, Conditions, Max, Skip, Sort, SortOrder}, _From, #state{ cache_enable = false } = State) ->
111+
handle_call({find, Type, Conditions, Max, Skip, Sort, SortOrder, _}, _From, #state{ cache_enable = false } = State) ->
94112
{Adapter, Conn} = db_for_type(Type, State),
95113
{reply, Adapter:find(Conn, Type, Conditions, Max, Skip, Sort, SortOrder), State};
96114

src/boss_record_compiler.erl

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -379,14 +379,15 @@ attribute_names_forms(ModuleName, Parameters) ->
379379
has_one_forms(HasOne, ModuleName, Opts) ->
380380
Type = proplists:get_value(module, Opts, HasOne),
381381
ForeignKey = proplists:get_value(foreign_key, Opts, atom_to_list(ModuleName) ++ "_id"),
382+
Include = proplists:get_value(include, Opts, []),
382383
[erl_syntax:add_precomments([erl_syntax:comment(
383384
[lists:concat(["% @spec ", HasOne, "() -> ", Type, " | undefined"]),
384385
lists:concat(["% @doc Retrieves the `", Type, "' with `", ForeignKey, "' ",
385386
"set to the `Id' of this `", ModuleName, "'"])])],
386387
erl_syntax:function(erl_syntax:atom(HasOne),
387388
[erl_syntax:clause([], none, [
388389
first_or_undefined_forms(
389-
has_many_application_forms(Type, ForeignKey, 1, id, false)
390+
has_many_application_forms(Type, ForeignKey, 1, id, false, Include)
390391
)
391392
])]))
392393
].
@@ -398,6 +399,7 @@ has_many_forms(HasMany, ModuleName, Limit, Opts) ->
398399
IsDescending = proplists:get_value(descending, Opts, false),
399400
Singular = inflector:singularize(atom_to_list(HasMany)),
400401
Type = proplists:get_value(module, Opts, Singular),
402+
Include = proplists:get_value(include, Opts, []),
401403
ForeignKey = proplists:get_value(foreign_key, Opts, atom_to_list(ModuleName) ++ "_id"),
402404
[erl_syntax:add_precomments([erl_syntax:comment(
403405
[
@@ -406,7 +408,7 @@ has_many_forms(HasMany, ModuleName, Limit, Opts) ->
406408
"set to the `Id' of this `", ModuleName, "'"])])],
407409
erl_syntax:function(erl_syntax:atom(HasMany),
408410
[erl_syntax:clause([], none, [
409-
has_many_application_forms(Type, ForeignKey, Limit, Sort, IsDescending)
411+
has_many_application_forms(Type, ForeignKey, Limit, Sort, IsDescending, Include)
410412
])])),
411413
erl_syntax:add_precomments([erl_syntax:comment(
412414
[
@@ -416,7 +418,7 @@ has_many_forms(HasMany, ModuleName, Limit, Opts) ->
416418
erl_syntax:function(erl_syntax:atom("first_"++Singular),
417419
[erl_syntax:clause([], none, [
418420
first_or_undefined_forms(
419-
has_many_application_forms(Type, ForeignKey, 1, Sort, IsDescending)
421+
has_many_application_forms(Type, ForeignKey, 1, Sort, IsDescending, Include)
420422
)
421423
])])),
422424
erl_syntax:add_precomments([erl_syntax:comment(
@@ -427,7 +429,7 @@ has_many_forms(HasMany, ModuleName, Limit, Opts) ->
427429
erl_syntax:function(erl_syntax:atom("last_"++Singular),
428430
[erl_syntax:clause([], none, [
429431
first_or_undefined_forms(
430-
has_many_application_forms(Type, ForeignKey, 1, Sort, not IsDescending)
432+
has_many_application_forms(Type, ForeignKey, 1, Sort, not IsDescending, Include)
431433
)
432434
])]))
433435
].
@@ -438,7 +440,7 @@ first_or_undefined_forms(Forms) ->
438440
[erl_syntax:variable(?PREFIX++"Record")]),
439441
erl_syntax:clause([erl_syntax:underscore()], none, [erl_syntax:atom(undefined)])]).
440442

441-
has_many_application_forms(Type, ForeignKey, Limit, Sort, IsDescending) ->
443+
has_many_application_forms(Type, ForeignKey, Limit, Sort, IsDescending, Include) ->
442444
erl_syntax:application(
443445
erl_syntax:atom(?DATABASE_MODULE),
444446
erl_syntax:atom(find),
@@ -457,7 +459,10 @@ has_many_application_forms(Type, ForeignKey, Limit, Sort, IsDescending) ->
457459
erl_syntax:atom(Sort)]),
458460
erl_syntax:tuple([
459461
erl_syntax:atom(descending),
460-
erl_syntax:atom(IsDescending)])
462+
erl_syntax:atom(IsDescending)]),
463+
erl_syntax:tuple([
464+
erl_syntax:atom(include),
465+
erl_syntax:list(lists:map(fun erl_syntax:atom/1, Include))])
461466
])
462467
]).
463468

0 commit comments

Comments
 (0)