New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add option to include stations in nearest
search
#5390
Conversation
Codecov ReportAttention:
Additional details and impacted files@@ Coverage Diff @@
## dev-2.x #5390 +/- ##
=============================================
+ Coverage 67.36% 67.45% +0.08%
- Complexity 16167 16193 +26
=============================================
Files 1858 1858
Lines 71093 71126 +33
Branches 7403 7412 +9
=============================================
+ Hits 47893 47976 +83
+ Misses 20743 20678 -65
- Partials 2457 2472 +15 ☔ View full report in Codecov by Sentry. |
if (o instanceof Station) { | ||
return schema.getObjectType("Stop"); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the the if statement above this can be expanded to check for Station also and this block can be removed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Run the optimize imports
thing on intellij to get rid of extra imports here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here
placesFound.add(new PlaceAtDistance(stop, distance)); | ||
seenStops.add(stop.getId()); | ||
if ( | ||
includeStopsAndStations && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code is now inside the includeStops
block. I think we should separate these so includeStops
doesn't need to be and probably shouldn't be true when includeStopsAndStations
is true (at least on the GraphQL argument level). I'm not quite sure what should happen if someone has defined both filters as true, should we include the stations in addition to their stops or should we just throw some exception that this combination of filters is not allowed.
includeStopsAndStations && | ||
stop.getParentStation() != null && | ||
!seenStops.contains(stop.getParentStation().getId()) && | ||
!seenStops.contains(stop.getId()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't this check unnecessary because if the stop has a parent station, we add the parent stations id to the seenStops
, not the stop's.
src/main/java/org/opentripplanner/routing/graphfinder/PlaceFinderTraverseVisitor.java
Outdated
Show resolved
Hide resolved
} else { | ||
// Do not accumulate the same station many times | ||
if ( | ||
stop.getParentStation() != null && seenStops.contains(stop.getParentStation().getId()) | ||
) return; | ||
seenStops.add(stop.getId()); | ||
placesFound.add(new PlaceAtDistance(stop, distance)); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I took a while for me to understand this logic. I think it would be simpler if we first check if a stop has a parent station or not and then inside those blocks do other checks etc.
@@ -986,6 +986,10 @@ enum FilterPlaceType { | |||
|
|||
"""Parking lots that contain spaces for cars""" | |||
CAR_PARK | |||
|
|||
|
|||
"""Stops or stations""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should add more documentation here what this actually does and also it's relation to the STOP filter (and vice versa on the STOP filter).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would vote to rename this to STATION
and the following semantics:
STATION
: only return stations, not stops. if a single stop is within radius then its station is also.STOP
: only return stops, not stationsSTATION,STOP
: return all stations but only those stops that don't have a parent station. if a single stop is within radius then its parent station is also.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok I think agree with you, lets do it like that. We need to document this behaviour well here in the schema.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, good documentation is important because it's going to be confusing no matter what the semantics will be.
@@ -80,6 +81,8 @@ public PlaceFinderTraverseVisitor( | |||
includeVehicleRentals = shouldInclude(filterByPlaceTypes, PlaceType.VEHICLE_RENT); | |||
includeCarParking = shouldInclude(filterByPlaceTypes, PlaceType.CAR_PARK); | |||
includeBikeParking = shouldInclude(filterByPlaceTypes, PlaceType.BIKE_PARK); | |||
includeStopsAndStations = shouldInclude(filterByPlaceTypes, PlaceType.STOP_OR_STATION); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think now this is true by default if no filters are defined. However, I think we maybe shouldn't have this true by default as the client software might have existing handling for when Stop type objects are returned which might break as now this sometimes would return stations which also use Stop type but have some crucial fields as null. Also again, now by default we have both STOP and STOP_OR_STATION on by default and it's a bit unclear what it means.
// Do not accumulate the same station many times | ||
if ( | ||
stop.getParentStation() != null && seenStops.contains(stop.getParentStation().getId()) | ||
) return; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Try to always wrap things inside an if in a {} block and preferable also split that block to a new line as it's easy to miss something happening on the same line where there is an if statement.
I wonder if we already have some existing tests for this part of the code and how difficult it would be to write a test for the GraphQL API. @leonardehrenfried probably has a better idea. |
There are tests for other visitors which you can use as a template:
You will likely have to add methods to |
nearby
search
@miles-grant-ibigroup @daniel-heppner-ibigroup @binh-dam-ibigroup You should probably know that this change is coming. |
nearby
searchnearest
search
This can be disabled right? Or like not included in the query |
If you actively select what kind of items you want, nothing will change. I don't think we haven't quite decided if the stations will be included by default or not. |
If the default changes to include stations but we aren't looking for the station type on the nearest response, I think that means we could be missing stops with a parent station, correct? Our options would be to either support stations or set the default to only include stops, it seems. It isn't a big deal to update that, especially since the nearby view isn't merged yet on our end. |
Yes, it will start returning Station from which you have to fetch the child stops. The safest option for you would be to explicitly select what you want and then nothing will change. |
I don't think we should return stations by default, it's not the most completely obvious selection and it has some backwards compatibility issues as stations and stops use the same type but don't contain the same information. |
src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls
Outdated
Show resolved
Hide resolved
src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls
Outdated
Show resolved
Hide resolved
@@ -80,6 +81,8 @@ public PlaceFinderTraverseVisitor( | |||
includeVehicleRentals = shouldInclude(filterByPlaceTypes, PlaceType.VEHICLE_RENT); | |||
includeCarParking = shouldInclude(filterByPlaceTypes, PlaceType.CAR_PARK); | |||
includeBikeParking = shouldInclude(filterByPlaceTypes, PlaceType.BIKE_PARK); | |||
includeStations = shouldInclude(filterByPlaceTypes, PlaceType.STATION); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need special handling for this as we don't want to return stations by default.
This fact needs to also documented in the query docs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there some better way to do so than to simply add a check for whether filterByPlaceTypes
is empty? Where exactly are the query docs that should be changed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, that's fine.
The docs to mentioned this are here:
OpenTripPlanner/src/main/resources/org/opentripplanner/apis/gtfs/schema.graphqls
Lines 2503 to 2506 in 7b48c4d
""" | |
Only return places that are one of these types, e.g. `STOP` or `VEHICLE_RENT` | |
""" | |
filterByPlaceTypes: [FilterPlaceType] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please also add a test for this slightly strange behaviour.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please make sure that stations are only included if you explicitly enable them.
Co-authored-by: Leonard Ehrenfried <mail@leonard.io>
Can you please also write a test for the GraphQL aspect? You will have to write a query like this: https://github.com/opentripplanner/OpenTripPlanner/blob/621b802d93a2eb595b645b77be21a36da7314842/src/test/resources/org/opentripplanner/apis/gtfs/queries/patterns.graphql#L1-L0 but for the nearest query. You then need to run In order to get useful results for the query, you might have to write a test implementation of that interface: OpenTripPlanner/src/main/java/org/opentripplanner/routing/graphfinder/GraphFinder.java Line 18 in 581eae5
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The stop ids filtering doesn't seem to work. Also, while testing this, I realized that the filtering is broken(?) also upstream. In OTP1 the nearest search only returned the entities that were included in the filters. In OTP2, the nearest search returns only the entities of a type that had some id filter if they were included in it but also additionally all entities of different types if there were no filters for those. Based on the documentation and how it was OTP1, I think we should only return the elements that were included in id filters if any id filters are specified for any type. I think that behaviour should be fixed within the scope of this pr to match the OTP1 behaviour.
!seenStops.contains(stop.getParentStation().getId()) && | ||
stopIsIncludedByStationFilter(stop) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need these two checks as they are already checked above?
placesFound.add(new PlaceAtDistance(stop, distance)); | ||
seenStops.add(stop.getParentStation().getId()); | ||
placesFound.add(new PlaceAtDistance(stop.getParentStation(), distance)); | ||
} else if (includeStops && !seenStops.contains(stop.getId())) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same with this seenStops check.
src/test/java/org/opentripplanner/routing/graphfinder/PlaceFinderTraverseVisitorTest.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file is in the old resource path before the api was moved into the core code. Is this file used in tests anymore? I saw there was another test file for this query that was slightly different.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could potentially have this is a separate test or merge this and the other test so that both DEPARTURE_ROW and STOP are returned.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The whitespace usage in this file is slightly inconsistent, there is whitespace before the last } for example.
Co-authored-by: Joel Lappalainen <lappalj8@gmail.com>
(includeStations && !stop.isPartOfStation() && !stopIsIncludedByStopFilter(stop)) || | ||
(!includeStations && !stopIsIncludedByStopFilter(stop)) || | ||
(stop.isPartOfStation() && !stopIsIncludedByStationFilter(stop)) || |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This part is a bit hard to follow but I don't know what to do about it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would put each part in between ||
into a separate method which has a name and possibly a bit of Javadoc.
This doesn't make the logic less complicated but helps someone else understand what each piece does.
} else if (includeStations && stop.getParentStation() != null) { | ||
seenStops.add(stop.getParentStation().getId()); | ||
placesFound.add(new PlaceAtDistance(stop.getParentStation(), distance)); | ||
} else if (includeStops) { | ||
seenStops.add(stop.getId()); | ||
placesFound.add(new PlaceAtDistance(stop, distance)); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't it enough to have just these two checks and remove the first if? If I'm not missing anything, I think this also covers the case where both includeStops
and includeStations
is true.
src/main/java/org/opentripplanner/routing/graphfinder/PlaceFinderTraverseVisitor.java
Outdated
Show resolved
Hide resolved
Co-authored-by: Leonard Ehrenfried <mail@leonard.io>
src/main/java/org/opentripplanner/apis/gtfs/datafetchers/QueryTypeImpl.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are getting close - just the refactoring of the complex if statement.
Co-authored-by: Leonard Ehrenfried <mail@leonard.io>
While testing that the transmodel API still works, I noticed there is a |
Also, should we also mark the transmodel APIs |
I tested this a bit more and checked the transmodel API code. Transmodel API returns both the quay (stop in gtfs terminology) that belongs to a StopPlace (sort of the same thing as station in gtfs) and the StopPlace itself. This differs from the behaviour we designed for the GTFS API in this pr. The code that makes the transmodel API return the StopPlaces resides in the transmodel API code and it modifies the results returned by the method that is used by the GTFS API also. It might make sense to remove the transmodel API specific code and reuse the logic in the shared core code but I guess we need to keep the transmodel API behaviour as is and it would complicate the core code even more. I don't really want to put the burden of refactoring this to @nurmAV within the scope of this pr. |
Summary
Adds an option to include both stops and stations when searching for stops. If both are included, only the parent station of a stop is returned if one exists.
Unit tests
The code was tested manually