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
+73 -77
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,86 +419,73 @@ 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")
)
rn = (
fn.ROW_NUMBER()
.over(partition_by=[Event.label], order_by=[Event.start_time.desc()])
.alias("rn")
)
base_query = Event.select( # get most recent events for this label
*explore_columns, label_events = (
event_count, Event.select(*explore_columns)
rn, .where((Event.label == label) & (Event.camera << allowed_cameras))
).where(Event.camera << allowed_cameras) .order_by(Event.start_time.desc())
ranked = base_query.cte("ranked") .limit(limit)
query = ( .iterator()
Event.select( )
ranked.c.id,
ranked.c.camera, # count total events for this label
ranked.c.label, label_counts[label] = (
ranked.c.sub_label, Event.select()
ranked.c.zones, .where((Event.label == label) & (Event.camera << allowed_cameras))
ranked.c.start_time, .count()
ranked.c.end_time, )
ranked.c.has_clip,
ranked.c.has_snapshot, yield from label_events
ranked.c.plus_id,
ranked.c.retain_indefinitely, def process_events():
ranked.c.top_score, for event in event_generator():
ranked.c.false_positive, processed_event = {
ranked.c.box, "id": event.id,
ranked.c.data, "camera": event.camera,
ranked.c.event_count, "label": event.label,
) "zones": event.zones,
.from_(ranked) "start_time": event.start_time,
.with_cte(ranked) "end_time": event.end_time,
.where(ranked.c.rn <= limit) "has_clip": event.has_clip,
.order_by(ranked.c.event_count.desc(), ranked.c.start_time.desc()) "has_snapshot": event.has_snapshot,
.objects() "plus_id": event.plus_id,
"retain_indefinitely": event.retain_indefinitely,
"sub_label": event.sub_label,
"top_score": event.top_score,
"false_positive": event.false_positive,
"box": event.box,
"data": {
k: v
for k, v in event.data.items()
if k
in [
"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,
) )
allowed_data_keys = {
"type",
"score",
"top_score",
"description",
"sub_label_score",
"average_estimated_speed",
"velocity_angle",
"path_data",
"recognized_license_plate",
"recognized_license_plate_score",
}
processed_events = [
{
"id": event.id,
"camera": event.camera,
"label": event.label,
"zones": event.zones,
"start_time": event.start_time,
"end_time": event.end_time,
"has_clip": event.has_clip,
"has_snapshot": event.has_snapshot,
"plus_id": event.plus_id,
"retain_indefinitely": event.retain_indefinitely,
"sub_label": event.sub_label,
"top_score": event.top_score,
"false_positive": event.false_positive,
"box": event.box,
"data": {
k: v for k, v in (event.data or {}).items() if k in allowed_data_keys
},
"event_count": event.event_count,
}
for event in query
]
return JSONResponse(content=processed_events) return JSONResponse(content=processed_events)