refactor: optimisation around stoptimes
- moved stoptime related functionality into new core:data:stoptime module
- will feature all the different realtime stoptime sources to be
integrated later
- create proper database schema for future migrations
- deduplicate trips into stoppingpatterns, since many trips share the
exact same stopping pattern
- stoptimes are now linked to stoppingpatterns instead
- stoppingpattern ids are generated from a hash composed of all stoptimes
- stoptimes now use deltas for arrival time to save space
This commit is contained in:
parent
f1770744db
commit
102c028407
39 changed files with 396 additions and 223 deletions
|
|
@ -47,6 +47,7 @@ sqldelight {
|
|||
databases {
|
||||
register("BanksiaDatabase") {
|
||||
packageName.set("moe.lava.banksia.core.sqld")
|
||||
schemaOutputDirectory.set(file("src/commonMain/sqldelight/schema"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@ import app.cash.sqldelight.driver.android.AndroidSqliteDriver
|
|||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.get
|
||||
|
||||
actual class DatabaseManager actual constructor() : KoinComponent {
|
||||
actual class DatabaseManager : KoinComponent {
|
||||
actual val database by lazy {
|
||||
val ctx = get<Context>().applicationContext
|
||||
val driver = AndroidSqliteDriver(BanksiaDatabase.Schema, ctx, "timetable.db")
|
||||
val driver = AndroidSqliteDriver(BanksiaDatabase.Schema, ctx, "${DBNAME}.db")
|
||||
BanksiaDatabase(driver)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package moe.lava.banksia.core.sqld
|
||||
|
||||
import org.koin.core.component.KoinComponent
|
||||
internal const val DBNAME = "timetable"
|
||||
|
||||
expect class DatabaseManager() : KoinComponent {
|
||||
expect class DatabaseManager() {
|
||||
val database: BanksiaDatabase
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ val sqldDiModule = module {
|
|||
factory { get<BanksiaDatabase>().serviceExceptionQueries }
|
||||
factory { get<BanksiaDatabase>().shapeQueries }
|
||||
factory { get<BanksiaDatabase>().stopQueries }
|
||||
factory { get<BanksiaDatabase>().stoppingPatternQueries }
|
||||
factory { get<BanksiaDatabase>().stopTimeQueries }
|
||||
factory { get<BanksiaDatabase>().tripQueries }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,23 +3,25 @@ package moe.lava.banksia.core.sqld.mappers
|
|||
import moe.lava.banksia.core.model.FutureTime
|
||||
import moe.lava.banksia.core.model.FutureTime.Companion.asInt
|
||||
import moe.lava.banksia.core.model.StopTime
|
||||
import moe.lava.banksia.core.model.TimeType
|
||||
import moe.lava.banksia.core.sqld.StopTime as DbStopTime
|
||||
|
||||
fun DbStopTime.asModel() = StopTime(
|
||||
tripId = tripId,
|
||||
patternId = patternId,
|
||||
stopId = stopId,
|
||||
arrivalTime = FutureTime.fromInt(arrivalTime.toInt()),
|
||||
departureTime = FutureTime.fromInt(departureTime.toInt()),
|
||||
headsign = null,
|
||||
time = TimeType.Undated(
|
||||
arrival = FutureTime.fromInt((departureTime + arrivalDelta).toInt()),
|
||||
departure = FutureTime.fromInt(departureTime.toInt()),
|
||||
),
|
||||
pickupType = pickupType.toInt(),
|
||||
dropOffType = dropOffType.toInt(),
|
||||
)
|
||||
|
||||
fun StopTime.asDb() = DbStopTime(
|
||||
tripId = tripId,
|
||||
fun StopTime.Undated.asDb() = DbStopTime(
|
||||
patternId = patternId,
|
||||
stopId = stopId,
|
||||
arrivalTime = arrivalTime.asInt().toLong(),
|
||||
departureTime = departureTime.asInt().toLong(),
|
||||
arrivalDelta = (time.arrival.asInt() - time.departure.asInt()).toLong(),
|
||||
departureTime = time.departure.asInt().toLong(),
|
||||
pickupType = pickupType.toLong(),
|
||||
dropOffType = dropOffType.toLong(),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
package moe.lava.banksia.core.sqld.mappers
|
||||
|
||||
import moe.lava.banksia.core.model.StopTime
|
||||
import moe.lava.banksia.core.model.StoppingPattern
|
||||
import moe.lava.banksia.core.sqld.StoppingPattern as DbStoppingPattern
|
||||
|
||||
fun DbStoppingPattern.asModel(stoptimes: List<StopTime.Undated>) = StoppingPattern.Undated(
|
||||
id = id,
|
||||
routeId = routeId,
|
||||
shapeId = shapeId,
|
||||
headsign = headsign,
|
||||
wheelchairAccessible = wheelchairAccessible == 1L,
|
||||
stoptimes = stoptimes,
|
||||
)
|
||||
|
||||
fun StoppingPattern.Undated.asDb() = DbStoppingPattern(
|
||||
id = id,
|
||||
routeId = routeId,
|
||||
shapeId = shapeId,
|
||||
headsign = headsign,
|
||||
wheelchairAccessible = if (wheelchairAccessible) 1L else 0L,
|
||||
)
|
||||
|
|
@ -1,32 +1,27 @@
|
|||
package moe.lava.banksia.core.sqld.mappers
|
||||
|
||||
import moe.lava.banksia.core.model.Service
|
||||
import moe.lava.banksia.core.model.StoppingPattern
|
||||
import moe.lava.banksia.core.model.Trip
|
||||
import moe.lava.banksia.core.sqld.Trip as DbTrip
|
||||
|
||||
fun DbTrip.asModel(service: Service): Trip {
|
||||
fun DbTrip.asModel(pattern: StoppingPattern.Undated, service: Service): Trip.Undated {
|
||||
if (serviceId != service.id) {
|
||||
throw IllegalArgumentException("trip and service id mismatch (${serviceId} != ${service.id})")
|
||||
}
|
||||
return Trip(
|
||||
id = id,
|
||||
routeId = routeId,
|
||||
id = gtfsId,
|
||||
pattern = pattern,
|
||||
service = service,
|
||||
shapeId = shapeId,
|
||||
tripHeadsign = tripHeadsign,
|
||||
directionId = directionId,
|
||||
blockId = blockId,
|
||||
wheelchairAccessible = wheelchairAccessible == 1L
|
||||
directionId = directionId.toInt(),
|
||||
blockId = blockId.toString(),
|
||||
)
|
||||
}
|
||||
|
||||
fun Trip.asDb() = DbTrip(
|
||||
id = id,
|
||||
routeId = routeId,
|
||||
fun Trip.Undated.asDb() = DbTrip(
|
||||
gtfsId = id,
|
||||
patternId = pattern.id,
|
||||
serviceId = service.id,
|
||||
shapeId = shapeId,
|
||||
tripHeadsign = tripHeadsign,
|
||||
directionId = directionId,
|
||||
blockId = blockId,
|
||||
wheelchairAccessible = if (wheelchairAccessible) 1L else 0L
|
||||
directionId = directionId.toLong(),
|
||||
blockId = blockId?.toLong(),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ getAll:
|
|||
SELECT * FROM Stop;
|
||||
|
||||
getAllParentless:
|
||||
SELECT * FROM Stop WHERE platformCode IS NULL AND parent IS NULL;
|
||||
SELECT * FROM Stop WHERE platformCode IS NOT NULL AND parent IS NULL;
|
||||
|
||||
get:
|
||||
SELECT * FROM Stop WHERE id == ?;
|
||||
|
|
@ -32,8 +32,8 @@ UPDATE Stop SET parent = ? WHERE id IN ?;
|
|||
getByRoute:
|
||||
SELECT Stop.* FROM Stop
|
||||
INNER JOIN StopTime ON StopTime.stopId == Stop.id
|
||||
INNER JOIN Trip ON Trip.id == StopTime.tripId
|
||||
WHERE Trip.routeId == :id
|
||||
INNER JOIN StoppingPattern ON StoppingPattern.id == StopTime.patternId
|
||||
WHERE StoppingPattern.routeId == :id
|
||||
GROUP BY Stop.id;
|
||||
|
||||
-- I vibecoded this, sorry
|
||||
|
|
@ -41,8 +41,8 @@ getParentsByRoute:
|
|||
WITH RECURSIVE Tree AS (
|
||||
SELECT Stop.* FROM Stop
|
||||
INNER JOIN StopTime ON StopTime.stopId == Stop.id
|
||||
INNER JOIN Trip ON Trip.id == StopTime.tripId
|
||||
WHERE Trip.routeId == :id
|
||||
INNER JOIN StoppingPattern ON StoppingPattern.id == StopTime.patternId
|
||||
WHERE StoppingPattern.routeId == :id
|
||||
GROUP BY Stop.id
|
||||
|
||||
UNION ALL
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
CREATE TABLE StopTime (
|
||||
tripId TEXT NOT NULL REFERENCES Trip (id),
|
||||
patternId INTEGER NOT NULL REFERENCES StoppingPattern (id),
|
||||
stopId TEXT NOT NULL REFERENCES Stop (id),
|
||||
arrivalTime INTEGER NOT NULL,
|
||||
arrivalDelta INTEGER NOT NULL,
|
||||
departureTime INTEGER NOT NULL,
|
||||
pickupType INTEGER NOT NULL,
|
||||
dropOffType INTEGER NOT NULL,
|
||||
PRIMARY KEY (tripId, stopId)
|
||||
PRIMARY KEY (patternId, stopId)
|
||||
) WITHOUT ROWID;
|
||||
|
||||
CREATE INDEX idx_StopTime_stopId ON StopTime (stopId);
|
||||
|
|
@ -16,8 +16,9 @@ INSERT OR REPLACE INTO StopTime VALUES ?;
|
|||
getForStopDated:
|
||||
SELECT DISTINCT StopTime.* FROM StopTime
|
||||
INNER JOIN Service ON Service.days & :days = :days AND :date BETWEEN Service.start AND Service.`end`
|
||||
INNER JOIN Trip ON Trip.serviceId == Service.id
|
||||
LEFT JOIN ServiceException ON ServiceException.serviceId == Service.id AND ServiceException.date == :date
|
||||
WHERE StopTime.tripId == Trip.id
|
||||
INNER JOIN Trip ON Trip.serviceId == Service.id
|
||||
INNER JOIN StoppingPattern ON StoppingPattern.id == Trip.patternId
|
||||
WHERE StopTime.patternId == StoppingPattern.id
|
||||
AND StopTime.stopId IN (SELECT Stop.id FROM Stop WHERE Stop.parent == :stopId OR Stop.id == :stopId)
|
||||
AND ServiceException.type IS NULL;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
CREATE TABLE StoppingPattern (
|
||||
id INTEGER PRIMARY KEY NOT NULL,
|
||||
routeId TEXT NOT NULL REFERENCES Route (id),
|
||||
shapeId TEXT NOT NULL REFERENCES Shape (id),
|
||||
headsign TEXT NOT NULL,
|
||||
wheelchairAccessible INTEGER NOT NULL
|
||||
);
|
||||
|
||||
insert:
|
||||
INSERT OR REPLACE INTO StoppingPattern VALUES ?;
|
||||
|
|
@ -1,14 +1,12 @@
|
|||
CREATE TABLE Trip (
|
||||
id TEXT PRIMARY KEY NOT NULL,
|
||||
routeId TEXT NOT NULL REFERENCES Route (id),
|
||||
gtfsId TEXT PRIMARY KEY NOT NULL,
|
||||
patternId INTEGER NOT NULL REFERENCES StoppingPattern (id),
|
||||
serviceId TEXT NOT NULL REFERENCES Service (id),
|
||||
shapeId TEXT NOT NULL REFERENCES Shape (id),
|
||||
tripHeadsign TEXT NOT NULL,
|
||||
directionId TEXT NOT NULL,
|
||||
blockId TEXT,
|
||||
wheelchairAccessible INTEGER NOT NULL
|
||||
blockId INTEGER,
|
||||
directionId INTEGER NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX idx_Trip_patternId ON Trip (patternId);
|
||||
CREATE INDEX idx_Trip_serviceId ON Trip (serviceId);
|
||||
|
||||
insert:
|
||||
|
|
|
|||
BIN
core/sqld/src/commonMain/sqldelight/schema/1.db
Normal file
BIN
core/sqld/src/commonMain/sqldelight/schema/1.db
Normal file
Binary file not shown.
|
|
@ -1,10 +1,11 @@
|
|||
package moe.lava.banksia.core.sqld
|
||||
|
||||
import app.cash.sqldelight.driver.native.NativeSqliteDriver
|
||||
import org.koin.core.component.KoinComponent
|
||||
|
||||
actual class DatabaseManager actual constructor() : org.koin.core.component.KoinComponent {
|
||||
actual class DatabaseManager : KoinComponent {
|
||||
actual val database by lazy {
|
||||
val driver = NativeSqliteDriver(BanksiaDatabase.Schema, "timetable.db")
|
||||
val driver = NativeSqliteDriver(BanksiaDatabase.Schema, "${DBNAME}.db")
|
||||
BanksiaDatabase(driver)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,9 +11,7 @@ import java.io.File
|
|||
import java.util.Properties
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
private const val DBNAME = "timetable"
|
||||
|
||||
actual class DatabaseManager actual constructor() : KoinComponent {
|
||||
actual class DatabaseManager : KoinComponent {
|
||||
private var driver = connect()
|
||||
actual val database get() = BanksiaDatabase(driver)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue