Class: Queries::TaxonName::Autocomplete
Constant Summary
collapse
- CACHED_NAME_WEIGHT =
Weights. Theory (using this loosely) is that this
will proportionally increase the importance in the list of the corresponding element.
The trade-off is subtle, but seems to work at first try.
8.0
- CACHED_AUTHOR_YEAR_WEIGHT =
6.0
- CACHED_WEIGHT =
4.0
- CACHED_ORIGINAL_COMBINATION_WEIGHT =
2.0
Instance Attribute Summary collapse
#dynamic_limit, #project_id, #query_string
Attributes inherited from Query
#query_string, #terms
Instance Method Summary
collapse
#autocomplete_cached_wildcard_anywhere, #autocomplete_common_name_exact, #autocomplete_common_name_like, #autocomplete_exact_id, #autocomplete_exactly_named, #autocomplete_named, #autocomplete_ordered_wildcard_pieces_in_cached, #cached_facet, #combine_or_clauses, #common_name_name, #common_name_table, #common_name_wild_pieces, #exactly_named, #fragments, #integers, #least_levenshtein, #match_wildcard_end_in_cached, #match_wildcard_in_cached, #named, #only_ids, #only_integers?, #parent, #parent_child_join, #parent_child_where, #pieces, #safe_integers, #scope, #string_fragments, #wildcard_wrapped_integers, #wildcard_wrapped_years, #with_cached, #with_cached_like, #with_id, #with_project_id, #year_letter, #years
Methods inherited from Query
#alphabetic_strings, #alphanumeric_strings, base_name, #base_name, #build_terms, #cached_facet, #end_wildcard, #levenshtein_distance, #match_ordered_wildcard_pieces_in_cached, #no_terms?, referenced_klass, #referenced_klass, #referenced_klass_except, #referenced_klass_intersection, #referenced_klass_union, #start_and_end_wildcard, #start_wildcard, #table, #wildcard_pieces
Constructor Details
#initialize(string, **params) ⇒ Autocomplete
Returns a new instance of Autocomplete.
42
43
44
45
46
47
48
49
50
51
52
|
# File 'lib/queries/taxon_name/autocomplete.rb', line 42
def initialize(string, **params)
@nomenclature_group = params[:nomenclature_group]
@valid = boolean_param(params, :valid)
@type = params[:type]
@parent_id = params[:parent_id]
@no_leaves = boolean_param(params, :no_leaves)
@exact = boolean_param(params, :exact)
super
end
|
Instance Attribute Details
#authorship ⇒ String
Returns (including empty).
39
40
41
|
# File 'lib/queries/taxon_name/autocomplete.rb', line 39
def authorship
@authorship
end
|
#exact ⇒ Boolean
Returns &exact=<"true"|"false">
if 'true' then only #name = query_string results are returned (no fuzzy matching).
31
32
33
|
# File 'lib/queries/taxon_name/autocomplete.rb', line 31
def exact
@exact
end
|
#no_leaves ⇒ Boolean
Returns &no_leaves=<"true"|"false">
if 'true' then only names with descendents will be returned.
36
37
38
|
# File 'lib/queries/taxon_name/autocomplete.rb', line 36
def no_leaves
@no_leaves
end
|
#nomenclature_group ⇒ Array
Returns &nomenclature_group=<<Iczn|Icnp|Icn>::<Higher|Family|Genus|Species>>.
8
9
10
|
# File 'lib/queries/taxon_name/autocomplete.rb', line 8
def nomenclature_group
@nomenclature_group
end
|
#parent_id ⇒ Array
Returns &parent_id=&parent_id=<other_int> etc.
24
25
26
|
# File 'lib/queries/taxon_name/autocomplete.rb', line 24
def parent_id
@parent_id
end
|
#type ⇒ Array
Returns &type=<Protonym, Combination, Hybrid, etc.>&type= etc.
20
21
22
|
# File 'lib/queries/taxon_name/autocomplete.rb', line 20
def type
@type
end
|
#valid ⇒ Boolean?
Returns &valid=<"true"|"false">
if 'true' then id == cached_valid_taxon_name_id
if 'false' then id != cached_valid_taxon_name
if nil then no check made, i.e. all names
string is converted to Boolean here.
16
17
18
|
# File 'lib/queries/taxon_name/autocomplete.rb', line 16
def valid
@valid
end
|
Instance Method Details
#and_clauses ⇒ Arel:Nodes?
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
|
# File 'lib/queries/taxon_name/autocomplete.rb', line 67
def and_clauses
clauses = [
valid_state,
is_type,
with_parent_id,
with_nomenclature_group,
].compact
return nil if clauses.nil?
a = clauses.shift
clauses.each do |b|
a = a.and(b)
end
a
end
|
#autocomplete ⇒ Array
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
|
# File 'lib/queries/taxon_name/autocomplete.rb', line 400
def autocomplete
queries = (exact ? exact_autocomplete : comprehensive_autocomplete )
queries.compact!
result = []
queries.each_with_index do |q,i|
a = q
a = q.where(project_id:) if project_id.present? a = a.where(and_clauses.to_sql) if and_clauses
if !parent_id.empty?
a = a.descendants_of(::TaxonName.where(id: parent_id))
end
a = a.not_leaves if no_leaves
result += a.limit(20).to_a
break if result.count > 19
end
result.uniq!
result
end
|
#autocomplete_cached ⇒ Object
---- gin methods
Consider word_similarity()
276
277
278
279
280
281
|
# File 'lib/queries/taxon_name/autocomplete.rb', line 276
def autocomplete_cached
::TaxonName.where(project_id:).select(ApplicationRecord.sanitize_sql(['taxon_names.*, similarity(?, taxon_names.cached) AS sml', query_string]))
.where('taxon_names.cached % ?', query_string) .where(ApplicationRecord.sanitize_sql_array(["similarity('%s', taxon_names.cached) > 0.6", query_string]))
.order('sml DESC, taxon_names.cached')
end
|
#autocomplete_cached_author_year ⇒ Object
290
291
292
293
294
295
|
# File 'lib/queries/taxon_name/autocomplete.rb', line 290
def autocomplete_cached_author_year
::TaxonName.select(ApplicationRecord.sanitize_sql(['taxon_names.*, similarity(?, taxon_names.cached_author_year) AS sml', query_string]))
.where('taxon_names.cached_author_year % ?', query_string)
.where(ApplicationRecord.sanitize_sql(["similarity('%s', taxon_names.cached_author_year) > 0.6", query_string]))
.order('sml DESC, taxon_names.cached_author_year')
end
|
#autocomplete_cached_end_wildcard ⇒ Scope
187
188
189
190
191
|
# File 'lib/queries/taxon_name/autocomplete.rb', line 187
def autocomplete_cached_end_wildcard
s = query_string.delete('\\')
a = table[:cached].matches("#{s}%")
base_query.where(a.to_sql).limit(20)
end
|
#autocomplete_cached_name_end_wildcard ⇒ Scope
216
217
218
219
|
# File 'lib/queries/taxon_name/autocomplete.rb', line 216
def autocomplete_cached_name_end_wildcard
a = table[:name].matches("#{query_string}%")
base_query.where(a.to_sql).limit(20)
end
|
#autocomplete_cached_original_combination_wildcard_whitespace_with_space ⇒ Object
226
227
228
229
|
# File 'lib/queries/taxon_name/autocomplete.rb', line 226
def autocomplete_cached_original_combination_wildcard_whitespace_with_space
a = table[:cached_original_combination].matches("#{query_string.gsub('. ', ' ').gsub(/[\s\\]/, '% ')}%")
base_query.where(a.to_sql).limit(20)
end
|
#autocomplete_cached_wildcard_whitespace ⇒ Scope
232
233
234
235
|
# File 'lib/queries/taxon_name/autocomplete.rb', line 232
def autocomplete_cached_wildcard_whitespace
a = table[:cached].matches("#{query_string.gsub('. ', ' ').gsub(/[\s\\]/, '%')}")
base_query.where(a.to_sql).limit(20)
end
|
#autocomplete_cached_wildcard_whitespace_with_space ⇒ Object
221
222
223
224
|
# File 'lib/queries/taxon_name/autocomplete.rb', line 221
def autocomplete_cached_wildcard_whitespace_with_space
a = table[:cached].matches("#{query_string.gsub('. ', ' ').gsub(/[\s\\]/, '% ')}%")
base_query.where(a.to_sql).limit(20)
end
|
#autocomplete_combined_gin ⇒ Object
Used in /otus/api/v1/autocomplete
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
|
# File 'lib/queries/taxon_name/autocomplete.rb', line 306
def autocomplete_combined_gin
a = ::TaxonName.select(ApplicationRecord.sanitize_sql(
['taxon_names.*, similarity(?, name) AS sml_n, similarity(?, taxon_names.cached_author_year) AS sml_cay, similarity(?, cached) AS sml_c, similarity(?, taxon_names.cached_original_combination) AS sml_coc',
query_string, authorship, query_string, query_string])
).where('taxon_names.cached_author_year % ? OR taxon_names.cached_original_combination % ? OR taxon_names.cached % ?', query_string, query_string, query_string)
s = 'WITH tns AS (' + a.to_sql + ') ' +
::TaxonName
.select(Arel.sql("taxon_names.*, (( COALESCE(tns1.sml_n,0) * #{CACHED_NAME_WEIGHT} + \
COALESCE(tns1.sml_cay,0) * #{CACHED_AUTHOR_YEAR_WEIGHT} + \
COALESCE(tns1.sml_c,0) * #{CACHED_WEIGHT} + \
COALESCE(tns1.sml_coc,0) * #{CACHED_ORIGINAL_COMBINATION_WEIGHT} \
)) sml_tn"))
.joins('JOIN tns as tns1 on tns1.id = taxon_names.id')
.to_sql
::TaxonName.select('taxon_names.*, sml_tn as sml_t').from('(' + s + ') as taxon_names').order('taxon_names.sml_tn DESC').distinct
end
|
#autocomplete_exact_cached ⇒ Scope
113
114
115
116
|
# File 'lib/queries/taxon_name/autocomplete.rb', line 113
def autocomplete_exact_cached
a = table[:cached].eq(query_string)
base_query.where(a.to_sql).order('taxon_names.cached_author_year ASC').limit(20)
end
|
#autocomplete_exact_cached_name_and_author_year ⇒ Scope?
Matches the full displayed label, e.g.
"Isocapnia crinita (Needham & Claassen, 1925)",
i.e. cached and cached_author_year (see
TaxonName#cached_name_and_author_year).
141
142
143
144
145
146
147
|
# File 'lib/queries/taxon_name/autocomplete.rb', line 141
def autocomplete_exact_cached_name_and_author_year
p = parsed_name_and_author_year
return nil if p.nil?
a = table[:cached].eq(p[:name]).and(table[:cached_author_year].eq(p[:author_year]))
base_query.where(a.to_sql).order('taxon_names.id ASC').limit(5)
end
|
#autocomplete_exact_cached_original_combination ⇒ Scope
150
151
152
153
|
# File 'lib/queries/taxon_name/autocomplete.rb', line 150
def autocomplete_exact_cached_original_combination
a = table[:cached_original_combination].eq(query_string)
base_query.where(a.to_sql).order('taxon_names.cached_author_year ASC').limit(20)
end
|
#autocomplete_exact_name ⇒ Scope
174
175
176
177
|
# File 'lib/queries/taxon_name/autocomplete.rb', line 174
def autocomplete_exact_name
a = table[:name].eq(query_string)
base_query.where(a.to_sql).order('taxon_names.cached_author_year ASC').limit(20)
end
|
#autocomplete_exact_name_and_year ⇒ Scope
162
163
164
165
166
167
168
169
170
171
|
# File 'lib/queries/taxon_name/autocomplete.rb', line 162
def autocomplete_exact_name_and_year
a = alphabetic_strings.select { |b| !(b =~ /\d/) }
b = years
if a.size == 1 && !b.empty?
a = table[:name].eq(a.first).and(table[:cached_author_year].matches_any(wildcard_wrapped_years))
base_query.where(a.to_sql).limit(10)
else
nil
end
end
|
#autocomplete_genus_species1(result) ⇒ Scope
201
202
203
204
205
|
# File 'lib/queries/taxon_name/autocomplete.rb', line 201
def autocomplete_genus_species1(result)
return nil if result.nil?
a = table[:cached].matches(result)
base_query.where(a.to_sql).order('type DESC, cached ASC').limit(8)
end
|
#autocomplete_genus_species2(result) ⇒ Scope
209
210
211
212
213
|
# File 'lib/queries/taxon_name/autocomplete.rb', line 209
def autocomplete_genus_species2(result)
return nil if result.nil?
a = table[:cached].matches(result + '%')
base_query.where(a.to_sql).order('taxon_names.type DESC, taxon_names.cached ASC').limit(8)
end
|
#autocomplete_name_author_year_fragment ⇒ Scope?
238
239
240
241
242
243
244
245
246
|
# File 'lib/queries/taxon_name/autocomplete.rb', line 238
def autocomplete_name_author_year_fragment
f = fragments
if f.size == 2
a = table[:name].matches(f[0]).and(table[:cached_author_year].matches(f[1]))
base_query.where(a.to_sql).limit(20)
else
nil
end
end
|
#autocomplete_original_combination ⇒ Object
283
284
285
286
287
288
|
# File 'lib/queries/taxon_name/autocomplete.rb', line 283
def autocomplete_original_combination
::TaxonName.select(ApplicationRecord.sanitize_sql(['taxon_names.*, similarity(?, taxon_names.cached_original_combination) AS sml', query_string]))
.where('taxon_names.cached_original_combination % ?', query_string)
.where(ApplicationRecord.sanitize_sql_array(["similarity('%s', taxon_names.cached_original_combination) > 0.6", query_string]))
.order('sml DESC, taxon_names.cached_original_combination')
end
|
#autocomplete_taxon_name_author_year_matches ⇒ Arel::Nodes::Matches
263
264
265
266
267
|
# File 'lib/queries/taxon_name/autocomplete.rb', line 263
def autocomplete_taxon_name_author_year_matches
a = authorship
return nil if a.nil?
base_query.where(table[:cached_author_year].matches(a).to_sql).limit(10)
end
|
#autocomplete_top_cached ⇒ Scope
180
181
182
183
184
|
# File 'lib/queries/taxon_name/autocomplete.rb', line 180
def autocomplete_top_cached
s = query_string
a = table[:cached].matches("#{s}%")
base_query.where(a.to_sql).limit(1)
end
|
#autocomplete_top_cached_subgenus ⇒ Scope
194
195
196
197
|
# File 'lib/queries/taxon_name/autocomplete.rb', line 194
def autocomplete_top_cached_subgenus
a = table[:cached].matches("%(#{query_string})")
base_query.where(a.to_sql).limit(1)
end
|
#autocomplete_wildcard_author_year_joined_pieces ⇒ Scope?
249
250
251
252
253
|
# File 'lib/queries/taxon_name/autocomplete.rb', line 249
def autocomplete_wildcard_author_year_joined_pieces
return nil if pieces.empty?
a = table[:cached_author_year].matches("%#{pieces.join('%')}%")
base_query.where(a.to_sql).order('taxon_names.cached ASC').limit(20)
end
|
#autocomplete_wildcard_cached_original_combination ⇒ Scope
156
157
158
159
|
# File 'lib/queries/taxon_name/autocomplete.rb', line 156
def autocomplete_wildcard_cached_original_combination
a = table[:cached_original_combination].matches(wildcard_pieces)
base_query.where(a.to_sql).order('taxon_names.cached_author_year ASC').limit(20)
end
|
#autocomplete_wildcard_joined_strings ⇒ Scope?
256
257
258
259
260
|
# File 'lib/queries/taxon_name/autocomplete.rb', line 256
def autocomplete_wildcard_joined_strings
return nil if alphabetic_strings.empty?
a = table[:cached].matches("%#{alphabetic_strings.join('%')}%")
base_query.where(a.to_sql).limit(10)
end
|
#base_query ⇒ Scope
TODO: this should deprecate for gin based approaches.
447
448
449
450
451
452
|
# File 'lib/queries/taxon_name/autocomplete.rb', line 447
def base_query
::TaxonName.select(:id, :parent_id, :type, :rank_class, :name, :cached, :cached_html,
:cached_original_combination, :cached_author_year, :cached_valid_taxon_name_id, :cached_is_valid, 'char_length(taxon_names.cached)')
.eager_load(:parent)
.order(Arel.sql('char_length(taxon_names.cached), taxon_names.cached ASC'))
end
|
#comprehensive_autocomplete ⇒ Object
TODO: Refactor to OTU approach?
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
|
# File 'lib/queries/taxon_name/autocomplete.rb', line 352
def comprehensive_autocomplete
z = genus_species
queries = [
autocomplete_exact_cached,
autocomplete_exact_cached_name_and_author_year,
autocomplete_exact_cached_original_combination,
autocomplete_exact_name_and_year,
autocomplete_exact_name,
autocomplete_exact_id,
autocomplete_identifier_cached_exact,
autocomplete_identifier_identifier_exact,
autocomplete_cached, autocomplete_original_combination, autocomplete_cached_author_year,
autocomplete_genus_species1(z), autocomplete_genus_species2(z), autocomplete_top_cached_subgenus,
autocomplete_wildcard_joined_strings,
autocomplete_wildcard_author_year_joined_pieces,
autocomplete_wildcard_cached_original_combination
]
end
|
#exact_autocomplete ⇒ Object
Used in New taxon name task, for example
TODO: what is intent?
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
|
# File 'lib/queries/taxon_name/autocomplete.rb', line 327
def exact_autocomplete
[
autocomplete_exact_id,
autocomplete_exact_cached,
autocomplete_exact_cached_name_and_author_year,
autocomplete_exact_cached_original_combination,
autocomplete_identifier_cached_exact,
autocomplete_identifier_identifier_exact,
autocomplete_exact_name_and_year,
autocomplete_cached_end_wildcard,
autocomplete_cached_wildcard_whitespace_with_space,
autocomplete_cached_original_combination_wildcard_whitespace_with_space,
autocomplete_cached_wildcard_whitespace,
autocomplete_name_author_year_fragment,
autocomplete_taxon_name_author_year_matches,
autocomplete_wildcard_joined_strings,
autocomplete_wildcard_author_year_joined_pieces,
autocomplete_wildcard_cached_original_combination,
autocomplete_exact_name, ]
end
|
#genus_species ⇒ String?
Returns parse and only return what is assumed to be genus/species, with a wildcard in front.
430
431
432
433
434
435
436
437
438
439
440
441
442
443
|
# File 'lib/queries/taxon_name/autocomplete.rb', line 430
def genus_species
p = Vendor::Biodiversity::Result.new
p.name = query_string
r = p.parse
a = p.genus
b = p.species
if a && b
a + '%' + b
else
nil
end
end
|
#is_type ⇒ Arel::Nodes::<>?
93
94
95
96
|
# File 'lib/queries/taxon_name/autocomplete.rb', line 93
def is_type
return nil if type.empty?
table[:type].in(type)
end
|
#parsed_name_and_author_year ⇒ Hash?
Returns author_year: split from the query string via the GlobalNames
parser, reformatted to match how cached and cached_author_year are
stored (comma before the year, authors joined with '&'). nil when the
string doesn't parse with an authorship component.
123
124
125
126
127
128
129
130
131
132
133
134
|
# File 'lib/queries/taxon_name/autocomplete.rb', line 123
def parsed_name_and_author_year
return @parsed_name_and_author_year if defined?(@parsed_name_and_author_year)
a = parsed_query_string
name = a.dig(:canonical, :simple)
author_year = a.dig(:authorship, :normalized)
@parsed_name_and_author_year = if a.dig(:parsed) && name.present? && author_year.present?
{ name:, author_year: author_year.sub(/ (\d{4})/, ', \1') }
end
end
|
#parsed_query_string ⇒ Hash
Returns the memoized GlobalNames parser result for query_string.
466
467
468
469
|
# File 'lib/queries/taxon_name/autocomplete.rb', line 466
def parsed_query_string
return @parsed_query_string if defined?(@parsed_query_string)
@parsed_query_string = ::Biodiversity::Parser.parse(query_string)
end
|
#taxon_name_hierarchies_table ⇒ Arel::Table
455
456
457
|
# File 'lib/queries/taxon_name/autocomplete.rb', line 455
def taxon_name_hierarchies_table
Arel::Table.new('taxon_name_hierarchies')
end
|
#unified_autocomplete ⇒ Object
391
392
393
394
395
396
397
|
# File 'lib/queries/taxon_name/autocomplete.rb', line 391
def unified_autocomplete
[
autocomplete_exact_id,
autocomplete_combined_gin,
autocomplete_identifier_cached_exact,
]
end
|
#valid_state ⇒ Arel::Nodes::<>?
86
87
88
89
|
# File 'lib/queries/taxon_name/autocomplete.rb', line 86
def valid_state
return nil if @valid.nil?
valid ? table[:id].eq(table[:cached_valid_taxon_name_id]) : table[:id].not_eq(table[:cached_valid_taxon_name_id])
end
|
#with_cached_author_year ⇒ Arel::Nodes::Matches
460
461
462
|
# File 'lib/queries/taxon_name/autocomplete.rb', line 460
def with_cached_author_year
table[:cached_author_year].matches_any(terms)
end
|
#with_nomenclature_group ⇒ Arel::Nodes::Grouping?
107
108
109
110
|
# File 'lib/queries/taxon_name/autocomplete.rb', line 107
def with_nomenclature_group
return nil if nomenclature_group.empty?
table[:rank_class].matches_any(nomenclature_group)
end
|
#with_parent_id ⇒ Arel::Nodes::<>?
and clause, limit to ancestors or [ids]
100
101
102
103
|
# File 'lib/queries/taxon_name/autocomplete.rb', line 100
def with_parent_id
return nil if parent_id.empty?
taxon_name_hierarchies_table[:ancestor_id].in(parent_id)
end
|