Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions lib/queries/otu/autocomplete.rb
Original file line number Diff line number Diff line change
Expand Up @@ -221,8 +221,18 @@ def api_autocomplete_extended
compact = []

r.each do |h|
g = h[:label_target].id.to_s + h[:label_target].class.name
m = [ h[:otu].id, g ]
# Generate the actual rendered label to detect visual duplicates

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.

This fixes the issue, but let's move the solution to where we can actually create the current label, in app/views/otus/api/v1/autocomplete.json.jbuilder - that will keep us from getting out of sync with the display code.

if h[:label_target].kind_of?(::Otu)
label_text = h[:label_target].name || ''
if h[:label_target].taxon_name
label_text = h[:label_target].taxon_name.cached || h[:label_target].taxon_name.name
end
else # TaxonName
label_text = h[:label_target].cached || h[:label_target].name
end

# Create a key based on OTU ID and the visual label
m = [ h[:otu].id, label_text ]
next if seen.include?( m )
seen << m
compact.push h
Expand Down
97 changes: 97 additions & 0 deletions spec/lib/queries/otu/autocomplete_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -214,10 +214,107 @@
query.terms = 'Fitch 1800'
expect(query.autocomplete.first).to eq(otu2)
end
end

context 'duplicate filtering' do
# Helper method to extract the visual label from autocomplete results
def extract_label(result)
target = result[:label_target]
if target.kind_of?(::Otu)
target.taxon_name ? (target.taxon_name.cached || target.taxon_name.name) : (target.name || '')
else # TaxonName
target.cached || target.name
end
end

context 'with synonym relationships' do
let!(:synonym_taxon) { Protonym.create!(
name: 'ashtonii',
rank_class: Ranks.lookup(:iczn, 'species'),
parent: genus,
verbatim_author: 'Author',
year_of_publication: 1900) }

let!(:valid_taxon) { Protonym.create!(
name: 'ashtonii',
rank_class: Ranks.lookup(:iczn, 'species'),
parent: genus,
verbatim_author: 'Smith',
year_of_publication: 1910) }

let!(:otu_with_valid) { Otu.create!(taxon_name: valid_taxon) }

before do
# Make synonym_taxon a synonym of valid_taxon
TaxonNameRelationship::Iczn::Invalidating::Synonym.create!(
subject_taxon_name: synonym_taxon,
object_taxon_name: valid_taxon
)
end

specify 'does not return visual duplicates in api_autocomplete_extended' do
q = Queries::Otu::Autocomplete.new('ashtonii', project_id: project_id)
results = q.api_autocomplete_extended

# Build array of [otu_id, visual_label] pairs
otu_label_pairs = results.map { |r| [r[:otu].id, extract_label(r)] }

# Verify no duplicates exist
expect(otu_label_pairs.uniq).to eq(otu_label_pairs)

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.

I think in this case, where your synonym is a homonym, we do get duplicate labels in the UI, since the otus for the two related taxa have all of the same data used to build the autocomplete label: (otu name, taxon name, taxon name cached) and have different otu ids.
So there will still be duplicated results possible for otu autocomplete, but with your fix here we should no longer get duplicate results for the same otu.
The duplicates caused by homonyms is a separate issue I think with a different sort of solution - for now I think you can just remove this 'with synonyms relationship' context.

end
end

context 'when common name and scientific name share substring' do
# This tests the original bug: searching for "ashton" would return duplicate
# entries when both the scientific name (ashtoni) and common name (ashton cuckoo
# bumble bee) contained the search term

let!(:ashton_taxon) { Protonym.create!(
name: 'ashtoni',
rank_class: Ranks.lookup(:iczn, 'species'),
parent: genus,
verbatim_author: 'Cresson',
year_of_publication: 1864) }

let!(:ashton_otu) { Otu.create!(taxon_name: ashton_taxon) }

let!(:ashton_common_name) { CommonName.create!(
name: 'ashton cuckoo bumble bee',
otu: ashton_otu,
geographic_area: GeographicArea.first
) }

specify 'returns only one result when searching for substring in both names' do
# Search for "ashton" which appears in both scientific and common name
q = Queries::Otu::Autocomplete.new('ashton', project_id: project_id, include_common_names: true)
results = q.api_autocomplete_extended

ashton_results = results.select { |r| r[:otu].id == ashton_otu.id }

expect(ashton_results.count).to eq(1),
"Expected 1 result for OTU but got #{ashton_results.count}"
end

specify 'maintains searchability by both scientific and common names' do
# Verify we can still find the OTU by searching either name type

# Search by scientific name
sci_results = Queries::Otu::Autocomplete.new('ashtoni',
project_id: project_id,
include_common_names: true
).api_autocomplete_extended

expect(sci_results.any? { |r| r[:otu].id == ashton_otu.id }).to be true

# Search by common name
common_results = Queries::Otu::Autocomplete.new('cuckoo',
project_id: project_id,
include_common_names: true
).api_autocomplete_extended

expect(common_results.any? { |r| r[:otu].id == ashton_otu.id }).to be true
end
end
end

end