Skip to content

feat: implement database indexing and premium UI for historical game …#706

Open
AbuJulaybeeb wants to merge 2 commits into
NOVUS-X:mainfrom
AbuJulaybeeb:feat/game-search-554
Open

feat: implement database indexing and premium UI for historical game …#706
AbuJulaybeeb wants to merge 2 commits into
NOVUS-X:mainfrom
AbuJulaybeeb:feat/game-search-554

Conversation

@AbuJulaybeeb

@AbuJulaybeeb AbuJulaybeeb commented Apr 28, 2026

Copy link
Copy Markdown

Implemented advanced historical game search functionality across the stack. This includes database-level optimizations for high-speed queries and a premium Next.js UI for the user dashboard.

Changes

  • Database: Added migrations for result, variant, and composite head-to-head indexes in the smdb schema.
  • API/Service: Enhanced the list_games endpoint with advanced filtering parameters (opponent, result, variant, date range).
  • UI/UX: Updated the RecentMatches dashboard component with a high-contrast search and filter system, ensuring a premium feel consistent with XLMate's aesthetic.

Verification

Summary by CodeRabbit

  • New Features

    • Advanced game-history filters: search by opponent, variant, result (win/loss/draw), and date range.
    • Interactive match history UI: opponent search, outcome filter, updated headers, empty-state and reset controls.
  • Performance

    • Added indexes to speed historical and head‑to‑head game searches and variant/result lookups.

@coderabbitai

coderabbitai Bot commented Apr 28, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5f4ba989-2bdc-43d5-b17b-2c4fdd30ac25

📥 Commits

Reviewing files that changed from the base of the PR and between 9640e59 and 2c557d0.

📒 Files selected for processing (3)
  • backend/modules/db/migrations/src/m20260428_100000_add_historical_search_indexes.rs
  • backend/modules/service/src/games.rs
  • frontend/components/dashboard/RecentMatches.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
  • backend/modules/db/migrations/src/m20260428_100000_add_historical_search_indexes.rs
  • backend/modules/service/src/games.rs
  • frontend/components/dashboard/RecentMatches.tsx

📝 Walkthrough

Walkthrough

Extends game-list filtering end-to-end and adds DB indexes: API and DTO accept opponent/variant/result/date filters, GameService applies head-to-head, variant, result and date constraints, a migration adds result/variant/head-to-head indexes, and RecentMatches gains client-side interactive filtering.

Changes

Cohort / File(s) Summary
API / DTO / Service
backend/modules/api/src/games.rs, backend/modules/dto/src/games.rs, backend/modules/service/src/games.rs
Adds optional filters: opponent_id, variant, result_side, from_date, to_date. API forwards new params; DTO includes new fields; service applies head-to-head player matching, maps variant/result strings to enums, and applies date-range filters.
Database migrations
backend/modules/db/migrations/src/m20260428_100000_add_historical_search_indexes.rs, backend/modules/db/migrations/src/lib.rs
New migration registering indexes: idx_games_result, idx_games_variant_search, and two composite head-to-head indexes for (white_player, black_player, created_at DESC) and its mirror; migration registered in registry.
Frontend RecentMatches
frontend/components/dashboard/RecentMatches.tsx
Reworks component into interactive history view: opponent search, W/L/D filter, memoized filtered list (reversed/sliced), updated columns/labels, date formatting, and empty-state with reset control.

Sequence Diagram

sequenceDiagram
    actor User
    participant Frontend as Frontend UI
    participant API as API Handler
    participant Service as GameService
    participant DB as Database

    User->>Frontend: set opponent / result / date filters
    Frontend->>Frontend: update state & compute filteredMatches
    Frontend->>API: GET /games?player_id&opponent_id&variant&result_side&from_date&to_date
    API->>Service: list_games(player_id, opponent_id, variant, result_side, from_date, to_date)
    Service->>Service: build query (player/head‑to‑head, variant, result, created_at bounds)
    Service->>DB: SELECT ... FROM game WHERE ... (uses indexes)
    DB-->>Service: return rows
    Service-->>API: games[]
    API-->>Frontend: games[] (JSON)
    Frontend->>Frontend: render latest 10 of filteredGames
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 I hopped through indexes, fields, and time,
I nibbled filters, variant and rhyme.
Head-to-head paths now brightly unfurled,
Matches returned to my curious world.
I twitch my whiskers — history, compile! 🥕

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: implementing database indexing and premium UI for historical game search functionality across the stack.
Linked Issues check ✅ Passed The PR implements all coding objectives from issue #554: database indexing (result, variant, head-to-head indexes), API filtering enhancements, and premium UI updates with search/filter in RecentMatches component.
Out of Scope Changes check ✅ Passed All changes are directly related to the historical game search feature: database migrations add required indexes, API/service layers implement filtering parameters, and frontend updates the RecentMatches component with search/filter UI.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 0/1 reviews remaining, refill in 60 minutes.

Comment @coderabbitai help to get the list of available commands and usage tips.

@drips-wave

drips-wave Bot commented Apr 28, 2026

Copy link
Copy Markdown

@AbuJulaybeeb Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
backend/modules/service/src/games.rs (1)

397-465: ⚠️ Potential issue | 🟡 Minor

Add coverage for the new filter branches.

These updated calls only prove the old happy path still compiles. Please add cases for opponent_id, variant, result_side, and the date range so the new SQL branches are exercised.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/modules/service/src/games.rs` around lines 397 - 465, Add tests that
call GameService::list_games with non-None values for opponent_id, variant,
result_side, and the date range so the new SQL branches are exercised: create
new tokio::test functions (or extend existing tests test_list_games and
test_list_games_with_cursor) that set opponent_id (Uuid), variant
(db_entity::game::GameVariant::...), result_side (appropriate enum/value), and
from_timestamp/to_timestamp (Utc times or encoded cursor) and supply a
MockDatabase with append_query_results followed by into_connection; then invoke
GameService::list_games with those parameters and assert the transaction_log
contains the expected WHERE clauses (references to "opponent", variant, result
filtering, and date range conditions) to ensure the new branches hit in
GameService::list_games.
backend/modules/api/src/games.rs (1)

183-188: ⚠️ Potential issue | 🟡 Minor

Keep the OpenAPI params in sync with the handler.

list_games handler accepts opponent_id, variant, result_side, from_date, and to_date via the ListGamesQuery struct, but the #[utoipa::path(params(...))] block at lines 183–188 does not document them. Generated API docs will be incomplete for these historical-search filters.

📚 Proposed fix
     params(
         ("status"    = Option<String>, Query, description = "Filter by status (waiting, in_progress, completed, aborted)"),
         ("player_id" = Option<String>, Query, description = "Filter by player UUID", format = "uuid"),
+        ("opponent_id" = Option<String>, Query, description = "Filter by opponent UUID", format = "uuid"),
+        ("variant"    = Option<String>, Query, description = "Filter by variant"),
+        ("result_side" = Option<String>, Query, description = "Filter by result side"),
+        ("from_date"  = Option<String>, Query, description = "Inclusive start date", format = "date-time"),
+        ("to_date"    = Option<String>, Query, description = "Inclusive end date", format = "date-time"),
         ("page"      = Option<i32>,    Query, description = "Page number"),
         ("limit"     = Option<i32>,    Query, description = "Items per page")
     ),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/modules/api/src/games.rs` around lines 183 - 188, The OpenAPI params
in the #[utoipa::path(...)] block are missing filters that the list_games
handler accepts; update the params to mirror the ListGamesQuery fields
(opponent_id, variant, result_side, from_date, to_date) so generated docs match
the handler. Locate the utoipa path for list_games and add param entries for
("opponent_id" = Option<String>, Query, format = "uuid"), ("variant" =
Option<String>, Query), ("result_side" = Option<String>, Query), ("from_date" =
Option<String>, Query, format = "date-time"), and ("to_date" = Option<String>,
Query, format = "date-time") to match ListGamesQuery types and descriptions.
Ensure names and types match the ListGamesQuery struct and keep existing params
(status, player_id, page, limit) intact. Verify documentation builds and the
generated OpenAPI spec includes all these fields.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@backend/modules/db/migrations/src/m20260428_100000_add_historical_search_indexes.rs`:
- Around line 20-27: The migration creates a one-direction composite index
"idx_games_head_to_head" on ("white_player", "black_player", "created_at" DESC)
but GameService::list_games queries both (white,black) and the reverse order;
add a second, mirrored composite index on ("black_player", "white_player",
"created_at" DESC) (e.g., name it "idx_games_head_to_head_rev") by issuing a
second execute_unprepared call on manager.get_connection() to CREATE INDEX for
"smdb"."game" so both search branches are optimized.
- Around line 48-51: The DROP INDEX in the down() migration is missing the
schema qualifier for idx_games_head_to_head; update the execute_unprepared call
that currently runs DROP INDEX IF EXISTS "idx_games_head_to_head" (inside
down()) to explicitly reference the smdb schema (e.g. DROP INDEX IF EXISTS
"smdb"."idx_games_head_to_head") so it matches the other schema-qualified drops
and avoids search_path dependency; modify the string passed to
manager.get_connection().execute_unprepared(...) accordingly.

In `@backend/modules/service/src/games.rs`:
- Around line 213-221: The variant mapping silently defaults unknown or mistyped
inputs to Standard and misses several GameVariant members; update the mapping
where variant is handled (the block that builds v_enum from variant.as_str() and
then calls query.filter(game::Column::Variant.eq(v_enum))) to include all enum
string forms used by db_entity::game::GameVariant (Standard, Chess960,
ThreeCheck (use "three_check"), Blitz, Rapid, Classical) and ensure the match
arm uses the correct string keys (e.g., "three_check" not "three-check");
alternatively, return an error or ignore the filter for any unrecognized input
instead of defaulting to Standard so typos do not produce incorrect results.
- Around line 223-233: The current mapping of result_side silently coerces
unknown values to ResultSide::Ongoing; instead validate result_side, explicitly
handle "ongoing" by filtering for NULL with query =
query.filter(game::Column::Result.is_null()), map
"white_wins"/"black_wins"/"draw"/"abandoned" to db_entity::game::ResultSide
variants as before, and return an error (or early-bail) for any other string
rather than defaulting to Ongoing; update the code around the result_side
handling (the result_side variable, the r_enum match, and the
query.filter(game::Column::Result.eq(...)) call) to implement these checks and
the NULL filter.

In `@frontend/components/dashboard/RecentMatches.tsx`:
- Around line 58-88: The search input and icon-only filter toggle need
accessible names: add an explicit aria-label (or a visually-hidden <label> tied
to the input id) to the input controlled by searchQuery/setSearchQuery (e.g.,
"Search opponents"), add an aria-label to the clear button (the X button) such
as "Clear search", and add aria-label (e.g., "Toggle filters") plus
aria-expanded={showFilters} to the filter toggle button that calls
setShowFilters; ensure labels are descriptive for screen readers and that
aria-expanded reflects the showFilters state.
- Around line 1-4: This file defines the RecentMatches component and uses React
hooks (useState, useMemo) but lacks the client directive; add the "use client"
directive as the very first line of
frontend/components/dashboard/RecentMatches.tsx so the component runs as a
Client Component and can use hooks (locate the file around the import block
referencing useState/useMemo and symbols like RecentMatches, EloDataPoint, cn,
and lucide-react imports).

---

Outside diff comments:
In `@backend/modules/api/src/games.rs`:
- Around line 183-188: The OpenAPI params in the #[utoipa::path(...)] block are
missing filters that the list_games handler accepts; update the params to mirror
the ListGamesQuery fields (opponent_id, variant, result_side, from_date,
to_date) so generated docs match the handler. Locate the utoipa path for
list_games and add param entries for ("opponent_id" = Option<String>, Query,
format = "uuid"), ("variant" = Option<String>, Query), ("result_side" =
Option<String>, Query), ("from_date" = Option<String>, Query, format =
"date-time"), and ("to_date" = Option<String>, Query, format = "date-time") to
match ListGamesQuery types and descriptions. Ensure names and types match the
ListGamesQuery struct and keep existing params (status, player_id, page, limit)
intact. Verify documentation builds and the generated OpenAPI spec includes all
these fields.

In `@backend/modules/service/src/games.rs`:
- Around line 397-465: Add tests that call GameService::list_games with non-None
values for opponent_id, variant, result_side, and the date range so the new SQL
branches are exercised: create new tokio::test functions (or extend existing
tests test_list_games and test_list_games_with_cursor) that set opponent_id
(Uuid), variant (db_entity::game::GameVariant::...), result_side (appropriate
enum/value), and from_timestamp/to_timestamp (Utc times or encoded cursor) and
supply a MockDatabase with append_query_results followed by into_connection;
then invoke GameService::list_games with those parameters and assert the
transaction_log contains the expected WHERE clauses (references to "opponent",
variant, result filtering, and date range conditions) to ensure the new branches
hit in GameService::list_games.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 45eb751a-1234-4bec-be01-3f95be9e7e44

📥 Commits

Reviewing files that changed from the base of the PR and between dc6c5dd and 9640e59.

📒 Files selected for processing (6)
  • backend/modules/api/src/games.rs
  • backend/modules/db/migrations/src/lib.rs
  • backend/modules/db/migrations/src/m20260428_100000_add_historical_search_indexes.rs
  • backend/modules/dto/src/games.rs
  • backend/modules/service/src/games.rs
  • frontend/components/dashboard/RecentMatches.tsx

Comment thread backend/modules/service/src/games.rs
Comment thread backend/modules/service/src/games.rs
Comment thread frontend/components/dashboard/RecentMatches.tsx
Comment thread frontend/components/dashboard/RecentMatches.tsx
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Backend: Database indexing for historical game search

1 participant