Skip to content

Commit 3a94a46

Browse files
committed
Fix "hybrid" autocomplete query for OTUs #4994
1 parent d2b047b commit 3a94a46

3 files changed

Lines changed: 49 additions & 6 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ This project <em>does not yet</em> adhere to [Semantic Versioning](https://semve
3535
- Monograph facilitator: Determination label is not visible in Safari browser
3636
- Checklist importer crashing on empty `originalNameUsageID` in some cases
3737
- Source and Repository autocompletes sometimes miss results, repository usage counts were sometimes wrong [#4990]
38+
- OTU autocomplete sometimes returns unuseful results instead of an exact match, even when an exact match exists [#4994]
3839

3940
[#4937]: https://github.com/SpeciesFileGroup/taxonworks/issues/4937
4041
[#4944]: https://github.com/SpeciesFileGroup/taxonworks/issues/4944
@@ -43,6 +44,7 @@ This project <em>does not yet</em> adhere to [Semantic Versioning](https://semve
4344
[#4978]: https://github.com/SpeciesFileGroup/taxonworks/issues/4978
4445
[#4983]: https://github.com/SpeciesFileGroup/taxonworks/issues/4983
4546
[#4990]: https://github.com/SpeciesFileGroup/taxonworks/issues/4990
47+
[#4994]: https://github.com/SpeciesFileGroup/taxonworks/issues/4994
4648

4749
## [0.63.1] - 2026-06-22
4850

lib/queries/otu/autocomplete.rb

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -113,15 +113,20 @@ def otu_name_similarity
113113
end
114114

115115
# For names like Tapinoma CASC_2231
116+
#
117+
# Note this intentionally does not use `terms` (Queries::Query#terms) -
118+
# that always returns a 2-element array of wildcarded copies of the
119+
# *whole* query_string, not the query_string split into words.
116120
def autocomplete_taxon_name_hybrid
117-
if terms.length == 2
118-
base_query
121+
parts = query_string.to_s.split(/\s+/)
122+
return nil unless parts.length == 2
123+
124+
genus_term, otu_term = parts
125+
126+
base_query
119127
.joins(:taxon_name)
120-
.where('taxon_names.cached % ? AND otus.name % ?', terms.first, terms.second)
128+
.where('taxon_names.cached % ? AND otus.name % ?', genus_term, otu_term)
121129
.order('taxon_names.cached, otus.name, length(taxon_names.cached), length(otus.name)')
122-
else
123-
nil
124-
end
125130
end
126131

127132
# @return [Scope]

spec/lib/queries/otu/autocomplete_spec.rb

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,8 +231,44 @@
231231
expect(query.autocomplete.first).to eq(otu2)
232232
end
233233

234+
# Regression: `terms` (Queries::Query#terms) always returns a 2-element
235+
# array of wildcarded copies of the *whole* query_string, never the
236+
# query_string split on whitespace. `autocomplete_taxon_name_hybrid` was
237+
# (mis)written assuming `terms.first`/`terms.second` were the genus and
238+
# otu.name (e.g. "Tapinoma CASC_2231") words, so `terms.length == 2` was
239+
# vacuously true for any query, and it trigram-matched the *entire*
240+
# phrase against both `taxon_names.cached` and `otus.name` instead of
241+
# matching each word against its own column.
242+
context '#autocomplete_taxon_name_hybrid' do
243+
let!(:tapinoma) { Protonym.create!(name: 'Tapinoma', rank_class: Ranks.lookup(:iczn, 'genus'), parent: root) }
244+
let!(:target) { Otu.create!(taxon_name: tapinoma, name: 'CASC_2231', project_id: project_id) }
245+
246+
specify 'matches genus term to taxon_names.cached and otu term to otus.name, independently' do
247+
# This otu.name redundantly restates the genus (as happens in real
248+
# data, e.g. "Scaphoideus menoni_E26" under genus "Scaphoideus").
249+
# Under the old whole-phrase bug this shared genus text was enough
250+
# to pass both trigram conditions even though the otu-specific part
251+
# ("sp2_Z9") has nothing to do with the query's otu term
252+
# ("CASC_2231").
253+
decoy = Otu.create!(taxon_name: tapinoma, name: 'Tapinoma sp2_Z9', project_id: project_id)
254+
255+
q = Queries::Otu::Autocomplete.new('Tapinoma CASC_2231', project_id: project_id)
256+
r = q.autocomplete_taxon_name_hybrid.to_a
257+
258+
expect(r).to include(target)
259+
expect(r).not_to include(decoy)
260+
end
234261

262+
specify 'does not run for single-word queries' do
263+
q = Queries::Otu::Autocomplete.new('Tapinoma', project_id: project_id)
264+
expect(q.autocomplete_taxon_name_hybrid).to be_nil
265+
end
235266

267+
specify 'does not run for queries with more than two words' do
268+
q = Queries::Otu::Autocomplete.new('Tapinoma CASC 2231', project_id: project_id)
269+
expect(q.autocomplete_taxon_name_hybrid).to be_nil
270+
end
271+
end
236272

237273
end
238274

0 commit comments

Comments
 (0)