Repository navigation
feat(rankings): dynamic sort & filter by any metric - #6
Merged
Merged
Conversation
Add allowlist-validated dynamic sorting and numeric filtering to the
player rankings, running server-side over the full paginated result set.
Backend:
- metric_fields.py: single-source allowlist (Stats + score fields) mapping
each public metric to its Mongo path / PlayerDTO accessor; doubles as the
injection guard for sort_by and filters.
- GET /v1/players: sort_by now accepts any allowlisted metric; new filters
param takes a JSON array of {field, op, value} clauses. 422 on unknown
field / bad op / non-numeric value.
- get_players: generic Mongo sort + numeric filter clauses; stats_view
Python path applies the same filter/sort after re-aggregation.
Frontend:
- METRIC_OPTIONS / FILTER_OP_OPTIONS / serializeFilters helpers.
- PlayerTable: clickable sortable column headers with asc/desc indicator.
- FilterBar: stackable metric filter builder (metric / op / value).
- Rankings: sort + filter state wired into the query; page resets on change.
- Add the 8 remaining typed Stats fields to the frontend metric list (pk_saved/pk_taken, penalty_miss/conceded/faced, yellow_red/direct_red cards, scoring_frequency) so the UI matches the backend allowlist. - Remove eq/ne filter operators (FE + BE): exact equality on continuous float metrics is a footgun; keep gte/lte/gt/lt.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds server-side dynamic sorting and numeric filtering to the player Rankings page. Previously the table was locked to sorting by
s_finaland only supported categorical filters (position/team/nationality/flag). Users can now order and filter the full paginated ranking by any typed metric (xG, xA, goals, assists, clean sheets, rating, saves, …).Because the table is server-paginated, sort/filter must run in the backend over the whole result set — a client-side sort would only reorder the visible 50 rows.
Backend
app/domain/metric_fields.py(new) — single-source allowlist of sortable/filterable metrics (everyStatsfield + the 4 score fields), auto-derived from theStatsdataclass. Doubles as the injection guard and maps each metric to its Mongo path /PlayerDTOaccessor.GET /v1/players—sort_bynow accepts any allowlisted metric (was locked tos_final); newfiltersparam takes a JSON array of{field, op, value}clauses (gte/lte/gt/lt). Returns422on unknown field / bad op / non-numeric value.get_players— generic Mongo sort + numeric filter clauses on the default path; thestats_viewre-aggregation path applies the same filter/sort in Python after recomputing. Filter/sort always run before pagination.Frontend
PlayerTable).FilterBar).METRIC_OPTIONS/FILTER_OP_OPTIONS/serializeFiltershelpers; sort + filter state wired into the query with page reset on change (Rankings).Tests
stats_viewpath.422validation for unknown field / malformed JSON / bad op / non-numeric value.Notes
player_statsdocs, so no DB repopulation needed.eq/neoperators intentionally omitted (exact equality on continuous floats is a footgun).stats_view) still pending against the running stack.🤖 Generated with Claude Code