Skip to content

Cap route-type searches after the service filter - #1272

Open
Mister-Raggs wants to merge 2 commits into
OneBusAway:mainfrom
Mister-Raggs:fix/1268-routetype-truncation-ordering
Open

Cap route-type searches after the service filter#1272
Mister-Raggs wants to merge 2 commits into
OneBusAway:mainfrom
Mister-Raggs:fix/1268-routetype-truncation-ordering

Conversation

@Mister-Raggs

@Mister-Raggs Mister-Raggs commented Aug 1, 2026

Copy link
Copy Markdown
Collaborator

Fixes #1268 (sub-issue of the #1235 spec review).

Route-type searches capped the candidate list in GetStopsForLocation, but stops with no service on the queried date are dropped afterwards in the handler. The cap therefore ran against a set that was still about to shrink, and the response could fall far below maxCount while limitExceeded claimed results had been withheld.

Measured on the RABA fixture (clock 2025-12-26, lat=40.583321&lon=-122.426966&radius=5000&routeType=3):

maxCount before after
2 list=0, limitExceeded=true list=2, limitExceeded=true
5 list=1, limitExceeded=true list=5, limitExceeded=true
1000 list=122, limitExceeded=false unchanged

Asking for two stops returned none, out of 122 matches.

This is the ordering issue recorded as Suspected Defect #4 ("the limit check in the geospatial path runs before route filtering… a clean reimplementation would not have this ordering issue"), and the result contradicts DC-6's intent that the list is capped at maxCount with limitExceeded signalling discarded results.

Approach

The cap moves downstream of the service filter, into the handler, where the surviving stops are known. Truncation still happens before the combined-ID sort, so the nearest matches are the ones kept.

Bounds searches (no routeType) are untouched — they keep capping upstream, which is the documented legacy ordering from Main Success Scenario step 5, not a defect.

Note

The candidate set handed to the batch queries is now the full route-type match within the search bounds rather than a pre-capped slice, since which stops survive the service filter isn't knowable before running it. That's the same shape legacy has.

Tests cover both cap sizes and the under-cap case; the capped cases were confirmed to fail against the previous ordering.

Verified: full suite in both sqlite_fts5 and purego modes, go vet both, race detector on restapi/gtfs, gofmt.

Summary by CodeRabbit

  • Bug Fixes

    • Improved location-based stop searches with route-type filters by removing inactive stops before applying result limits.
    • Corrected result counts and limit notifications so they accurately reflect the stops available for the selected date.
  • Tests

    • Added coverage for capped results and cases where all matching stops fit within the configured limit.

Route-type searches capped the candidate list before stops with no
service on the queried date were dropped, so the response could fall far
below maxCount while limitExceeded still claimed results were withheld.
On the RABA fixture, maxCount=2 returned nothing at all where 122 stops
matched, and maxCount=5 returned one.

Move that cap downstream of the service filter so the list fills to
maxCount and limitExceeded reports a real overflow. Bounds searches keep
capping upstream, which is the documented legacy ordering.

Fixes OneBusAway#1268
Copilot AI review requested due to automatic review settings August 1, 2026 02:26
@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@Mister-Raggs, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 38 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 5d62118b-6829-45b0-b02a-3c03ef162a14

📥 Commits

Reviewing files that changed from the base of the PR and between e34cacd and 55801be.

📒 Files selected for processing (2)
  • internal/restapi/stops_for_location_handler.go
  • internal/restapi/stops_for_location_handler_test.go
📝 Walkthrough

Walkthrough

The route-type stop search now applies maxCount after inactive stops are removed. It truncates stops and raw IDs together and sets LimitExceeded only when results are discarded. Tests cover capped and uncapped results.

Changes

Route-type result capping

Layer / File(s) Summary
Defer and apply route-type capping
internal/gtfs/gtfs_manager.go, internal/restapi/stops_for_location_handler.go
The GTFS manager no longer truncates route-type results before service filtering. The REST handler now caps filtered stops and raw IDs at maxCount and sets limitExceeded.
Validate capping behavior
internal/restapi/stops_for_location_handler_test.go
Tests verify capping after inactive-stop filtering and confirm that results under a high cap do not set LimitExceeded.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

Suggested labels: LGTM

Suggested reviewers: copilot, burma-shave

🚥 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 clearly describes the primary change: applying route-type result caps after service filtering.
Linked Issues check ✅ Passed The PR satisfies issue [#1268] by capping route-type results after service filtering and adding regression tests for overflow and under-cap cases.
Out of Scope Changes check ✅ Passed All production and test changes directly support route-type capping and accurate limitExceeded reporting described in [#1268].
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

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

@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: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/restapi/stops_for_location_handler_test.go`:
- Around line 151-175: Update
TestStopsForLocationRouteTypeCapsAfterServiceFilter to assert the returned stop
IDs, or their set, for each capped maxCount case in addition to length and
LimitExceeded. Verify the IDs represent the nearest route-type matches selected
before combined-ID sorting, while preserving the existing response-status and
count assertions.

In `@internal/restapi/stops_for_location_handler.go`:
- Around line 230-236: After the route-type truncation in the stops-for-location
handler, rebuild agencyIDs and routeIDs from the retained resultRawStopIDs so
references.agencies and references.routes only include IDs belonging to returned
stops. Keep the existing result and limit-exceeded behavior unchanged, and
perform this before constructing or returning the response.
🪄 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: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 6cbeb49e-b9bf-485b-b1db-cb0803bbde0f

📥 Commits

Reviewing files that changed from the base of the PR and between 3b73fd9 and e34cacd.

📒 Files selected for processing (3)
  • internal/gtfs/gtfs_manager.go
  • internal/restapi/stops_for_location_handler.go
  • internal/restapi/stops_for_location_handler_test.go

Comment thread internal/restapi/stops_for_location_handler_test.go
Comment thread internal/restapi/stops_for_location_handler.go

Copilot AI 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.

Pull request overview

Moves stops-for-location route-type result truncation to occur after the handler’s active-service filter so maxCount and limitExceeded reflect the final (post-filter) stop list, matching the intended endpoint behavior.

Changes:

  • Shift route-type truncation from GtfsManager.GetStopsForLocation into the REST handler after inactive stops are dropped.
  • Preserve bounds-search behavior (still capped upstream).
  • Add regression tests covering capped and under-cap route-type searches.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
internal/restapi/stops_for_location_handler.go Applies route-type truncation after service-date filtering (downstream cap).
internal/restapi/stops_for_location_handler_test.go Adds regression coverage for route-type cap ordering and under-cap behavior.
internal/gtfs/gtfs_manager.go Removes the upstream cap for route-type mode to defer truncation downstream.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +232 to +236
if len(routeTypes) > 0 && len(results) > maxCount {
results = results[:maxCount]
resultRawStopIDs = resultRawStopIDs[:maxCount]
isLimitExceeded = true
}
Agency and route references were collected from every candidate stop, so
capping the list left references describing stops the caller never saw.
With routeType=3 and maxCount=2 the response carried twelve routes for
two stops that use one between them.

Collect them from the returned stops instead, after the cap is applied.
Assert the surviving stop IDs as well as the count, so the test covers
which stops the cap keeps rather than only how many.
@sonarqubecloud

sonarqubecloud Bot commented Aug 1, 2026

Copy link
Copy Markdown

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.

stops-for-location: pin routeType truncation behaviour with a regression test

2 participants