Class: Autoselect::Otu::Levels::Fast
- Defined in:
- lib/autoselect/otu/levels/fast.rb
Overview
Fast level: prefix-only match via a single LEFT JOIN query; no similarity/fuzzy
matching. Note the underlying cached/name columns only carry trigram GIN
indexes (no plain btree under this DB's collation), so Postgres still serves
these prefix scans off the GIN index — "no GIN" describes the matching
strategy (deterministic prefix, no similarity scoring), not the query plan.
Covers three patterns in one round-trip:
1. Otu#name (taxon_name_id IS NULL) — prefix match
2. TaxonName#cached — prefix match
3. Multi-word hybrid — every possible split of the term into a
(taxon_prefix, otu_part) pair; both halves use prefix matching so
'P PE01' matches 'Pheidole' + 'PE01', 'Ph PE01' also matches, etc.
All split points are OR'd into the same query (one round-trip).
Ordering: shorter coalesced name first (exact matches float up over prefix matches); standalone OTUs use Otu#name length; then shorter Otu#name as tiebreaker.
Constant Summary
Constants inherited from Level
Level::DEFAULT_FUSE_MS, Level::EXTERNAL_FUSE_MS, Level::MINIMUM_RESULTS
Instance Method Summary collapse
- #call(term:, operator: nil, project_id: nil, user_id: nil, **_kwargs) ⇒ Array<Otu>
- #description ⇒ Object
- #key ⇒ Object
- #label ⇒ Object
Methods inherited from Level
#external?, #fuse_ms, #metadata, #minimum_results, #model_key, #record_info, #record_info_html, #record_label, #record_label_html
Instance Method Details
#call(term:, operator: nil, project_id: nil, user_id: nil, **_kwargs) ⇒ Array<Otu>
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
# File 'lib/autoselect/otu/levels/fast.rb', line 37 def call(term:, operator: nil, project_id: nil, user_id: nil, **_kwargs) return [] if term.blank? sanitized = ::ApplicationRecord.sanitize_sql_like(term) o = ::Otu.arel_table tn = ::TaxonName.arel_table # Pattern 1 — standalone OTU name (no taxon_name attachment) p1 = o[:taxon_name_id].eq(nil).and( o[:name].matches("#{sanitized}%") ) # Pattern 2 — OTU backed by a TaxonName whose cached column matches p2 = tn[:cached].matches("#{sanitized}%") conditions = p1.or(p2) # Pattern 3 — multi-word hybrid: try every split point so that both # short abbreviations ('P PE01') and longer prefixes ('Phei PE01') work. # Both halves use prefix matching. words = term.split(' ') if words.length >= 2 hybrid = (1...words.length).map do |i| taxon_part = words[0, i].join(' ') otu_part = words[i..].join(' ') s_taxon = ::ApplicationRecord.sanitize_sql_like(taxon_part) s_otu = ::ApplicationRecord.sanitize_sql_like(otu_part) tn[:cached].matches("#{s_taxon}%").and( o[:name].matches("#{s_otu}%") ) end.reduce(:or) conditions = conditions.or(hybrid) end scope = ::Otu.eager_load(:taxon_name).where(conditions) scope = scope.where(project_id:) if project_id.present? # Shorter coalesced name = closer to exact match; standalone OTUs fall back to otus.name. scope = scope.order( Arel.sql('length(coalesce(taxon_names.cached, otus.name, \'\')) asc nulls last, length(coalesce(otus.name, \'\')) asc nulls last') ) scope.limit(20).to_a end |
#description ⇒ Object
30 31 32 |
# File 'lib/autoselect/otu/levels/fast.rb', line 30 def description 'Prefix match on OTU name, linked taxon name cached, and taxon name x OTU name combinations (no fuzzy matching)' end |
#key ⇒ Object
22 23 24 |
# File 'lib/autoselect/otu/levels/fast.rb', line 22 def key :fast end |
#label ⇒ Object
26 27 28 |
# File 'lib/autoselect/otu/levels/fast.rb', line 26 def label 'Fast' end |