feat(go): add realtime kv projection
This commit is contained in:
@@ -111,6 +111,9 @@ func (w *SnapshotWriter) EnsureSchema(ctx context.Context) error {
|
||||
if _, err := w.exec.ExecContext(ctx, realtimeLocationTableSQL); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := w.exec.ExecContext(ctx, realtimeKVTableSQL); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -129,7 +132,7 @@ func (w *SnapshotWriter) Update(ctx context.Context, env envelope.FrameEnvelope)
|
||||
eventTime := nullableTime(env.EventTimeMS)
|
||||
receivedAt := nullableTime(env.ReceivedAtMS)
|
||||
platformName := platformNameFromEnvelope(env)
|
||||
parsedJSON, err := w.parsedJSONForEnvelope(ctx, env, vin)
|
||||
parsed, err := w.parsedForEnvelope(ctx, env, vin)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -139,13 +142,16 @@ func (w *SnapshotWriter) Update(ctx context.Context, env envelope.FrameEnvelope)
|
||||
plate,
|
||||
platformName,
|
||||
env.SourceEndpoint,
|
||||
parsedJSON,
|
||||
marshalParsedJSON(parsed),
|
||||
eventTime,
|
||||
receivedAt,
|
||||
env.StableEventID(),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := w.upsertKV(ctx, env, parsed); err != nil {
|
||||
return err
|
||||
}
|
||||
location, ok := realtimeLocationFromEnvelope(env, vin, plate)
|
||||
if !ok {
|
||||
return nil
|
||||
@@ -170,7 +176,7 @@ func (w *SnapshotWriter) Update(ctx context.Context, env envelope.FrameEnvelope)
|
||||
return err
|
||||
}
|
||||
|
||||
func (w *SnapshotWriter) parsedJSONForEnvelope(ctx context.Context, env envelope.FrameEnvelope, vin string) (any, error) {
|
||||
func (w *SnapshotWriter) parsedForEnvelope(ctx context.Context, env envelope.FrameEnvelope, vin string) (map[string]any, error) {
|
||||
if len(env.Parsed) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -184,7 +190,32 @@ func (w *SnapshotWriter) parsedJSONForEnvelope(ctx context.Context, env envelope
|
||||
parsed = mergeParsedForProtocol(env.Protocol, existing, env.Parsed)
|
||||
}
|
||||
}
|
||||
return marshalParsedJSON(parsed), nil
|
||||
return parsed, nil
|
||||
}
|
||||
|
||||
func (w *SnapshotWriter) upsertKV(ctx context.Context, env envelope.FrameEnvelope, parsed map[string]any) error {
|
||||
rows := realtimeKVFields(env, parsed)
|
||||
if len(rows) == 0 {
|
||||
return nil
|
||||
}
|
||||
placeholders := make([]string, 0, len(rows))
|
||||
args := make([]any, 0, len(rows)*9)
|
||||
for _, row := range rows {
|
||||
placeholders = append(placeholders, "(?, ?, ?, ?, ?, ?, ?, ?, ?)")
|
||||
args = append(args,
|
||||
string(row.Protocol),
|
||||
row.VIN,
|
||||
row.Domain,
|
||||
row.Field,
|
||||
row.Value,
|
||||
row.ValueType,
|
||||
nullableTime(row.EventTimeMS),
|
||||
nullableTime(row.ReceivedAtMS),
|
||||
row.EventID,
|
||||
)
|
||||
}
|
||||
_, err := w.exec.ExecContext(ctx, upsertRealtimeKVSQLPrefix+strings.Join(placeholders, ",")+upsertRealtimeKVSQLSuffix, args...)
|
||||
return err
|
||||
}
|
||||
|
||||
func platformNameFromEnvelope(env envelope.FrameEnvelope) string {
|
||||
@@ -499,6 +530,37 @@ const realtimeLocationTableSQL = `CREATE TABLE IF NOT EXISTS vehicle_realtime_lo
|
||||
KEY idx_protocol_updated (protocol, updated_at)
|
||||
)`
|
||||
|
||||
const realtimeKVTableSQL = `CREATE TABLE IF NOT EXISTS vehicle_realtime_kv (
|
||||
protocol VARCHAR(32) NOT NULL,
|
||||
vin VARCHAR(32) NOT NULL,
|
||||
domain_name VARCHAR(64) NOT NULL,
|
||||
field_name VARCHAR(160) NOT NULL,
|
||||
field_value TEXT NULL,
|
||||
value_type VARCHAR(16) NOT NULL DEFAULT '',
|
||||
event_time DATETIME(3) NULL,
|
||||
received_at DATETIME(3) NULL,
|
||||
event_id VARCHAR(64) NOT NULL DEFAULT '',
|
||||
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (protocol, vin, domain_name, field_name),
|
||||
KEY idx_vin_domain (vin, domain_name),
|
||||
KEY idx_protocol_domain (protocol, domain_name)
|
||||
)`
|
||||
|
||||
const upsertRealtimeKVSQLPrefix = `
|
||||
INSERT INTO vehicle_realtime_kv
|
||||
(protocol, vin, domain_name, field_name, field_value, value_type, event_time, received_at, event_id)
|
||||
VALUES `
|
||||
|
||||
const upsertRealtimeKVSQLSuffix = `
|
||||
ON DUPLICATE KEY UPDATE
|
||||
field_value = IF(VALUES(event_time) IS NOT NULL AND (vehicle_realtime_kv.event_time IS NULL OR VALUES(event_time) >= vehicle_realtime_kv.event_time), VALUES(field_value), field_value),
|
||||
value_type = IF(VALUES(event_time) IS NOT NULL AND (vehicle_realtime_kv.event_time IS NULL OR VALUES(event_time) >= vehicle_realtime_kv.event_time), VALUES(value_type), value_type),
|
||||
event_time = IF(VALUES(event_time) IS NOT NULL AND (vehicle_realtime_kv.event_time IS NULL OR VALUES(event_time) >= vehicle_realtime_kv.event_time), VALUES(event_time), event_time),
|
||||
received_at = IF(VALUES(event_time) IS NOT NULL AND (vehicle_realtime_kv.event_time IS NULL OR VALUES(event_time) >= vehicle_realtime_kv.event_time), VALUES(received_at), received_at),
|
||||
event_id = IF(VALUES(event_time) IS NOT NULL AND (vehicle_realtime_kv.event_time IS NULL OR VALUES(event_time) >= vehicle_realtime_kv.event_time), VALUES(event_id), event_id),
|
||||
updated_at = IF(VALUES(event_time) IS NOT NULL AND (vehicle_realtime_kv.event_time IS NULL OR VALUES(event_time) >= vehicle_realtime_kv.event_time), CURRENT_TIMESTAMP, updated_at)
|
||||
`
|
||||
|
||||
const upsertRealtimeLocationSQL = `
|
||||
INSERT INTO vehicle_realtime_location
|
||||
(protocol, vin, plate, event_time, latitude, longitude, speed_kmh, total_mileage_km,
|
||||
|
||||
Reference in New Issue
Block a user