src/core/core_booking.erl View File
| 33 | },
|
| 34 | F = fun() -> mnesia:write(Booking), {ok, Booking} end,
|
| 35 | case mnesia:transaction(F) of
|
| 36 | {atomic, Result} -> Result;
|
| 45 | get_by_id(Id) ->
|
| 46 | case mnesia:dirty_read(booking, Id) of
|
| 47 | [] -> {error, not_found};
|
| 57 | %% Optimized: use event_id index instead of full table scan.
|
| 58 | Bookings = mnesia:dirty_index_read(booking, EventId, #booking.event_id),
|
| 59 | {ok, Bookings}.
|
| 67 | %% Optimized: use user_id index instead of full table scan.
|
| 68 | Bookings = mnesia:dirty_index_read(booking, UserId, #booking.user_id),
|
| 69 | {ok, Bookings}.
|
| 77 | %% Genuinely lists all bookings (admin view); no index can help here.
|
| 78 | mnesia:dirty_match_object(#booking{_ = '_'}).
|
| 79 |
|
| 87 | F = fun() ->
|
| 88 | case mnesia:read(booking, Id) of
|
| 89 | [] -> {error, not_found};
|
| 91 | UpdatedBooking = apply_updates(Booking, Updates),
|
| 92 | mnesia:write(UpdatedBooking),
|
| 93 | {ok, UpdatedBooking}
|
| 95 | end,
|
| 96 | case mnesia:transaction(F) of
|
| 97 | {atomic, Result} -> Result;
|
| 106 | delete(Id) ->
|
| 107 | case mnesia:dirty_read(booking, Id) of
|
| 108 | [] -> {error, not_found};
|
| 109 | [_] -> mnesia:dirty_delete(booking, Id), ok
|
| 110 | end.
|
| 117 | count_bookings() ->
|
| 118 | mnesia:table_info(booking, size).
|
| 119 |
|
| 128 | %% instead of a full table scan.
|
| 129 | Candidates = mnesia:dirty_index_read(booking, EventId, #booking.event_id),
|
| 130 | case [B || B <- Candidates, B#booking.user_id =:= UserId] of |
src/core/core_calendar.erl View File
| 64 | },
|
| 65 | F = fun() -> mnesia:write(Calendar), {ok, Calendar} end,
|
| 66 | case mnesia:transaction(F) of
|
| 67 | {atomic, Result} -> Result;
|
| 76 | get_by_id(Id) ->
|
| 77 | case mnesia:dirty_read(calendar, Id) of
|
| 78 | [] -> {error, not_found};
|
| 86 | get_by_short_name(Name) when is_binary(Name) ->
|
| 87 | case mnesia:dirty_index_read(calendar, Name, #calendar.short_name) of
|
| 88 | [] ->
|
| 110 | Match = #calendar{owner_id = OwnerId, status = active, _ = '_'},
|
| 111 | Calendars = mnesia:dirty_match_object(Match),
|
| 112 | {ok, Calendars}.
|
| 123 | F = fun() ->
|
| 124 | case mnesia:read(calendar, Id) of
|
| 125 | [] -> {error, not_found};
|
| 127 | UpdatedCalendar = apply_updates(Calendar, Updates),
|
| 128 | mnesia:write(UpdatedCalendar),
|
| 129 | {ok, UpdatedCalendar}
|
| 131 | end,
|
| 132 | case mnesia:transaction(F) of
|
| 133 | {atomic, Result} -> Result;
|
| 150 | count_calendars() ->
|
| 151 | mnesia:table_info(calendar, size).
|
| 152 |
|
| 158 | list_all() ->
|
| 159 | {atomic, Calendars} = mnesia:transaction(fun() ->
|
| 160 | mnesia:foldl(fun(C, Acc) -> [C | Acc] end, [], calendar)
|
| 161 | end),
|
| 189 | core_stats:with_daily(calendars_created, From, To, fun() ->
|
| 190 | All = mnesia:dirty_match_object(#calendar{_ = '_'}),
|
| 191 | Filtered = lists:filter(fun(C) -> C#calendar.created_at >= From andalso
|
| 211 | core_stats:with_dim(calendar, type, fun() ->
|
| 212 | Calendars = mnesia:dirty_match_object(#calendar{_ = '_'}),
|
| 213 | lists:foldl(fun(#calendar{type = Type}, Acc) ->
|
| 227 | core_stats:with_dim(calendar, status, fun() ->
|
| 228 | Calendars = mnesia:dirty_match_object(#calendar{_ = '_'}),
|
| 229 | lists:foldl(fun(#calendar{status = Status}, Acc) ->
|
| 245 | {error, _} ->
|
| 246 | Calendars = mnesia:dirty_match_object(#calendar{_ = '_'}),
|
| 247 | Sorted = lists:reverse(lists:sort(
|
| 279 | {error, _} ->
|
| 280 | Calendars = mnesia:dirty_match_object(#calendar{_ = '_'}),
|
| 281 | Sorted = lists:reverse(lists:sort(
|
| 288 | top_by_review_filter(N, FilterFun) ->
|
| 289 | Reviews = mnesia:dirty_match_object(#review{target_type = calendar, _ = '_'}),
|
| 290 | Filtered = lists:filter(FilterFun, Reviews), |
src/core/core_calendar_follow.erl View File
| 29 | },
|
| 30 | mnesia:write(Rec),
|
| 31 | {ok, Rec}
|
| 33 | end,
|
| 34 | case mnesia:transaction(F) of
|
| 35 | {atomic, Result} -> Result;
|
| 49 | [#calendar_follow{id = Id}] ->
|
| 50 | mnesia:delete({calendar_follow, Id}),
|
| 51 | ok
|
| 53 | end,
|
| 54 | case mnesia:transaction(F) of
|
| 55 | {atomic, ok} -> ok;
|
| 64 | is_following(UserId, CalendarId) ->
|
| 65 | case mnesia:dirty_match_object(
|
| 66 | #calendar_follow{calendar_id = CalendarId, user_id = UserId, _ = '_'}) of
|
| 76 | list_by_user(UserId) ->
|
| 77 | mnesia:dirty_match_object(#calendar_follow{user_id = UserId, _ = '_'}).
|
| 78 |
|
| 84 | list_by_calendar(CalendarId) ->
|
| 85 | mnesia:dirty_match_object(#calendar_follow{calendar_id = CalendarId, _ = '_'}).
|
| 86 |
|
| 95 | fun(#calendar_follow{id = Id}) ->
|
| 96 | mnesia:delete({calendar_follow, Id})
|
| 97 | end,
|
| 98 | mnesia:match_object(#calendar_follow{calendar_id = CalendarId, _ = '_'})),
|
| 99 | ok
|
| 100 | end,
|
| 101 | case mnesia:transaction(F) of
|
| 102 | {atomic, ok} -> ok;
|
| 110 | find(CalendarId, UserId) ->
|
| 111 | mnesia:match_object(#calendar_follow{calendar_id = CalendarId, user_id = UserId, _ = '_'}). |
src/core/core_calendar_share.erl View File
| 21 | },
|
| 22 | mnesia:write(Updated),
|
| 23 | {ok, Updated};
|
| 31 | },
|
| 32 | mnesia:write(Rec),
|
| 33 | {ok, Rec}
|
| 35 | end,
|
| 36 | case mnesia:transaction(F) of
|
| 37 | {atomic, Result} -> Result;
|
| 54 | try
|
| 55 | mnesia:dirty_match_object(#calendar_share{calendar_id = CalendarId, _ = '_'})
|
| 56 | catch
|
| 63 | try
|
| 64 | mnesia:dirty_match_object(#calendar_share{user_id = UserId, _ = '_'})
|
| 65 | catch
|
| 75 | [#calendar_share{id = Id}] ->
|
| 76 | mnesia:delete({calendar_share, Id}),
|
| 77 | ok
|
| 79 | end,
|
| 80 | case mnesia:transaction(F) of
|
| 81 | {atomic, Result} -> Result;
|
| 93 | Updated = Rec#calendar_share{rights = Rights},
|
| 94 | mnesia:write(Updated),
|
| 95 | {ok, Updated}
|
| 97 | end,
|
| 98 | case mnesia:transaction(F) of
|
| 99 | {atomic, Result} -> Result;
|
| 110 | Updated = Rec#calendar_share{mirror_to_default = Mirror},
|
| 111 | mnesia:write(Updated),
|
| 112 | {ok, Updated}
|
| 114 | end,
|
| 115 | case mnesia:transaction(F) of
|
| 116 | {atomic, Result} -> Result;
|
| 120 | find(CalendarId, UserId) ->
|
| 121 | mnesia:match_object(#calendar_share{calendar_id = CalendarId, user_id = UserId, _ = '_'}).
|
| 122 |
|
| 123 | find_dirty(CalendarId, UserId) ->
|
| 124 | mnesia:dirty_match_object(
|
| 125 | #calendar_share{calendar_id = CalendarId, user_id = UserId, _ = '_'}). |
src/core/core_calendar_share_invite.erl View File
| 15 | F = fun() ->
|
| 16 | mnesia:write(Rec),
|
| 17 | {ok, Rec}
|
| 18 | end,
|
| 19 | case mnesia:transaction(F) of
|
| 20 | {atomic, Result} -> Result;
|
| 25 | write(Rec) ->
|
| 26 | mnesia:dirty_write(Rec).
|
| 27 |
|
| 30 | get_by_id(Id) ->
|
| 31 | case mnesia:dirty_read(calendar_share_invite, Id) of
|
| 32 | [Rec] -> {ok, Rec};
|
| 38 | get_by_token(Token) ->
|
| 39 | case mnesia:dirty_index_read(calendar_share_invite, Token,
|
| 40 | #calendar_share_invite.token) of
|
| 47 | list_by_calendar(CalendarId) ->
|
| 48 | mnesia:dirty_match_object(
|
| 49 | #calendar_share_invite{calendar_id = CalendarId, _ = '_'}).
|
| 52 | list_by_invitee(UserId) ->
|
| 53 | mnesia:dirty_match_object(
|
| 54 | #calendar_share_invite{invitee_user_id = UserId, _ = '_'}).
|
| 57 | list_by_email(Email) ->
|
| 58 | mnesia:dirty_match_object(
|
| 59 | #calendar_share_invite{invitee_email = Email, _ = '_'}).
|
| 64 | F = fun() ->
|
| 65 | case mnesia:read(calendar_share_invite, Id) of
|
| 66 | [] ->
|
| 69 | Updated = Rec#calendar_share_invite{status = Status},
|
| 70 | mnesia:write(Updated),
|
| 71 | {ok, Updated}
|
| 73 | end,
|
| 74 | case mnesia:transaction(F) of
|
| 75 | {atomic, Result} -> Result; |
src/core/core_calendar_specialist.erl View File
| 30 | },
|
| 31 | mnesia:write(Rec),
|
| 32 | {ok, Rec}
|
| 34 | end,
|
| 35 | case mnesia:transaction(F) of
|
| 36 | {atomic, Result} -> Result;
|
| 42 | get_by_calendar_and_user(CalendarId, UserId) ->
|
| 43 | case mnesia:dirty_match_object(
|
| 44 | #calendar_specialist{calendar_id = CalendarId, user_id = UserId, _ = '_'}) of
|
| 51 | list_by_calendar(CalendarId) ->
|
| 52 | mnesia:dirty_match_object(#calendar_specialist{calendar_id = CalendarId, _ = '_'}).
|
| 53 |
|
| 55 | list_by_user(UserId) ->
|
| 56 | mnesia:dirty_match_object(#calendar_specialist{user_id = UserId, _ = '_'}).
|
| 57 |
|
| 66 | Updated = apply_updates(Rec, Updates),
|
| 67 | mnesia:write(Updated),
|
| 68 | {ok, Updated}
|
| 70 | end,
|
| 71 | case mnesia:transaction(F) of
|
| 72 | {atomic, Result} -> Result;
|
| 82 | [#calendar_specialist{id = Id}] ->
|
| 83 | mnesia:delete({calendar_specialist, Id}),
|
| 84 | ok
|
| 86 | end,
|
| 87 | case mnesia:transaction(F) of
|
| 88 | {atomic, Result} -> Result;
|
| 103 | find(CalendarId, UserId) ->
|
| 104 | mnesia:match_object(
|
| 105 | #calendar_specialist{calendar_id = CalendarId, user_id = UserId, _ = '_'}). |
src/core/core_event.erl View File
| 60 | },
|
| 61 | F = fun() -> mnesia:write(Event), {ok, Event} end,
|
| 62 | case mnesia:transaction(F) of
|
| 63 | {atomic, Result} -> Result;
|
| 102 | },
|
| 103 | F = fun() -> mnesia:write(Event), {ok, Event} end,
|
| 104 | case mnesia:transaction(F) of
|
| 105 | {atomic, Result} -> Result;
|
| 117 | materialize_occurrence(MasterId, OccurrenceStart, SpecialistId) ->
|
| 118 | case mnesia:dirty_read(event, MasterId) of
|
| 119 | [] ->
|
| 123 | %% then filter by start_time and is_instance.
|
| 124 | Candidates = mnesia:dirty_index_read(event, MasterId, #event.master_id),
|
| 125 | Existing = [E || E <- Candidates,
|
| 156 | },
|
| 157 | F = fun() -> mnesia:write(Instance), {ok, Instance} end,
|
| 158 | case mnesia:transaction(F) of
|
| 159 | {atomic, Result} -> Result;
|
| 174 | get_by_id(Id) ->
|
| 175 | case mnesia:dirty_read(event, Id) of
|
| 176 | [] -> {error, not_found};
|
| 187 | %% then filter by status and is_instance.
|
| 188 | Candidates = mnesia:dirty_index_read(event, CalendarId, #event.calendar_id),
|
| 189 | Events = [E || E <- Candidates,
|
| 198 | list_active_including_instances(CalendarId) ->
|
| 199 | Candidates = mnesia:dirty_index_read(event, CalendarId, #event.calendar_id),
|
| 200 | Events = [E || E <- Candidates, E#event.status =:= active],
|
| 212 | F = fun() ->
|
| 213 | case mnesia:read(event, Id) of
|
| 214 | [] -> {error, not_found};
|
| 216 | UpdatedEvent = apply_updates(Event, Updates),
|
| 217 | mnesia:write(UpdatedEvent),
|
| 218 | {ok, UpdatedEvent}
|
| 220 | end,
|
| 221 | case mnesia:transaction(F) of
|
| 222 | {atomic, Result} -> Result;
|
| 239 | count_events() ->
|
| 240 | mnesia:table_info(event, size).
|
| 241 |
|
| 249 | %% then filter by is_instance instead of a full table scan.
|
| 250 | Active = mnesia:dirty_index_read(event, active, #event.status),
|
| 251 | [E || E <- Active, E#event.is_instance =:= false].
|
| 261 | core_stats:with_daily(events_created, From, To, fun() ->
|
| 262 | %% TODO: created_at has a hash index (no range-scan support in Mnesia);
|
| 263 | %% a full scan is still required here. Pre-aggregate in stats_collector
|
| 264 | %% to avoid this fallback path.
|
| 265 | All = mnesia:dirty_match_object(#event{_ = '_'}),
|
| 266 | Filtered = lists:filter(fun(E) -> E#event.created_at >= From andalso
|
| 288 | Events = lists:flatmap(
|
| 289 | fun(Type) -> mnesia:dirty_index_read(event, Type, #event.event_type) end,
|
| 290 | KnownTypes),
|
| 309 | Events = lists:flatmap(
|
| 310 | fun(Status) -> mnesia:dirty_index_read(event, Status, #event.status) end,
|
| 311 | KnownStatuses),
|
| 330 | %% The primary path uses stats_tops; this is only a fallback.
|
| 331 | Events = mnesia:dirty_match_object(#event{_ = '_'}),
|
| 332 | Sorted = lists:reverse(lists:sort( |
src/core/core_node_metric.erl View File
| 10 | save(Metric) ->
|
| 11 | try mnesia:dirty_write(Metric)
|
| 12 | catch _:_ -> ok
|
| 21 | get_by_timerange(From, To, Node) ->
|
| 22 | All = mnesia:dirty_match_object(#node_metric{node = Node, _ = '_'}),
|
| 23 | Filtered = lists:filter(fun(M) ->
|
| 36 | calendar:datetime_to_gregorian_seconds(Now) - Seconds),
|
| 37 | All = mnesia:dirty_match_object(#node_metric{_ = '_'}),
|
| 38 | lists:foreach(fun(M) ->
|
| 39 | case M#node_metric.timestamp < Threshold of
|
| 40 | true -> mnesia:dirty_delete_object(M);
|
| 41 | false -> ok |
src/core/core_notification.erl View File
src/core/core_password_reset.erl View File
| 15 | calendar:datetime_to_gregorian_seconds(calendar:universal_time()) + ?TOKEN_LIFETIME_HOURS * 3600),
|
| 16 | mnesia:dirty_write(#password_reset{token = Token, user_id = UserId, expires_at = Expires}),
|
| 17 | {ok, Token, Expires}.
|
| 25 | verify_token(Token) ->
|
| 26 | case mnesia:dirty_read(password_reset, Token) of
|
| 27 | [#password_reset{user_id = UserId, expires_at = Expires}] ->
|
| 57 | delete_token(Token) ->
|
| 58 | mnesia:dirty_delete(password_reset, Token),
|
| 59 | ok.
|
| 63 | Now = calendar:universal_time(),
|
| 64 | case mnesia:dirty_match_object(#password_reset{user_id = UserId, _ = '_'}) of
|
| 65 | [] ->
|
| 80 | {error, not_found} ->
|
| 81 | case mnesia:transaction(fun() -> mnesia:read(user, UserId) end) of
|
| 82 | {atomic, [_ | _]} -> true; |
src/core/core_push_subscription.erl View File
| 22 | },
|
| 23 | case mnesia:transaction(fun() -> mnesia:write(Rec) end) of
|
| 24 | {atomic, ok} -> {ok, Rec};
|
| 35 | },
|
| 36 | case mnesia:transaction(fun() -> mnesia:write(Rec) end) of
|
| 37 | {atomic, ok} -> {ok, Rec};
|
| 43 | list_by_user(UserId) ->
|
| 44 | mnesia:dirty_match_object(#push_subscription{user_id = UserId, _ = '_'}).
|
| 45 |
|
| 47 | get_by_endpoint(Endpoint) ->
|
| 48 | case mnesia:dirty_index_read(push_subscription, Endpoint, #push_subscription.endpoint) of
|
| 49 | [Rec | _] -> {ok, Rec};
|
| 50 | [] ->
|
| 51 | case mnesia:dirty_match_object(#push_subscription{endpoint = Endpoint, _ = '_'}) of
|
| 52 | [Rec | _] -> {ok, Rec};
|
| 69 | delete_by_id(Id) ->
|
| 70 | case mnesia:transaction(fun() -> mnesia:delete({push_subscription, Id}) end) of
|
| 71 | {atomic, ok} -> ok; |
src/core/core_report.erl View File
| 36 | },
|
| 37 | F = fun() -> mnesia:write(Report), {ok, Report} end,
|
| 38 | case mnesia:transaction(F) of
|
| 39 | {atomic, Result} -> Result;
|
| 48 | get_by_id(Id) ->
|
| 49 | case mnesia:dirty_read(report, Id) of
|
| 50 | [] -> {error, not_found};
|
| 59 | list_all() ->
|
| 60 | mnesia:dirty_match_object(#report{_ = '_'}).
|
| 61 |
|
| 68 | Match = #report{reporter_id = ReporterId, _ = '_'},
|
| 69 | mnesia:dirty_match_object(Match).
|
| 70 |
|
| 78 | Match = #report{target_type = TargetType, target_id = TargetId, _ = '_'},
|
| 79 | mnesia:dirty_match_object(Match).
|
| 80 |
|
| 99 | F = fun() ->
|
| 100 | case mnesia:read(report, Id) of
|
| 101 | [] -> {error, not_found};
|
| 107 | },
|
| 108 | mnesia:write(Updated),
|
| 109 | {ok, Updated}
|
| 111 | end,
|
| 112 | case mnesia:transaction(F) of
|
| 113 | {atomic, Result} -> Result;
|
| 122 | count_reports() ->
|
| 123 | mnesia:table_info(report, size).
|
| 124 |
|
| 133 | Match = #report{status = Status, _ = '_'},
|
| 134 | length(mnesia:dirty_match_object(Match))
|
| 135 | end).
|
| 144 | Rows = index_read(report, AdminId, #report.resolved_by, fun() ->
|
| 145 | mnesia:dirty_match_object(#report{resolved_by = AdminId, _ = '_'})
|
| 146 | end),
|
| 159 | Match = #report{status = Status, _ = '_'},
|
| 160 | Reports = mnesia:dirty_match_object(Match),
|
| 161 | Resolved = [R || R <- Reports, R#report.resolved_at /= undefined],
|
| 179 | core_stats:with_daily(reports_created, From, To, fun() ->
|
| 180 | All = mnesia:dirty_match_object(#report{_ = '_'}),
|
| 181 | Filtered = lists:filter(fun(R) -> R#report.created_at >= From andalso
|
| 199 | core_stats:with_dim(report, target_type, fun() ->
|
| 200 | Reports = mnesia:dirty_match_object(#report{_ = '_'}),
|
| 201 | lists:foldl(fun(#report{target_type = Type}, Acc) ->
|
| 215 | core_stats:with_dim(report, status, fun() ->
|
| 216 | Reports = mnesia:dirty_match_object(#report{_ = '_'}),
|
| 217 | lists:foldl(fun(#report{status = Status}, Acc) ->
|
| 235 | {error, _} ->
|
| 236 | Reports = mnesia:dirty_match_object(#report{_ = '_'}),
|
| 237 | Dict = lists:foldl(fun(#report{target_type = Type, target_id = Id}, Acc) ->
|
| 251 | index_read(Table, Key, Pos, FallbackFun) ->
|
| 252 | case catch mnesia:dirty_index_read(Table, Key, Pos) of
|
| 253 | {'EXIT', _} -> FallbackFun(); |
src/core/core_review.erl View File
| 42 | },
|
| 43 | F = fun() -> mnesia:write(Review), {ok, Review} end,
|
| 44 | case mnesia:transaction(F) of
|
| 45 | {atomic, Result} -> Result;
|
| 54 | get_by_id(Id) ->
|
| 55 | case mnesia:dirty_read(review, Id) of
|
| 56 | [] -> {error, not_found};
|
| 68 | status = visible, _ = '_'},
|
| 69 | Reviews = mnesia:dirty_match_object(Match),
|
| 70 | {ok, lists:sort(fun(A, B) -> A#review.created_at >= B#review.created_at end,
|
| 79 | Match = #review{user_id = UserId, _ = '_'},
|
| 80 | Reviews = mnesia:dirty_match_object(Match),
|
| 81 | {ok, Reviews}.
|
| 92 | F = fun() ->
|
| 93 | case mnesia:read(review, Id) of
|
| 94 | [] -> {error, not_found};
|
| 96 | UpdatedReview = apply_updates(Review, Updates),
|
| 97 | mnesia:write(UpdatedReview),
|
| 98 | {ok, UpdatedReview}
|
| 100 | end,
|
| 101 | case mnesia:transaction(F) of
|
| 102 | {atomic, Result} -> Result;
|
| 112 | F = fun() ->
|
| 113 | case mnesia:read(review, Id) of
|
| 114 | [] ->
|
| 117 | %% Use review_vote.review_id index instead of full-table match.
|
| 118 | Votes = mnesia:index_read(review_vote, Id, #review_vote.review_id),
|
| 119 | lists:foreach(fun(#review_vote{id = VoteId}) ->
|
| 120 | mnesia:delete({review_vote, VoteId})
|
| 121 | end, Votes),
|
| 122 | mnesia:delete_object(Review),
|
| 123 | {ok, deleted}
|
| 125 | end,
|
| 126 | case mnesia:transaction(F) of
|
| 127 | {atomic, Result} -> Result;
|
| 158 | status = visible, _ = '_'},
|
| 159 | Reviews = mnesia:dirty_match_object(Match),
|
| 160 | case length(Reviews) of
|
| 175 | target_id = TargetId, _ = '_'},
|
| 176 | case mnesia:dirty_match_object(Match) of
|
| 177 | [] -> false;
|
| 186 | count_reviews() ->
|
| 187 | mnesia:table_info(review, size).
|
| 188 |
|
| 194 | list_all() ->
|
| 195 | mnesia:dirty_match_object(#review{_ = '_'}).
|
| 196 |
|
| 204 | core_stats:with_daily(reviews_created, From, To, fun() ->
|
| 205 | All = mnesia:dirty_match_object(#review{_ = '_'}),
|
| 206 | Filtered = lists:filter(fun(R) -> R#review.created_at >= From andalso
|
| 226 | core_stats:with_dim(review, target_type, fun() ->
|
| 227 | Reviews = mnesia:dirty_match_object(#review{_ = '_'}),
|
| 228 | lists:foldl(fun(#review{target_type = Type}, Acc) ->
|
| 242 | core_stats:with_dim(review, status, fun() ->
|
| 243 | Reviews = mnesia:dirty_match_object(#review{_ = '_'}),
|
| 244 | lists:foldl(fun(#review{status = Status}, Acc) ->
|
| 281 | aggregate_targets_by(Pred, N) ->
|
| 282 | Reviews = mnesia:dirty_match_object(#review{_ = '_'}),
|
| 283 | Filtered = lists:filter(Pred, Reviews), |
src/core/core_review_vote.erl View File
| 20 | F = fun() ->
|
| 21 | case mnesia:read(review, ReviewId) of
|
| 22 | [] ->
|
| 38 | },
|
| 39 | mnesia:write(Vote),
|
| 40 | Updated = bump(Review, Value, 1),
|
| 41 | mnesia:write(Updated),
|
| 42 | {ok, Updated, Value};
|
| 44 | %% тот же голос — идемпотентно
|
| 45 | mnesia:write(Vote#review_vote{updated_at = Now}),
|
| 46 | {ok, Review, Value};
|
| 48 | UpdatedVote = Vote#review_vote{value = Value, updated_at = Now},
|
| 49 | mnesia:write(UpdatedVote),
|
| 50 | Updated0 = bump(Review, OldValue, -1),
|
| 51 | Updated1 = bump(Updated0, Value, 1),
|
| 52 | mnesia:write(Updated1),
|
| 53 | {ok, Updated1, Value}
|
| 56 | end,
|
| 57 | case mnesia:transaction(F) of
|
| 58 | {atomic, Result} -> Result;
|
| 69 | F = fun() ->
|
| 70 | case mnesia:read(review, ReviewId) of
|
| 71 | [] ->
|
| 81 | [#review_vote{value = Value, id = VoteId}] ->
|
| 82 | mnesia:delete({review_vote, VoteId}),
|
| 83 | Updated = bump(Review, Value, -1),
|
| 84 | mnesia:write(Updated),
|
| 85 | {ok, Updated, null}
|
| 88 | end,
|
| 89 | case mnesia:transaction(F) of
|
| 90 | {atomic, Result} -> Result;
|
| 100 | get_by_user_and_review(UserId, ReviewId) ->
|
| 101 | case mnesia:dirty_match_object(
|
| 102 | #review_vote{review_id = ReviewId, user_id = UserId, _ = '_'}) of
|
| 113 | list_by_user(UserId) ->
|
| 114 | mnesia:dirty_match_object(#review_vote{user_id = UserId, _ = '_'}).
|
| 115 |
|
| 121 | list_by_review(ReviewId) ->
|
| 122 | mnesia:dirty_match_object(#review_vote{review_id = ReviewId, _ = '_'}).
|
| 123 |
|
| 132 | lists:foreach(fun(#review_vote{id = Id}) ->
|
| 133 | mnesia:delete({review_vote, Id})
|
| 134 | end, list_by_review_tx(ReviewId)),
|
| 136 | end,
|
| 137 | case mnesia:transaction(F) of
|
| 138 | {atomic, ok} -> ok;
|
| 146 | find_vote(ReviewId, UserId) ->
|
| 147 | mnesia:match_object(#review_vote{review_id = ReviewId, user_id = UserId, _ = '_'}).
|
| 148 |
|
| 149 | list_by_review_tx(ReviewId) ->
|
| 150 | mnesia:match_object(#review_vote{review_id = ReviewId, _ = '_'}).
|
src/core/core_session.erl View File
| 27 | },
|
| 28 | mnesia:dirty_write(Session),
|
| 29 | core_counters:inc(active_sessions),
|
| 41 | validate(Token) ->
|
| 42 | case mnesia:dirty_read({session, Token}) of
|
| 43 | [Session] ->
|
| 46 | % Получаем пользователя по ID из сессии
|
| 47 | case mnesia:dirty_read({user, Session#session.user_id}) of
|
| 48 | [User] -> {ok, Session#session.user_id, User};
|
| 51 | false ->
|
| 52 | mnesia:dirty_delete({session, Token}),
|
| 53 | core_counters:dec(active_sessions),
|
| 64 | delete(Token) ->
|
| 65 | mnesia:dirty_delete({session, Token}),
|
| 66 | core_counters:dec(active_sessions), |
src/core/core_specialist_invite.erl View File
| 13 | F = fun() ->
|
| 14 | mnesia:write(Rec),
|
| 15 | {ok, Rec}
|
| 16 | end,
|
| 17 | case mnesia:transaction(F) of
|
| 18 | {atomic, Result} -> Result;
|
| 23 | get_by_id(Id) ->
|
| 24 | case mnesia:dirty_read(specialist_invite, Id) of
|
| 25 | [Rec] -> {ok, Rec};
|
| 30 | get_by_token(Token) ->
|
| 31 | case mnesia:dirty_index_read(specialist_invite, Token, #specialist_invite.token) of
|
| 32 | [Rec] -> {ok, Rec};
|
| 38 | list_by_calendar(CalendarId) ->
|
| 39 | mnesia:dirty_match_object(#specialist_invite{calendar_id = CalendarId, _ = '_'}).
|
| 40 |
|
| 42 | list_by_invitee(UserId) ->
|
| 43 | mnesia:dirty_match_object(#specialist_invite{invitee_user_id = UserId, _ = '_'}).
|
| 44 |
|
| 46 | list_by_email(Email) ->
|
| 47 | mnesia:dirty_match_object(#specialist_invite{invitee_email = Email, _ = '_'}).
|
| 48 |
|
| 52 | F = fun() ->
|
| 53 | case mnesia:read(specialist_invite, Id) of
|
| 54 | [] ->
|
| 57 | Updated = Rec#specialist_invite{status = Status},
|
| 58 | mnesia:write(Updated),
|
| 59 | {ok, Updated}
|
| 61 | end,
|
| 62 | case mnesia:transaction(F) of
|
| 63 | {atomic, Result} -> Result; |
src/core/core_stats.erl View File
src/core/core_subscription.erl View File
| 35 | },
|
| 36 | F = fun() -> mnesia:write(Subscription), {ok, Subscription} end,
|
| 37 | case mnesia:transaction(F) of
|
| 38 | {atomic, Result} -> Result;
|
| 47 | get_by_id(Id) ->
|
| 48 | case mnesia:dirty_read(subscription, Id) of
|
| 49 | [] -> {error, not_found};
|
| 59 | Match = #subscription{user_id = UserId, status = active, _ = '_'},
|
| 60 | case catch mnesia:dirty_match_object(Match) of
|
| 61 | {'EXIT', _} -> {error, not_found};
|
| 72 | Match = #subscription{user_id = UserId, _ = '_'},
|
| 73 | case catch mnesia:dirty_match_object(Match) of
|
| 74 | {'EXIT', _} ->
|
| 87 | Match = #subscription{_ = '_'},
|
| 88 | Subscriptions = mnesia:dirty_match_object(Match),
|
| 89 | {ok, Subscriptions}.
|
| 98 | F = fun() ->
|
| 99 | case mnesia:read(subscription, Id) of
|
| 100 | [] -> {error, not_found};
|
| 105 | },
|
| 106 | mnesia:write(Updated),
|
| 107 | {ok, Updated}
|
| 109 | end,
|
| 110 | case mnesia:transaction(F) of
|
| 111 | {atomic, Result} -> Result;
|
| 122 | Match = #subscription{status = active, _ = '_'},
|
| 123 | ActiveSubscriptions = mnesia:dirty_match_object(Match),
|
| 124 | lists:foreach(fun(Sub) ->
|
| 196 | Updated = apply_updates(Sub, Updates),
|
| 197 | mnesia:dirty_write(Updated),
|
| 198 | {ok, Updated};
|
| 209 | {ok, _Sub} ->
|
| 210 | mnesia:dirty_delete({subscription, Id}),
|
| 211 | {ok, deleted};
|
| 284 | count_subscriptions() ->
|
| 285 | mnesia:table_info(subscription, size).
|
| 286 |
|
| 294 | core_stats:with_daily(subscriptions_created, From, To, fun() ->
|
| 295 | All = mnesia:dirty_match_object(#subscription{_ = '_'}),
|
| 296 | Filtered = lists:filter(fun(S) -> S#subscription.created_at >= From andalso
|
| 316 | core_stats:with_dim(subscription, plan, fun() ->
|
| 317 | Subs = mnesia:dirty_match_object(#subscription{_ = '_'}),
|
| 318 | lists:foldl(fun(#subscription{plan = Plan}, Acc) ->
|
| 332 | core_stats:with_dim(subscription, status, fun() ->
|
| 333 | Subs = mnesia:dirty_match_object(#subscription{_ = '_'}),
|
| 334 | lists:foldl(fun(#subscription{status = Status}, Acc) ->
|
| 349 | Match = #subscription{trial_used = false, _ = '_'},
|
| 350 | length(mnesia:dirty_match_object(Match))
|
| 351 | end).
|
| 362 | Match = #subscription{status = active, trial_used = true, _ = '_'},
|
| 363 | ActivePaid = mnesia:dirty_match_object(Match),
|
| 364 | lists:filter(fun(#subscription{expires_at = Exp}) -> |
src/core/core_ticket.erl View File
| 50 | },
|
| 51 | F = fun() -> mnesia:write(Ticket), {ok, Ticket} end,
|
| 52 | case mnesia:transaction(F) of
|
| 53 | {atomic, Result} -> Result;
|
| 104 | get_by_id(Id) ->
|
| 105 | case mnesia:dirty_read(ticket, Id) of
|
| 106 | [] -> {error, not_found};
|
| 115 | list_all() ->
|
| 116 | mnesia:dirty_match_object(#ticket{_ = '_'}).
|
| 117 |
|
| 124 | Match = #ticket{reporter_id = UserId, _ = '_'},
|
| 125 | mnesia:dirty_match_object(Match).
|
| 126 |
|
| 134 | F = fun() ->
|
| 135 | case mnesia:read(ticket, Id) of
|
| 136 | [] -> {error, not_found};
|
| 138 | UpdatedTicket = apply_updates(Ticket, Updates),
|
| 139 | mnesia:write(UpdatedTicket),
|
| 140 | {ok, UpdatedTicket}
|
| 142 | end,
|
| 143 | case mnesia:transaction(F) of
|
| 144 | {atomic, Result} -> Result;
|
| 163 | delete(Id) ->
|
| 164 | case mnesia:dirty_read(ticket, Id) of
|
| 165 | [] -> {error, not_found};
|
| 166 | [_] -> mnesia:dirty_delete(ticket, Id), {ok, deleted}
|
| 167 | end.
|
| 211 | count_tickets() ->
|
| 212 | mnesia:table_info(ticket, size).
|
| 213 |
|
| 222 | Match = #ticket{status = Status, _ = '_'},
|
| 223 | length(mnesia:dirty_match_object(Match))
|
| 224 | end).
|
| 262 | length(index_read(ticket, AdminId, #ticket.assigned_to, fun() ->
|
| 263 | mnesia:dirty_match_object(#ticket{assigned_to = AdminId, _ = '_'})
|
| 264 | end));
|
| 266 | Rows = index_read(ticket, AdminId, #ticket.assigned_to, fun() ->
|
| 267 | mnesia:dirty_match_object(#ticket{assigned_to = AdminId, _ = '_'})
|
| 268 | end),
|
| 278 | core_stats:with_daily(tickets_created, From, To, fun() ->
|
| 279 | All = mnesia:dirty_match_object(#ticket{_ = '_'}),
|
| 280 | Filtered = lists:filter(fun(T) -> T#ticket.first_seen >= From andalso
|
| 295 | index_read(Table, Key, Pos, FallbackFun) ->
|
| 296 | case catch mnesia:dirty_index_read(Table, Key, Pos) of
|
| 297 | {'EXIT', _} -> FallbackFun(); |
src/core/core_user.erl View File
| 59 | },
|
| 60 | F = fun() -> mnesia:write(User), {ok, User} end,
|
| 61 | case mnesia:transaction(F) of
|
| 62 | {atomic, Result} -> Result;
|
| 72 | get_by_id(Id) ->
|
| 73 | case mnesia:dirty_read(user, Id) of
|
| 74 | [] -> {error, not_found};
|
| 84 | %% Use the index on #user.email instead of a full table scan.
|
| 85 | case mnesia:dirty_index_read(user, Email, #user.email) of
|
| 86 | [] -> {error, not_found};
|
| 110 | F = fun() ->
|
| 111 | case mnesia:read(user, Id) of
|
| 112 | [] -> {error, not_found};
|
| 114 | UpdatedUser = apply_updates(User, Updates),
|
| 115 | mnesia:write(UpdatedUser),
|
| 116 | {ok, UpdatedUser}
|
| 118 | end,
|
| 119 | case mnesia:transaction(F) of
|
| 120 | {atomic, Result} -> Result;
|
| 132 | F = fun() ->
|
| 133 | case mnesia:read(user, Id, write) of
|
| 134 | [] -> {error, not_found};
|
| 136 | Updated = User#user{last_login = calendar:universal_time()},
|
| 137 | mnesia:write(Updated),
|
| 138 | {ok, Updated}
|
| 140 | end,
|
| 141 | case mnesia:transaction(F) of
|
| 142 | {atomic, Result} -> Result;
|
| 155 | F = fun() ->
|
| 156 | case mnesia:read(user, Id, write) of
|
| 157 | [] -> {error, not_found};
|
| 160 | updated_at = calendar:universal_time()},
|
| 161 | mnesia:write(Updated),
|
| 162 | {ok, Updated}
|
| 164 | end,
|
| 165 | case mnesia:transaction(F) of
|
| 166 | {atomic, Result} -> Result;
|
| 199 | MS = ets:fun2ms(fun(#user{status = S} = U) when S =/= deleted -> U end),
|
| 200 | case mnesia:dirty_select(user, MS) of
|
| 201 | [] ->
|
| 203 | All ->
|
| 204 | %% Apply offset/limit at the list level (Mnesia dirty_select
|
| 205 | %% doesn't support offset natively, but we filtered at DB level).
|
| 248 | F = fun() ->
|
| 249 | case mnesia:read(user, Id, write) of
|
| 250 | [] -> {error, not_found};
|
| 253 | updated_at = calendar:universal_time()},
|
| 254 | mnesia:write(Updated),
|
| 255 | {ok, Updated}
|
| 257 | end,
|
| 258 | case mnesia:transaction(F) of
|
| 259 | {atomic, Result} -> Result;
|
| 271 | F = fun() ->
|
| 272 | case mnesia:read(user, Id, write) of
|
| 273 | [] -> {error, not_found};
|
| 276 | updated_at = calendar:universal_time()},
|
| 277 | mnesia:write(Updated),
|
| 278 | {ok, Updated}
|
| 280 | end,
|
| 281 | case mnesia:transaction(F) of
|
| 282 | {atomic, Result} -> Result;
|
| 303 | count_users() ->
|
| 304 | mnesia:table_info(user, size).
|
| 305 |
|
| 311 | list_all() ->
|
| 312 | mnesia:dirty_match_object(#user{_ = '_'}).
|
| 313 |
|
| 322 | core_stats:with_daily(users_created, From, To, fun() ->
|
| 323 | All = mnesia:dirty_match_object(#user{_ = '_'}),
|
| 324 | Filtered = lists:filter(fun(U) -> U#user.created_at >= From andalso
|
| 343 | Match = #user{status = pending, _ = '_'},
|
| 344 | length(mnesia:dirty_match_object(Match))
|
| 345 | end).
|
| 354 | core_stats:with_daily(pending_users_created, From, To, fun() ->
|
| 355 | All = mnesia:dirty_match_object(#user{_ = '_'}),
|
| 356 | Pending = lists:filter(fun(U) -> U#user.status =:= pending end, All),
|
| 376 | core_stats:with_dim(user, role, fun() ->
|
| 377 | Users = mnesia:dirty_match_object(#user{_ = '_'}),
|
| 378 | lists:foldl(fun(#user{role = Role}, Acc) ->
|
| 393 | core_stats:with_dim(user, status, fun() ->
|
| 394 | Users = mnesia:dirty_match_object(#user{_ = '_'}),
|
| 395 | lists:foldl(fun(#user{status = Status}, Acc) ->
|
| 453 | F = fun() ->
|
| 454 | case mnesia:index_read(user, Email, #user.email) of
|
| 455 | [] ->
|
| 475 | },
|
| 476 | ok = mnesia:write(User),
|
| 477 | {ok, User};
|
| 481 | end,
|
| 482 | case mnesia:transaction(F) of
|
| 483 | {atomic, Result} -> Result;
|
| 494 | F = fun() ->
|
| 495 | case mnesia:read(user, Id, write) of
|
| 496 | [#user{role = bot}] ->
|
| 497 | mnesia:delete({user, Id}),
|
| 498 | ok;
|
| 502 | end,
|
| 503 | case mnesia:transaction(F) of
|
| 504 | {atomic, Result} -> Result; |