Specify 7.11.3
This issue has been present for as long as I have used Specify (it is not a new bug introduced in a recent version). However I never fully investigated why it happens until this week.
Description of Bug:
When a user queries collectingEvent as a base table with a query that includes latitude and longitude information and views the resulting records using the Geomap plugin, clicking any of the pins will cause a 404 error rather than loading the relevant information pertaining to the locality. (Example Query)
This error also seems to effect some other tables (for example, Agent) when the locality information is queried through a collectingEvent table relationship (ex. Agent → Collectors → collectingEvent → Locality). (Example Query).
Also very interestingly, the error can be fixed in collectingEvent and Agent tables by adding localityID field to a query and ensuring it is visible. If you hide localityID from the query results, the 404 error behavior returns. Finally, I’ve noticed that “distinct” checked vs unchecked are also factors in whether the 404 error will happen, but it seems to depend on the query content.
Expected Behavior:
If you look at the GET requests the browser is sending when clicking a Geomap pin for queries where the base table is eithercollectionObject and Locality, one can see that clicking a marker/pin in the Geomap plugin makes a GET request via the API using the relevant localityID
e.g. if you click a pin generated for the locality record with the localityID 261437, your browser makes this request: https://[yourBaseURL]/api/specify/locality/261437/. This is the expected, normal behavior, and the application finds the correct localityID linked to the records in the query results without having to include localityID as a field in the query.
Why This Error Occurs:
If you look at the GET request sent by the browser when querying on collectingEvent, you can see that the 404 error comes from a request for a locality record that doesn’t exist. In my testing, the localityID appended at the end of the GET request is switched to be the recordID when using Geomap in queries with base tables other than CO and Locality. If the integer recordID doesn’t collide with a localityID, the response is a 404 and Geomap crashes.
As I mentioned above, if you include localityID as a field in your initial query, the 404 never gets thrown. This all suggests that the issue has something to do with how Geomap plugin looks up the localityID for a query row, and that if localityID is not present in the query then the plugin code uses some sort of column index match to find a suitable integer value that it assumes is the localityID, but obviously it’s not correct in a lot of cases.
Potential Solution:
I think I’ve found the part in the codebase where the bug originates:
https://github.com/specify/specify7/blob/1e8a44877865f4e7da6ac5ea5ab60fe327cc4bff/specifyweb/frontend/js_src/lib/components/QueryBuilder/ToMap.tsx
It seems that when you click a marker on the map the app attempts to locate the column index for locality.localityId in the query’s mapping paths. If that column is missing, indexOf(...) returns -1. the code then reads row[columnIndex + 1], which becomes row[0]. if the value in row[0] can be parsed as an integer, it seems to get mistakenly treated as a localityId.
It may be a bit more complicated, but I think this is on the right track, at least. I think one potential solution is that the Geomap plugin should never search for a localityID in this way. The localityId should be derived from the relationship path when it’s not directly in the query.
If I made a fix that does this, would it be appropriate to make a PR on GitHub?