Update RedisParkingRepository.ts to use Redis aggregation functions

This commit is contained in:
2025-07-03 16:00:57 -04:00
parent f406e3e01d
commit d1b60772d8

View File

@@ -1,5 +1,5 @@
import { ParkingGetterSetterRepository } from "./ParkingGetterSetterRepository";
import { IParkingStructure, IParkingStructureTimestampRecord } from "../entities/ParkingRepositoryEntities";
import { IParkingStructure } from "../entities/ParkingRepositoryEntities";
import { HistoricalParkingAverageQueryResult, ParkingStructureCountOptions } from "./ParkingGetterRepository";
import { BaseRedisRepository } from "./BaseRedisRepository";
import { PARKING_LOGGING_INTERVAL_MS } from "./ParkingRepositoryConstants";
@@ -76,25 +76,7 @@ export class RedisParkingRepository extends BaseRedisRepository implements Parki
};
getHistoricalAveragesOfParkingStructureCounts = async (id: string, options: ParkingStructureCountOptions): Promise<HistoricalParkingAverageQueryResult[]> => {
const keys = this.createRedisKeys(id);
try {
const timeSeriesData = await this.redisClient.sendCommand([
'TS.RANGE',
keys.timeSeries,
options.startUnixEpochMs.toString(),
options.endUnixEpochMs.toString()
]) as [string, string][];
if (!timeSeriesData || timeSeriesData.length === 0) {
return [];
}
const records = this.convertTimeSeriesDataToRecords(timeSeriesData, id);
return this.calculateAveragesFromRecords(records, options);
} catch (error) {
return [];
}
return this.calculateAveragesFromRecords(id, options);
};
private createRedisKeys = (structureId: string) => ({
@@ -173,30 +155,40 @@ export class RedisParkingRepository extends BaseRedisRepository implements Parki
}
};
private convertTimeSeriesDataToRecords = (timeSeriesData: [string, string][], id: string): IParkingStructureTimestampRecord[] => {
return timeSeriesData.map(([timestamp, value]) => ({
id,
timestampMs: parseInt(timestamp),
spotsAvailable: parseInt(value)
}));
};
private calculateAveragesFromRecords = (
records: IParkingStructureTimestampRecord[],
private calculateAveragesFromRecords = async (
id: string,
options: ParkingStructureCountOptions
): HistoricalParkingAverageQueryResult[] => {
const results: HistoricalParkingAverageQueryResult[] = [];
): Promise<HistoricalParkingAverageQueryResult[]> => {
const keys = this.createRedisKeys(id);
const { startUnixEpochMs, endUnixEpochMs, intervalMs } = options;
const results: HistoricalParkingAverageQueryResult[] = [];
let currentIntervalStart = startUnixEpochMs;
while (currentIntervalStart < endUnixEpochMs) {
const currentIntervalEnd = Math.min(currentIntervalStart + intervalMs, endUnixEpochMs);
const recordsInInterval = this.getRecordsInTimeRange(records, currentIntervalStart, currentIntervalEnd);
try {
const aggregationResult = await this.redisClient.sendCommand([
'TS.RANGE',
keys.timeSeries,
currentIntervalStart.toString(),
currentIntervalEnd.toString(),
'AGGREGATION',
'AVG',
intervalMs.toString()
]) as [string, string][];
if (recordsInInterval.length > 0) {
const averageResult = this.calculateAverageForInterval(currentIntervalStart, currentIntervalEnd, recordsInInterval);
results.push(averageResult);
if (aggregationResult && aggregationResult.length > 0) {
const [, averageValue] = aggregationResult[0];
results.push({
fromUnixEpochMs: currentIntervalStart,
toUnixEpochMs: currentIntervalEnd,
averageSpotsAvailable: parseFloat(averageValue)
});
}
} catch (error) {
// If Redis aggregation fails, skip this interval
}
currentIntervalStart = currentIntervalEnd;
@@ -205,31 +197,6 @@ export class RedisParkingRepository extends BaseRedisRepository implements Parki
return results;
};
private getRecordsInTimeRange = (
records: IParkingStructureTimestampRecord[],
startMs: number,
endMs: number
): IParkingStructureTimestampRecord[] => {
return records.filter(record =>
record.timestampMs >= startMs && record.timestampMs < endMs
);
};
private calculateAverageForInterval = (
fromMs: number,
toMs: number,
records: IParkingStructureTimestampRecord[]
): HistoricalParkingAverageQueryResult => {
const totalSpotsAvailable = records.reduce((sum, record) => sum + record.spotsAvailable, 0);
const averageSpotsAvailable = totalSpotsAvailable / records.length;
return {
fromUnixEpochMs: fromMs,
toUnixEpochMs: toMs,
averageSpotsAvailable
};
};
setLoggingInterval = (intervalMs: number): void => {
this.loggingIntervalMs = intervalMs;
};