From db2f79806f66ce320828bbcf81f185f9d01faf23 Mon Sep 17 00:00:00 2001 From: Cal Corum Date: Tue, 3 Mar 2026 17:35:08 -0600 Subject: [PATCH] store: Batch BattingCard/BattingCardRatings lookups in paper-dynasty-database lineup builder --- ...ratings-lookups-in-paper-dynasty-ce86b4.md | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 graph/fixes/batch-battingcardbattingcardratings-lookups-in-paper-dynasty-ce86b4.md diff --git a/graph/fixes/batch-battingcardbattingcardratings-lookups-in-paper-dynasty-ce86b4.md b/graph/fixes/batch-battingcardbattingcardratings-lookups-in-paper-dynasty-ce86b4.md new file mode 100644 index 00000000000..b09273f39af --- /dev/null +++ b/graph/fixes/batch-battingcardbattingcardratings-lookups-in-paper-dynasty-ce86b4.md @@ -0,0 +1,46 @@ +--- +id: ce86b4d5-0738-46b5-b08c-06d454404a12 +type: fix +title: "Batch BattingCard/BattingCardRatings lookups in paper-dynasty-database lineup builder" +tags: [paper-dynasty-database, python, peewee, performance, batch-query, sqlite, fix] +importance: 0.6 +confidence: 0.8 +created: "2026-03-03T23:35:08.886774+00:00" +updated: "2026-03-03T23:35:08.886774+00:00" +--- + +## Problem +`get_bratings()` helper inside `build_lineup` endpoint (`app/routers_v2/teams.py`) called `BattingCard.get_or_none` + 2× `BattingCardRatings.get_or_none` per player — O(3N) DB round trips per lineup call. + +## Root Cause +Nested per-player queries inside a Peewee ORM function that was called for each position candidate in the lineup loop. + +## Solution +Before the position loop, batch-fetch all BattingCards and BattingCardRatings for legal_players ∪ backup_players using two bulk SELECTs. Store results in dicts: +- `_batting_cards_by_player`: `player_id → BattingCard` +- `_ratings_by_card_hand`: `card_id → {hand → BattingCardRatings}` + +Rewrite `get_bratings(player_id)` to do O(1) dict lookups. Error behaviour (AttributeError when card/rating missing) is preserved for existing try/except callers. + +## Pattern (reusable) +```python +_batch_bcards = BattingCard.select().where( + (BattingCard.player << legal_players) | (BattingCard.player << backup_players) +) +_batting_cards_by_player = {bc.player_id: bc for bc in _batch_bcards} +_all_bratings = ( + BattingCardRatings.select().where( + BattingCardRatings.battingcard << list(_batting_cards_by_player.values()) + ) + if _batting_cards_by_player else [] +) +_ratings_by_card_hand = {} +for _r in _all_bratings: + _ratings_by_card_hand.setdefault(_r.battingcard_id, {})[_r.vs_hand] = _r +``` + +## Files Changed +- `app/routers_v2/teams.py` (lines 366–400) + +## PR +https://git.manticorum.com/cal/paper-dynasty-database/pulls/45 (issue #18)