Class: Autoselect::Otu::Levels::Fast

Inherits:
Level
  • Object
show all
Defined in:
lib/autoselect/otu/levels/fast.rb

Overview

Fast level: prefix-only match via a single LEFT JOIN query; no GIN, no similarity. Covers three patterns in one round-trip:

1. Otu#name (taxon_name_id IS NULL) — exact or prefix
2. TaxonName#cached               — exact or prefix
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

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>

Parameters:

  • term (String)
  • project_id (Integer, nil) (defaults to: nil)

Returns:



33
34
35
36
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
# File 'lib/autoselect/otu/levels/fast.rb', line 33

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].eq(term).or(o[:name].matches("#{sanitized}%"))
  )

  # Pattern 2 — OTU backed by a TaxonName whose cached column matches
  p2 = tn[:cached].eq(term).or(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.
  # The taxon half uses a prefix match; the OTU half uses exact-or-prefix.
  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].eq(otu_part).or(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

#descriptionObject



26
27
28
# File 'lib/autoselect/otu/levels/fast.rb', line 26

def description
  'Prefix match on OTU name and linked taxon name cached (no fuzzy matching)'
end

#keyObject



18
19
20
# File 'lib/autoselect/otu/levels/fast.rb', line 18

def key
  :fast
end

#labelObject



22
23
24
# File 'lib/autoselect/otu/levels/fast.rb', line 22

def label
  'Fast'
end