Remove the CTE and restore the generator with sub-queries, which is more performance (thanks Nick and Blake for testing against your larger DB!)

This commit is contained in:
Gdub
2026-06-26 11:08:53 -07:00
parent c575fb223b
commit ec1238e723
+54 -58
View File
@@ -392,6 +392,15 @@ def events_explore(
if not allowed_cameras: if not allowed_cameras:
return JSONResponse(content=[]) return JSONResponse(content=[])
distinct_labels = (
Event.select(Event.label)
.where(Event.camera << allowed_cameras)
.distinct()
.order_by(Event.label)
)
label_counts = {}
explore_columns = ( explore_columns = (
Event.id, Event.id,
Event.camera, Event.camera,
@@ -410,64 +419,31 @@ def events_explore(
Event.data, Event.data,
) )
# Single query: per-label COUNT and top-N ranking by start_time computed def event_generator():
# via window functions in a CTE, then filtered to rn <= limit for label_obj in distinct_labels.iterator():
event_count = ( label = label_obj.label
fn.COUNT(Event.id).over(partition_by=[Event.label]).alias("event_count")
) # get most recent events for this label
rn = ( label_events = (
fn.ROW_NUMBER() Event.select(*explore_columns)
.over(partition_by=[Event.label], order_by=[Event.start_time.desc()]) .where((Event.label == label) & (Event.camera << allowed_cameras))
.alias("rn") .order_by(Event.start_time.desc())
.limit(limit)
.iterator()
) )
base_query = Event.select( # count total events for this label
*explore_columns, label_counts[label] = (
event_count, Event.select()
rn, .where((Event.label == label) & (Event.camera << allowed_cameras))
).where(Event.camera << allowed_cameras) .count()
ranked = base_query.cte("ranked")
query = (
Event.select(
ranked.c.id,
ranked.c.camera,
ranked.c.label,
ranked.c.sub_label,
ranked.c.zones,
ranked.c.start_time,
ranked.c.end_time,
ranked.c.has_clip,
ranked.c.has_snapshot,
ranked.c.plus_id,
ranked.c.retain_indefinitely,
ranked.c.top_score,
ranked.c.false_positive,
ranked.c.box,
ranked.c.data,
ranked.c.event_count,
)
.from_(ranked)
.with_cte(ranked)
.where(ranked.c.rn <= limit)
.order_by(ranked.c.event_count.desc(), ranked.c.start_time.desc())
.objects()
) )
allowed_data_keys = { yield from label_events
"type",
"score",
"top_score",
"description",
"sub_label_score",
"average_estimated_speed",
"velocity_angle",
"path_data",
"recognized_license_plate",
"recognized_license_plate_score",
}
processed_events = [ def process_events():
{ for event in event_generator():
processed_event = {
"id": event.id, "id": event.id,
"camera": event.camera, "camera": event.camera,
"label": event.label, "label": event.label,
@@ -483,12 +459,32 @@ def events_explore(
"false_positive": event.false_positive, "false_positive": event.false_positive,
"box": event.box, "box": event.box,
"data": { "data": {
k: v for k, v in (event.data or {}).items() if k in allowed_data_keys k: v
}, for k, v in event.data.items()
"event_count": event.event_count, if k
} in [
for event in query "type",
"score",
"top_score",
"description",
"sub_label_score",
"average_estimated_speed",
"velocity_angle",
"path_data",
"recognized_license_plate",
"recognized_license_plate_score",
] ]
},
"event_count": label_counts[event.label],
}
yield processed_event
# convert iterator to list and sort
processed_events = sorted(
process_events(),
key=lambda x: (x["event_count"], x["start_time"]),
reverse=True,
)
return JSONResponse(content=processed_events) return JSONResponse(content=processed_events)