Davereinhart/gnomad id search - #707
Conversation
Add gnomAD IDs (e.g. 17-7676154-G-C) as a search type on the MaveMD variant search screen and in the homepage hero search. A gnomAD ID does not name the reference genome its coordinates belong to, so getAlleleByGnomad translates it into genomic HGVS and resolves it against GRCh38 first, falling back to GRCh37 when the registry rejects the position with IncorrectReferenceAllele. Resolving HGVS is used in preference to the registry's gnomAD cross-reference index, which only covers variants ClinGen has ingested a gnomAD record for and so misses registered alleles such as X-41334274-A-C. - Add lib/gnomad with a GRCh38/GRCh37 RefSeq chromosome accession table and VCF-style to HGVS conversion covering substitutions, insertions, deletions and delins - Add getAlleleByGnomad to the ClinGen API module - Add gnomadIdRegex alongside the other search identifier patterns - Add the gnomAD search type to SEARCH_TYPE_OPTIONS, and its type, colors and placeholder to the homepage hero search - Add the gnomAD color tokens to the theme - Correct getAlleleByDbSnp and getAlleleByClinVar return types to ClinGenAllele[], which the /alleles endpoint has always returned Every accession in the table was verified against NCBI (accession to chromosome and assembly) and against the ClinGen registry (that each GRCh38/GRCh37 pair describes the same locus).
A gnomAD ID does not name the reference genome its coordinates belong to, and the same coordinates can be valid under both assemblies while naming a different variant in each: 1-1000001-G-T resolves under GRCh38 and GRCh37 to loci 64,620 bp apart. Searching one silently took the first assembly that resolved, so an ID taken from gnomAD v2 could return a confidently wrong answer with nothing to indicate it. Results for a gnomAD ID search now report the assembly the coordinates were read under, alongside the HGVS that was resolved, with a button to re-read the same ID under the other assembly. - Return the resolving assembly from getAlleleByGnomad, and accept one to read under, which skips the fallback and lets failure propagate - Thread a forced assembly through defaultSearch so switching reuses the existing search and MaveDB lookup path - Report coordinates that do not match an assembly's reference sequence as a warning naming the base actually found there, rather than surfacing the registry's IncorrectReferenceAllele as an error - Show alleles the registry resolved but has not registered, which it answers with a blank node id rather than a CA id, instead of discarding them as no result; this also affects HGVS searches for unregistered variants - Render one card header for registered and unregistered alleles, so both show their GRCh38 and GRCh37 coordinates
Make "Any" the default search type on the MaveMD variant search screen and the homepage hero. It matches the search string against the identifier patterns already defined for the specific types, then takes that type's usual path, so nothing about the individual searches changes. Detection order matters where those patterns overlap. A VRS digest also satisfies the deliberately loose HGVS pattern, which asks only for an identifier, a colon and a description, so it is recognized first. A bare number is a ClinVar Variation ID only once the more specific forms have been ruled out, so a dbSNP rsID written without its rs prefix reads as ClinVar and still needs the dbSNP type chosen explicitly. Because the selected type stays "Any", the results report which type the string was taken to be, as a chip in that type's colour, so a reading like that one is visible rather than silent. - Add detectSearchType alongside the identifier patterns it tries - Resolve "Any" to a concrete type at the top of fetchDefaultSearchResults, leaving every existing branch untouched - Add the "Any" type, colours and placeholder to both search screens, and point the search type fallbacks at it - Show the detected type in the results header, inside the existing live region so it is announced with the result count - Size the search type tabs to match the example chips below the search bar
Add "Gene symbol" as a search type on the MaveMD variant search screen and the homepage hero. Such a search trims and uppercases the string and resolves to the existing gene page at /genes/:symbol rather than looking up an allele, leaving GeneView to report a symbol it does not recognize. The route is replaced rather than pushed. The search screen re-runs its search from the query params on mount, so a pushed entry would send the back button here and immediately forward again to the gene page. Gene symbols are also detected by the "Any" search type. Any bare word resembles a symbol, so the pattern is tried only once every more specific identifier has been ruled out. Two consequences follow: a symbol that collides with a variant identifier loses to it, so CA1-CA14 and RS1 need the gene symbol type chosen explicitly; and "Any" now rarely reports an unrecognized identifier, since a string it cannot otherwise place resolves to a gene page that reports the symbol as not found. - Add geneSymbolRegex and try it last when detecting a search string - Navigate to the gene page from the search screen, with the symbol standardized to uppercase - Add the gene symbol type, colours, examples and placeholder to both search screens - Say that "Any" accepts gene symbols in its placeholder, which the search screen now takes from the shared placeholders rather than from an example
A gene symbol search replaced the variant search screen's history entry with the gene page, so the back button skipped past it. Pushing instead is no better on its own: that screen re-runs its search from the query params on mount, so returning to a recorded gene search bounced the user straight forward to the gene page again. Searches started from the homepage had a further problem, routing through the variant search screen and leaving a page the user never asked for between the two. Handle a gene symbol before the search is recorded in the query params, strip it from the entry being left behind, and push the gene route; and send the homepage straight to the gene page. The back button now returns where the search was started from, with nothing left to re-trigger: homepage Home -> gene page, back -> Home search screen /mavemd -> gene page, back -> /mavemd, search cleared link or bookmark /mavemd?search=BRCA1 back -> /mavemd, no re-run - Add geneSymbolSearchTarget, which resolves the search type and standardizes the symbol, so both screens decide this the same way - Move gene symbol handling out of fetchDefaultSearchResults, which gene searches no longer reach, and into defaultSearch ahead of the query sync - Await defaultSearch in searchForText and only record the search while still on the search screen, so a gene symbol example no longer races the navigation and rewrites the gene page's query params
Clicking a gene symbol example did nothing. searchForText selected the search type, reset the form and set the search text before searching, and each of those steps queued a query param rewrite of its own — four in all, plus a watcher clearing the search text mid-flight. Vue Router aborts an in-flight navigation when a new one starts, so the push to the gene page lost the race. Resolve the example up front and navigate before touching any search state. This covers gene symbol examples and any "Any" example that detects as one, and leaves every other example on the existing path. It also makes the previous commit's guard against recording a search after navigating away unreachable, since defaultSearch can no longer navigate from here, so that guard is removed again.
Add unit tests for the pure functions behind the "Any", gnomAD ID and gene symbol search types. The ambiguities these encode are much of the point: a bare number resolves to a ClinVar Variation ID rather than an unprefixed dbSNP rsID, CA1-CA14 and RS1 lose to the ClinGen and dbSNP patterns despite being real gene symbols, and a hyphenated string cannot be told apart from a symbol like HLA-A. Each is named in a test so it cannot drift unnoticed. - detectSearchType: the order it tries the patterns in, and what it returns for a string resembling nothing supported - geneSymbolSearchTarget: standardization, and that it hands a malformed symbol back for its caller to reject - getAlleleByGnomad: the GRCh38 to GRCh37 fallback, reads under a forced assembly, and which failures propagate rather than trigger a retry - Search configuration: that every hero search type has a colour, which the homepage dropdown indexes without a fallback, and that each documented example resolves to its own type gnomadIdRegex is already exercised through parseGnomadId in gnomad.test.ts, so it is tested directly only where that cannot reach: parseGnomadId also gates on the chromosome table, whereas detectSearchType applies the pattern on its own.
bencap
left a comment
There was a problem hiding this comment.
Thanks Dave! We should address the second and fourth comments below prior to merging, but it's going to be really nice to have a search field that handles all our identifiers from a single catch-all search.
I didn't really have a great idea for the pills spilling over to the second line. In the long run, I think we'll probably be happiest if we unify the homepage and the MaveMD page into a shared MvVariantSearchBar component with the dropdown style selector. Don't feel like you have to make that change here though, its fine with me if we leave the pills on multiple lines and track that as a follow up issue.
I was also thinking about the regEx you built out here and it reminded me of the routeToVariantSearchIfVariantIsSearchable function in search.ts. I thought it might make sense to extract or import detectSearchType there and use it within the routing function. The only thing that gives me pause is thinking that plain integers should remain on that page and that gene symbols probably shouldn't preemptively route to the gene page within the data set search flow. This also seems fine to defer if it's a pain.
Give the "Any" search type sage and hand its slate to HGVS, so the search bar reads as brand-coloured on first load rather than neutral grey. Nothing tied HGVS to sage. Rename the token to --color-hgvs, following the name-by-search-type convention in that block. "Any" now points straight at --color-sage, and because SEARCH_COLORS.any is also the fallback for an unrecognized search type, that fallback returns to sage as well.
Address review feedback on #707. The results described the current form rather than the last search. Typing a new identifier rewrote the banner beneath the previous results, and a reading that failed because the position lies past the end of the chromosome still claimed the reference sequence did not match, because the template had no way to tell the two apart. Record the reading rather than the assembly alone: the ID as searched, the assembly it was read under, and how the registry answered. The error handler classifies IncorrectReferenceAllele and IncorrectHgvsPosition and threads that verdict through, so the template can name the real reason instead of reaching into the search box. - Hold {id, assembly, mismatch} rather than a bare assembly, and derive the banner from the recorded ID - Tailor the empty state to the kind of mismatch, and show the banner only for a reading that resolved, so it no longer contradicts the message below it or duplicates its button - Let the search type watcher run before an example fills the box, so choosing an example from another type leaves the text visible
The history API does not scroll and no scrollBehavior was configured, so every route change left the window where the previous page had it. Arriving at a gene page from a search made it obvious: the search screen scrolls its results into view, and the gene page then opened part way down. Scroll to the top for a new page, restore the saved position on back and forward, honour hash targets, and leave same-path navigation alone — the search screens mirror their state into the query string, and those navigations are not new pages.
|
Thanks for the review and detailed comments @bencap! I addressed each of them and also added 0085ad3 which was not covered here. It is a global change, so wanted to check if you know of any cases where we would not want the vertical scroll position reset when navigating to a new page. I can look into extracting |
bencap
left a comment
There was a problem hiding this comment.
Looks good, thanks Dave! I can open a couple issues for the search RegEx harmonization and consolidating the components.
This pull request introduces several improvements and new features to the variant search experience, particularly around searching by gnomAD variant IDs and handling gene symbol searches. It also includes UI enhancements and code refactoring for clarity and maintainability.
Variant Search Improvements
getAlleleByGnomad), including automatic detection of the correct genome assembly (GRCh38 or GRCh37), fallback logic, and user interface to switch assemblies if needed. The UI now clearly reports which assembly was used and allows users to retry with the other assembly. [1] [2] [3]Gene Symbol Search Improvements
API and Data Structure Updates
getAlleleByDbSnpandgetAlleleByClinVarnow return arrays of alleles instead of a single allele, improving consistency with the API and supporting multiple results.getAlleleByGnomadfunction, covering various edge cases and error handling.UI and Styling Enhancements
Default Search Behavior
These changes collectively enhance the search experience, provide clearer feedback to users, and improve code maintainability.