Class: Queries::TaxonName::Filter

Inherits:
Query
  • Object
show all
Includes:
Concerns::Tags, Concerns::Users
Defined in:
lib/queries/taxon_name/filter.rb

Overview

Instance Attribute Summary collapse

Attributes inherited from Query

#dynamic_limit, #options, #query_string, #terms

Instance Method Summary collapse

Methods inherited from Query

#alphabetic_strings, #attribute_exact_facet, #autocomplete, #autocomplete_cached, #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, #build_terms, #cached, #combine_or_clauses, #common_name_name, #common_name_table, #common_name_wild_pieces, #end_wildcard, #exactly_named, #fragments, #integers, #levenshtein_distance, #match_ordered_wildcard_pieces_in_cached, #match_wildcard_end_in_cached, #match_wildcard_in_cached, #named, #no_terms?, #only_ids, #only_integers?, #parent, #parent_child_join, #parent_child_where, #pieces, #result, #scope, #start_and_end_wildcard, #start_wildcard, #wildcard_pieces, #wildcard_wrapped_integers, #wildcard_wrapped_years, #with_cached, #with_cached_like, #with_id, #with_project_id, #year_letter, #years

Constructor Details

#initialize(params) ⇒ Filter

Returns a new instance of Filter.

Parameters:

  • params (Params)

    as permitted via controller



148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# File 'lib/queries/taxon_name/filter.rb', line 148

def initialize(params)
  @author = params[:author]
  @authors = (params[:authors]&.downcase == 'true' ? true : false) if !params[:authors].nil?
  @citations = params[:citations]
  @descendants = (params[:descendants]&.downcase == 'true' ? true : false) if !params[:descendants].nil?
  @descendants_max_depth = params[:descendants_max_depth]
  @ancestors = (params[:ancestors]&.downcase == 'true' ? true : false) if !params[:ancestors].nil?
  @exact = (params[:exact]&.downcase == 'true' ? true : false) if !params[:exact].nil?
  @leaves = (params[:leaves]&.downcase == 'true' ? true : false) if !params[:leaves].nil?
  @name = params[:name]
  @nomenclature_code = params[:nomenclature_code]  if !params[:nomenclature_code].nil?
  @nomenclature_group = params[:nomenclature_group]  if !params[:nomenclature_group].nil?
  @otus = (params[:otus]&.downcase == 'true' ? true : false) if !params[:otus].nil?
  @etymology = (params[:etymology]&.downcase == 'true' ? true : false) if !params[:etymology].nil?
  @project_id = params[:project_id]
  @taxon_name_classification = params[:taxon_name_classification] || []
  @taxon_name_id = params[:taxon_name_id] || []
  @parent_id = params[:parent_id] || []
  @sort = params[:sort]
  @taxon_name_relationship = params[:taxon_name_relationship] || []
  @taxon_name_relationship_type = params[:taxon_name_relationship_type] || []
  @taxon_name_type = params[:taxon_name_type]
  @type_metadata = (params[:type_metadata]&.downcase == 'true' ? true : false) if !params[:type_metadata].nil?
  @updated_since = params[:updated_since].to_s
  @validity = (params[:validity]&.downcase == 'true' ? true : false) if !params[:validity].nil?
  @year = params[:year].to_s

  @taxon_name_author_ids = params[:taxon_name_author_ids].blank? ? [] : params[:taxon_name_author_ids]
  @taxon_name_author_ids_or = (params[:taxon_name_author_ids_or]&.downcase == 'true' ? true : false) if !params[:taxon_name_author_ids_or].nil?

  set_tags_params(params)
  set_user_dates(params)
end

Instance Attribute Details

#ancestorsObject

'true' or 'false'

on initialize

Ignored when taxon_name_id[].empty?  Works as AND clause with descendants :(

Parameters:

  • ancestors (Boolean)


64
65
66
# File 'lib/queries/taxon_name/filter.rb', line 64

def ancestors
  @ancestors
end

#authorObject

Parameters:

  • author (String)

    Use “&” for “and”. Matches against cached_author_year.



16
17
18
# File 'lib/queries/taxon_name/filter.rb', line 16

def author
  @author
end

#authorsObject

'true' or 'false'

on initialize

whether the name has an author string, from any source, provided

Parameters:

  • authors (Boolean, nil)


106
107
108
# File 'lib/queries/taxon_name/filter.rb', line 106

def authors
  @authors
end

#citationsObject

Returns the value of attribute citations.



91
92
93
# File 'lib/queries/taxon_name/filter.rb', line 91

def citations
  @citations
end

#descendantsObject

'true' or 'false'

on initialize

Ignored when taxon_name_id[].empty? Return descendants of parents as well.

Parameters:

  • descendants (Boolean)


54
55
56
# File 'lib/queries/taxon_name/filter.rb', line 54

def descendants
  @descendants
end

#descendants_max_depthObject

A positive integer indicating how many levels deep of descenants to retrieve.

Ignored when descentants is false/unspecified

Parameters:

  • descendants_max_depth (Integer)


59
60
61
# File 'lib/queries/taxon_name/filter.rb', line 59

def descendants_max_depth
  @descendants_max_depth
end

#etymologyObject

'true' or 'false'

on initialize

whether the name has etymology

Parameters:

  • etymology (Boolean, nil)


101
102
103
# File 'lib/queries/taxon_name/filter.rb', line 101

def etymology
  @etymology
end

#exactObject

Parameters:

  • exact (Boolean)

    true if matching must be exact, false if partial matches are allowed.



25
26
27
# File 'lib/queries/taxon_name/filter.rb', line 25

def exact
  @exact
end

#leavesBoolean?

TODO: inverse is duplicated in autocomplete

Returns:

  • (Boolean, nil)

    &leaves=<“true”|“false”> if 'true' then return only names without descendents if 'false' then return only names with descendents



127
128
129
# File 'lib/queries/taxon_name/filter.rb', line 127

def leaves
  @leaves
end

#nameObject

Parameters:

  • name (String)

    Matches against cached. See also exact.



12
13
14
# File 'lib/queries/taxon_name/filter.rb', line 12

def name
  @name
end

#nomenclature_codeString?

Returns accessor for attr :nomenclature_code, wrap with needed wildcards.

Returns:

  • (String, nil)

    accessor for attr :nomenclature_code, wrap with needed wildcards



120
121
122
# File 'lib/queries/taxon_name/filter.rb', line 120

def nomenclature_code
  @nomenclature_code
end

#nomenclature_groupString?

Returns accessor for attr :nomenclature_group, wrap with needed wildcards.

Returns:

  • (String, nil)

    accessor for attr :nomenclature_group, wrap with needed wildcards



116
117
118
# File 'lib/queries/taxon_name/filter.rb', line 116

def nomenclature_group
  @nomenclature_group
end

#otusObject

'true' or 'false'

on initialize

whether the name has an Otu

Parameters:

  • otus (Boolean, nil)


96
97
98
# File 'lib/queries/taxon_name/filter.rb', line 96

def otus
  @otus
end

#parent_idObject

Return all immediate children to any of these parent names

Returns:

  • Return all immediate children to any of these parent names



49
50
51
# File 'lib/queries/taxon_name/filter.rb', line 49

def parent_id
  @parent_id
end

#project_idObject

TODO: probably should be an array for API purposes. TODO: unify globally as to whether param belongs here, or at controller level.

Parameters:

  • project_id (String)

    The project scope.



86
87
88
# File 'lib/queries/taxon_name/filter.rb', line 86

def project_id
  @project_id
end

#sortString?

Parameters:

  • sort (String, nil)

    one of :classification, :alphabetical

Returns:

  • (String, nil)


144
145
146
# File 'lib/queries/taxon_name/filter.rb', line 144

def sort
  @sort
end

#taxon_name_author_idsArray

Returns:

  • (Array)


134
135
136
# File 'lib/queries/taxon_name/filter.rb', line 134

def taxon_name_author_ids
  @taxon_name_author_ids
end

#taxon_name_author_ids_orBoolean

Parameters:

  • 'true' (String)

Returns:

  • (Boolean)


139
140
141
# File 'lib/queries/taxon_name/filter.rb', line 139

def taxon_name_author_ids_or
  @taxon_name_author_ids_or
end

#taxon_name_classificationObject

Parameters:

  • taxon_name_classification (Array)

    Class names of TaxonNameClassification, as strings.



80
81
82
# File 'lib/queries/taxon_name/filter.rb', line 80

def taxon_name_classification
  @taxon_name_classification
end

#taxon_name_idObject

Return the taxon name(s) with this/these ids

Returns:

  • Return the taxon name(s) with this/these ids



43
44
45
# File 'lib/queries/taxon_name/filter.rb', line 43

def taxon_name_id
  @taxon_name_id
end

#taxon_name_relationshipObject

Each entry must have a 'type' Each entry must have one (and only one) of 'subject_taxon_name_id' or 'object_taxon_name_id'

Return all taxon names in a relationship of a given type and in relation to a another name. For example, return all synonyms of Aus bus.

Parameters:

  • taxon_name_relationship (Array)
    { 'type' => 'TaxonNameRelationship::<>', 'subject|object_taxon_name_id' => '123' } … {}


72
73
74
# File 'lib/queries/taxon_name/filter.rb', line 72

def taxon_name_relationship
  @taxon_name_relationship
end

#taxon_name_relationship_typeObject

Parameters:

  • taxon_name_relationship (Array)

    All names involved in any of these relationship



76
77
78
# File 'lib/queries/taxon_name/filter.rb', line 76

def taxon_name_relationship_type
  @taxon_name_relationship_type
end

#taxon_name_typeString?

Returns &taxon_name_type=<Protonym|Combination|Hybrid>.

Returns:

  • (String, nil)

    &taxon_name_type=<Protonym|Combination|Hybrid>



131
132
133
# File 'lib/queries/taxon_name/filter.rb', line 131

def taxon_name_type
  @taxon_name_type
end

#type_metadataObject

'true' or 'false'

on initialize

whether the name has TypeMaterial


111
112
113
# File 'lib/queries/taxon_name/filter.rb', line 111

def 
  @type_metadata
end

#updated_sinceObject

TODO: deprecate for Queries::Concerns::User

Parameters:

  • updated_since (String)

    in format yyyy-mm-dd Names updated (.modified_at) since this date.



32
33
34
# File 'lib/queries/taxon_name/filter.rb', line 32

def updated_since
  @updated_since
end

#validityObject

'true' or 'false'

on initialize

true if only valid, false if only invalid, nil if both


37
38
39
# File 'lib/queries/taxon_name/filter.rb', line 37

def validity
  @validity
end

#yearObject

Matches against cached_author_year.

Parameters:

  • year (String)

    “yyyy”



21
22
23
# File 'lib/queries/taxon_name/filter.rb', line 21

def year
  @year
end

Instance Method Details

#allActiveRecord::Relation

Returns:

  • (ActiveRecord::Relation)


485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
# File 'lib/queries/taxon_name/filter.rb', line 485

def all
  a = and_clauses
  b = merge_clauses

  q = nil
  if a && b
    q = b.where(a)
  elsif a
    q = ::TaxonName.where(a)
  elsif b
    q = b
  else
    q = ::TaxonName.all
  end

  q = q.where(project_id: project_id) if project_id
  q = order_clause(q) if sort

  q
end

#ancestor_facetObject

A merge facet.

Returns:

  • Scope match only names that are a ancestor of some taxon_name_id



230
231
232
233
234
235
236
237
238
239
# File 'lib/queries/taxon_name/filter.rb', line 230

def ancestor_facet
  return nil if taxon_name_id.empty? || !(ancestors == true)

  ancestors_subquery = ::TaxonNameHierarchy.where(
    ::TaxonNameHierarchy.arel_table[:ancestor_id].eq(::TaxonName.arel_table[:id]).and(
      ::TaxonNameHierarchy.arel_table[:descendant_id].in(taxon_name_id))
  )

  ::TaxonName.where(ancestors_subquery.arel.exists)
end

#and_clausesActiveRecord::Relation

Returns:

  • (ActiveRecord::Relation)


429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
# File 'lib/queries/taxon_name/filter.rb', line 429

def and_clauses
  clauses = []

  clauses += [
    parent_id_facet,
    author_facet,
    cached_name,
    year_facet,
    updated_since_facet,
    validity_facet,
    taxon_name_id_facet,
    with_nomenclature_group,
    with_nomenclature_code,
    taxon_name_type_facet
  ].compact

  return nil if clauses.empty?

  a = clauses.shift
  clauses.each do |b|
    a = a.and(b)
  end
  a
end

#author_facetObject



395
396
397
398
399
400
401
402
# File 'lib/queries/taxon_name/filter.rb', line 395

def author_facet
  return nil if author.blank?
  if exact
    table[:cached_author_year].eq(author.strip)
  else
    table[:cached_author_year].matches('%' + author.strip.gsub(/\s/, '%') + '%')
  end
end

#authors_facetObject

Returns Scope.

Returns:

  • Scope



249
250
251
252
253
254
# File 'lib/queries/taxon_name/filter.rb', line 249

def authors_facet
  return nil if authors.nil?
  authors ?
    ::TaxonName.where.not(cached_author_year: nil) :
    ::TaxonName.where(cached_author_year: nil)
end

#base_queryObject



187
188
189
# File 'lib/queries/taxon_name/filter.rb', line 187

def base_query
  ::TaxonName.select('taxon_names.*')
end

#cached_nameObject



381
382
383
384
385
386
387
388
# File 'lib/queries/taxon_name/filter.rb', line 381

def cached_name
  return nil if name.blank?
  if exact
    table[:cached].eq(name.strip)
  else
    table[:cached].matches('%' + name.strip.gsub(/\s+/, '%') + '%')
  end
end

#citations_facetObject

Returns Scope.

Returns:

  • Scope



349
350
351
352
353
354
355
356
357
358
359
360
# File 'lib/queries/taxon_name/filter.rb', line 349

def citations_facet
  return nil if citations.nil?

  citation_conditions = ::Citation.arel_table[:citation_object_id].eq(::TaxonName.arel_table[:id]).and(
    ::Citation.arel_table[:citation_object_type].eq('TaxonName'))

  if citations == 'without_origin_citation'
    citation_conditions = citation_conditions.and(::Citation.arel_table[:is_original].eq(true))
  end

  ::TaxonName.where.not(::Citation.where(citation_conditions).arel.exists)
end

#descendant_facetObject

A merge facet.

Returns:

  • Scope match only names that are a descendant of some taxon_name_id



212
213
214
215
216
217
218
219
220
221
222
223
224
225
# File 'lib/queries/taxon_name/filter.rb', line 212

def descendant_facet
  return nil if taxon_name_id.empty? || !(descendants == true)

  descendants_subquery = ::TaxonNameHierarchy.where(
    ::TaxonNameHierarchy.arel_table[:descendant_id].eq(::TaxonName.arel_table[:id]).and(
      ::TaxonNameHierarchy.arel_table[:ancestor_id].in(taxon_name_id))
  )

  unless descendants_max_depth.nil? || descendants_max_depth.to_i < 0
    descendants_subquery = descendants_subquery.where(TaxonNameHierarchy.arel_table[:generations].lteq(descendants_max_depth.to_i))
  end

  ::TaxonName.where(descendants_subquery.arel.exists)
end

#leaves_facetObject

Returns Scope.

Returns:

  • Scope



271
272
273
274
# File 'lib/queries/taxon_name/filter.rb', line 271

def leaves_facet
  return nil if leaves.nil?
  leaves ? ::TaxonName.leaves : ::TaxonName.not_leaves
end

#matching_taxon_name_author_idsObject

TODO: dry with Source, CollectingEvent , etc.



305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
# File 'lib/queries/taxon_name/filter.rb', line 305

def matching_taxon_name_author_ids
  return nil if taxon_name_author_ids.empty?
  o = table
  r = ::Role.arel_table

  a = o.alias("a_")
  b = o.project(a[Arel.star]).from(a)

  c = r.alias('r1')

  b = b.join(c, Arel::Nodes::OuterJoin)
    .on(
      a[:id].eq(c[:role_object_id])
    .and(c[:role_object_type].eq('TaxonName'))
    .and(c[:type].eq('TaxonNameAuthor'))
  )

  e = c[:id].not_eq(nil)
  f = c[:person_id].eq_any(taxon_name_author_ids)

  b = b.where(e.and(f))
  b = b.group(a['id'])
  b = b.having(a['id'].count.eq(taxon_name_author_ids.length)) unless taxon_name_author_ids_or
  b = b.as('z1_')

  ::TaxonName.joins(Arel::Nodes::InnerJoin.new(b, Arel::Nodes::On.new(b['id'].eq(o['id']))))
end

#merge_clausesObject



454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
# File 'lib/queries/taxon_name/filter.rb', line 454

def merge_clauses
  clauses = [
    taxon_name_relationship_type_facet,
    leaves_facet,
    descendant_facet,
    ancestor_facet,
    created_updated_facet,
    taxon_name_classification_facet,
    matching_keyword_ids,
    matching_taxon_name_author_ids,
    ,
    otus_facet,
    authors_facet,
    with_etymology_facet,
    citations_facet
  ].compact

  taxon_name_relationship.each do |hsh|
    clauses << taxon_name_relationship_facet(hsh)
  end

  return nil if clauses.empty?

  a = clauses.shift
  clauses.each do |b|
    a = a.merge(b)
  end
  a
end

#order_clause(query) ⇒ Object



506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
# File 'lib/queries/taxon_name/filter.rb', line 506

def order_clause(query)
  case sort
  when 'alphabetical'
    ::TaxonName.select('*').from(
      query.order('taxon_names.cached'), :inner_query
    )
  when 'classification'
    ::TaxonName.select('*').from(
      query
      .joins('INNER JOIN taxon_name_hierarchies ON taxon_names.id = taxon_name_hierarchies.descendant_id')
      .order('taxon_name_hierarchies.generations, taxon_name_hierarchies.ancestor_id, taxon_names.cached'),
    :inner_query
    )
  else
    query
  end
end

#otus_facetObject

Returns Scope.

Returns:

  • Scope



242
243
244
245
246
# File 'lib/queries/taxon_name/filter.rb', line 242

def otus_facet
  return nil if otus.nil?
  subquery = ::Otu.where(::Otu.arel_table[:taxon_name_id].eq(::TaxonName.arel_table[:id])).arel.exists
  ::TaxonName.where(otus ? subquery : subquery.not)
end

#parent_id_facetObject



390
391
392
393
# File 'lib/queries/taxon_name/filter.rb', line 390

def parent_id_facet
  return nil if parent_id.empty?
  table[:parent_id].eq_any(parent_id)
end

#tableArel::Table

Returns:

  • (Arel::Table)


183
184
185
# File 'lib/queries/taxon_name/filter.rb', line 183

def table
  ::TaxonName.arel_table
end

#taxon_name_classification_facetObject

Returns Scope.

Returns:

  • Scope



292
293
294
295
296
297
298
299
300
301
# File 'lib/queries/taxon_name/filter.rb', line 292

def taxon_name_classification_facet
  return nil if taxon_name_classification.empty?

  ::TaxonName.where(
    ::TaxonNameClassification.where(
      ::TaxonNameClassification.arel_table[:taxon_name_id].eq(::TaxonName.arel_table[:id]).and(
        ::TaxonNameClassification.arel_table[:type].in(taxon_name_classification))
    ).arel.exists
  )
end

#taxon_name_id_facetObject



423
424
425
426
# File 'lib/queries/taxon_name/filter.rb', line 423

def taxon_name_id_facet
  return nil if taxon_name_id.empty? || descendants || ancestors
  table[:id].eq_any(taxon_name_id)
end

#taxon_name_relationship_facet(hsh) ⇒ Object

Returns Scope wrapped in descendant_facet!.

Returns:

  • Scope wrapped in descendant_facet!



278
279
280
281
282
283
284
285
286
287
288
289
# File 'lib/queries/taxon_name/filter.rb', line 278

def taxon_name_relationship_facet(hsh)
  param_key = hsh['subject_taxon_name_id'] ? 'subject_taxon_name_id' : 'object_taxon_name_id'
  join_key = hsh['subject_taxon_name_id'] ? 'object_taxon_name_id' : 'subject_taxon_name_id'

  ::TaxonName.where(
    ::TaxonNameRelationship.where(
      ::TaxonNameRelationship.arel_table[join_key].eq(::TaxonName.arel_table[:id]).and(
        ::TaxonNameRelationship.arel_table[param_key].eq(hsh[param_key])).and(
          ::TaxonNameRelationship.arel_table[:type].eq(hsh['type']))
    ).arel.exists
  )
end

#taxon_name_relationship_type_facetObject

Returns Scope.

Returns:

  • Scope



265
266
267
268
# File 'lib/queries/taxon_name/filter.rb', line 265

def taxon_name_relationship_type_facet
  return nil if taxon_name_relationship_type.empty?
  ::TaxonName.with_taxon_name_relationship(taxon_name_relationship_type)
end

#taxon_name_type_facetObject



376
377
378
379
# File 'lib/queries/taxon_name/filter.rb', line 376

def taxon_name_type_facet
  return nil if taxon_name_type.blank?
  table[:type].eq(taxon_name_type)
end

#type_metadata_facetObject

Returns Scope.

Returns:

  • Scope



335
336
337
338
339
# File 'lib/queries/taxon_name/filter.rb', line 335

def 
  return nil if .nil?
  subquery = ::TypeMaterial.where(::TypeMaterial.arel_table[:protonym_id].eq(::TaxonName.arel_table[:id])).arel.exists
  ::TaxonName.where( ? subquery : subquery.not)
end

#updated_since_facetObject



409
410
411
412
# File 'lib/queries/taxon_name/filter.rb', line 409

def updated_since_facet
  return nil if updated_since.blank?
  table[:updated_at].gt(Date.parse(updated_since))
end

#validity_facetObject



414
415
416
417
418
419
420
421
# File 'lib/queries/taxon_name/filter.rb', line 414

def validity_facet
  return nil if validity.nil?
  if validity
    table[:id].eq(table[:cached_valid_taxon_name_id])
  else
    table[:id].not_eq(table[:cached_valid_taxon_name_id])
  end
end

#with_etymology_facetObject

Returns Scope.

Returns:

  • Scope



257
258
259
260
261
262
# File 'lib/queries/taxon_name/filter.rb', line 257

def with_etymology_facet
  return nil if etymology.nil?
  etymology ?
    ::TaxonName.where.not(etymology: nil) :
    ::TaxonName.where(etymology: nil)
end

#with_nomenclature_codeArel::Nodes::Grouping?

Returns and clause.

Returns:

  • (Arel::Nodes::Grouping, nil)

    and clause



371
372
373
374
# File 'lib/queries/taxon_name/filter.rb', line 371

def with_nomenclature_code
  return nil if nomenclature_code.nil?
  table[:rank_class].matches(nomenclature_code)
end

#with_nomenclature_groupArel::Nodes::Grouping?

Returns and clause.

Returns:

  • (Arel::Nodes::Grouping, nil)

    and clause



364
365
366
367
# File 'lib/queries/taxon_name/filter.rb', line 364

def with_nomenclature_group
  return nil if nomenclature_group.nil?
  table[:rank_class].matches(nomenclature_group)
end

#year_facetObject



404
405
406
407
# File 'lib/queries/taxon_name/filter.rb', line 404

def year_facet
  return nil if year.blank?
  table[:cached_author_year].matches('%' + year + '%')
end