mirror of
https://github.com/jamiepine/voicebox.git
synced 2026-09-15 04:40:40 -07:00
Batch story item counts in list_stories to eliminate N+1 (#663)
list_stories() previously executed one COUNT(story_items) query per story in a Python loop. With N stories that is N+1 round-trips to SQLite regardless of list length. Replace with a single aggregated GROUP BY query that fetches all counts at once, then populate each StoryResponse from a dict lookup.
This commit is contained in:
committed by
Jamie Pine
parent
46fe1c3608
commit
0f2032f058
@@ -125,12 +125,24 @@ async def list_stories(
|
||||
"""
|
||||
stories = db.query(DBStory).order_by(DBStory.updated_at.desc()).all()
|
||||
|
||||
if not stories:
|
||||
return []
|
||||
|
||||
# Batch-fetch all story item counts in one query to avoid an N+1 pattern
|
||||
# (previously there was one COUNT query per story in the loop below).
|
||||
story_ids = [s.id for s in stories]
|
||||
count_rows = (
|
||||
db.query(DBStoryItem.story_id, func.count(DBStoryItem.id).label("cnt"))
|
||||
.filter(DBStoryItem.story_id.in_(story_ids))
|
||||
.group_by(DBStoryItem.story_id)
|
||||
.all()
|
||||
)
|
||||
item_counts = {row.story_id: row.cnt for row in count_rows}
|
||||
|
||||
result = []
|
||||
for story in stories:
|
||||
item_count = db.query(func.count(DBStoryItem.id)).filter(DBStoryItem.story_id == story.id).scalar()
|
||||
|
||||
response = StoryResponse.model_validate(story)
|
||||
response.item_count = item_count
|
||||
response.item_count = item_counts.get(story.id, 0)
|
||||
result.append(response)
|
||||
|
||||
return result
|
||||
|
||||
Reference in New Issue
Block a user