My vehicle logbook app stores the data of the trips. It should also store the changes made to a specific trip; thus, anyone can see if and what data has been changed after the first entry of the trip. Since there are usually not many changes, I thought about storing the whole trip data with each change.
Due to its simple offline first functionality, I want to use AceBase, a NoSQL database. From the web client, I want to directly access the database on the AceBase-Server. I do not want to put an application server in between.
However, I could not find an elegant way to get only the current data of all the trips.
I only found this solution, which requires to load all the changes from the server:
import { AceBaseClient } from "acebase-client";
export type TripChange = {
changedAt: Date;
tripId: string;
purpose: string;
};
/** returns the current trips, i.e. all the trips
* from the latest tripchanges
*/
export async function currentTrips(
db: AceBaseClient,
vehicleId: string
):Promise<TripChange[]> {
// Getting all the trip changes
// sorted by trip and
// then in descending order by the timestamp of the change
const tripChanges = (
await db
.ref(`vehicles/${vehicleId}/tripchanges`)
.query()
.sort("tripId")
.sort("changedAt", false)
.get<TripChange>()
).getValues();
// take the first (=newest) change of a trip
// and push its data to the result array
const result: TripChange[] = [];
let lastTripId = "";
for (const tripChange of tripChanges) {
if (tripChange.tripId !== lastTripId) {
result.push(tripChange);
lastTripId = tripChange.tripId;
}
}
return result;
}
A test of such a function would look like:
const trip_changes_ref =
"vehicles/Scenic220/tripchanges";
await db.ref(trip_changes_ref).push({
changedAt: new Date(2023, 3, 1),
tripId: "idA",
purpose: "pA1",
});
await db.ref(trip_changes_ref).push<TripChange>({
changedAt: new Date(2023, 3, 2),
tripId: "idB",
purpose: "pB1",
});
await db.ref(trip_changes_ref).push<TripChange>({
changedAt: new Date(2023, 3, 3),
tripId: "idA",
purpose: "pA2",
});
await db.ref(trip_changes_ref).push<TripChange>({
changedAt: new Date(2023, 2, 1),
tripId: "idA",
purpose: "pA0",
});
const trips = await currentTrips(db, "Scenic220");
// console.log({ trips });
expect(trips).toHaveLength(2);
expect(trips[0]?.tripId).toBe("idA");
expect(trips[0]?.purpose).toBe("pA2");
expect(trips[1]?.tripId).toBe("idB");
expect(trips[1]?.purpose).toBe("pB1");
In SQL, a query only returning the current data of the trips would be like that:
CREATE TABLE tripchanges (
id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
changed_at TIMESTAMP DEFAULT NOW(),
vehicle_id VARCHAR(250),
trip_id VARCHAR(100),
purpose VARCHAR
);
WITH m AS (
SELECT trip_id, MAX(changed_at) AS latest
FROM tripchanges
GROUP BY trip_id
)
SELECT * FROM tripchanges,m
WHERE tripchanges.trip_id=m.trip_id AND changed_at=m.latest
AND tripchanges.vehicle_id='Scenic220';
Is there a similarly smart way in AceBase?