Class: Queries::TaxonName::Filter

Inherits:
Query
  • Object
show all
Includes:
Concerns::Tags, Concerns::Users, Helpers
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 included from Helpers

#boolean_param

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



157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'lib/queries/taxon_name/filter.rb', line 157

def initialize(params)
  @author = params[:author]
  @authors = boolean_param(params, :authors )
  @citations = params[:citations]
  @descendants = boolean_param(params,:descendants )
  @descendants_max_depth = params[:descendants_max_depth]
  @ancestors = boolean_param(params,:ancestors )
  @exact = boolean_param(params, :exact)
  @leaves = boolean_param(params, :leves)
  @name = params[:name]
  @not_specified = boolean_param(params, :not_specified)
  @nomenclature_code = params[:nomenclature_code]  if !params[:nomenclature_code].nil?
  @nomenclature_group = params[:nomenclature_group]  if !params[:nomenclature_group].nil?
  @otus = boolean_param(params, :otus)
  @etymology = boolean_param(params, :etymology)
  @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 = boolean_param(params, :type_metadata)
  @updated_since = params[:updated_since].to_s
  @validity = boolean_param(params, :validity)
  @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 = boolean_param(params, :taxon_name_author_ids_or)

  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)


66
67
68
# File 'lib/queries/taxon_name/filter.rb', line 66

def ancestors
  @ancestors
end

#authorObject

Parameters:

  • author (String)

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



18
19
20
# File 'lib/queries/taxon_name/filter.rb', line 18

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)


108
109
110
# File 'lib/queries/taxon_name/filter.rb', line 108

def authors
  @authors
end

#citationsObject

Returns the value of attribute citations.



93
94
95
# File 'lib/queries/taxon_name/filter.rb', line 93

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)


56
57
58
# File 'lib/queries/taxon_name/filter.rb', line 56

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)


61
62
63
# File 'lib/queries/taxon_name/filter.rb', line 61

def descendants_max_depth
  @descendants_max_depth
end

#etymologyObject

'true' or 'false'

on initialize

whether the name has etymology

Parameters:

  • etymology (Boolean, nil)


103
104
105
# File 'lib/queries/taxon_name/filter.rb', line 103

def etymology
  @etymology
end

#exactObject

Parameters:

  • exact (Boolean)

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



27
28
29
# File 'lib/queries/taxon_name/filter.rb', line 27

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



136
137
138
# File 'lib/queries/taxon_name/filter.rb', line 136

def leaves
  @leaves
end

#nameObject

Parameters:

  • name (String)

    Matches against cached. See also exact.



14
15
16
# File 'lib/queries/taxon_name/filter.rb', line 14

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



129
130
131
# File 'lib/queries/taxon_name/filter.rb', line 129

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



125
126
127
# File 'lib/queries/taxon_name/filter.rb', line 125

def nomenclature_group
  @nomenclature_group
end

#not_specifiedObject

Returns the value of attribute not_specified.



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

def not_specified
  @not_specified
end

#otusObject

'true' or 'false'

on initialize

whether the name has an Otu

Parameters:

  • otus (Boolean, nil)


98
99
100
# File 'lib/queries/taxon_name/filter.rb', line 98

def otus
  @otus
end

#parent_idObject

Return the taxon names with this/these parent_ids

Returns:

  • Return the taxon names with this/these parent_ids



51
52
53
# File 'lib/queries/taxon_name/filter.rb', line 51

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.



88
89
90
# File 'lib/queries/taxon_name/filter.rb', line 88

def project_id
  @project_id
end

#sortString?

Parameters:

  • sort (String, nil)

    one of :classification, :alphabetical

Returns:

  • (String, nil)


153
154
155
# File 'lib/queries/taxon_name/filter.rb', line 153

def sort
  @sort
end

#taxon_name_author_idsArray

Returns:

  • (Array)


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

def taxon_name_author_ids
  @taxon_name_author_ids
end

#taxon_name_author_ids_orBoolean

Parameters:

  • 'true' (String)

Returns:

  • (Boolean)


148
149
150
# File 'lib/queries/taxon_name/filter.rb', line 148

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.



82
83
84
# File 'lib/queries/taxon_name/filter.rb', line 82

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



45
46
47
# File 'lib/queries/taxon_name/filter.rb', line 45

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' } … {}


74
75
76
# File 'lib/queries/taxon_name/filter.rb', line 74

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



78
79
80
# File 'lib/queries/taxon_name/filter.rb', line 78

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>



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

def taxon_name_type
  @taxon_name_type
end

#type_metadataObject

'true' or 'false'

on initialize

whether the name has TypeMaterial


113
114
115
# File 'lib/queries/taxon_name/filter.rb', line 113

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.



34
35
36
# File 'lib/queries/taxon_name/filter.rb', line 34

def updated_since
  @updated_since
end

#validityObject

'true' or 'false'

on initialize

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


39
40
41
# File 'lib/queries/taxon_name/filter.rb', line 39

def validity
  @validity
end

#yearObject

Matches against cached_author_year.

Parameters:

  • year (String)

    “yyyy”



23
24
25
# File 'lib/queries/taxon_name/filter.rb', line 23

def year
  @year
end

Instance Method Details

#allActiveRecord::Relation

Returns:

  • (ActiveRecord::Relation)


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

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



251
252
253
254
255
256
257
258
259
260
# File 'lib/queries/taxon_name/filter.rb', line 251

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)


448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
# File 'lib/queries/taxon_name/filter.rb', line 448

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



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

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



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

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



197
198
199
# File 'lib/queries/taxon_name/filter.rb', line 197

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

#cached_nameObject



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

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



368
369
370
371
372
373
374
375
376
377
378
379
# File 'lib/queries/taxon_name/filter.rb', line 368

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



233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'lib/queries/taxon_name/filter.rb', line 233

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



292
293
294
295
# File 'lib/queries/taxon_name/filter.rb', line 292

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.



325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
# File 'lib/queries/taxon_name/filter.rb', line 325

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('tn_z1_')

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

#merge_clausesObject



473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
# File 'lib/queries/taxon_name/filter.rb', line 473

def merge_clauses
  clauses = [
    ancestor_facet,
    authors_facet,
    citations_facet,
    created_updated_facet,
    descendant_facet,
    keyword_id_facet,
    leaves_facet,
    matching_taxon_name_author_ids,
    not_specified_facet,
    otus_facet,
    taxon_name_classification_facet,
    taxon_name_relationship_type_facet,
    ,
    with_etymology_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

#not_specified_facetObject



219
220
221
222
223
224
225
226
227
228
# File 'lib/queries/taxon_name/filter.rb', line 219

def not_specified_facet
  return nil if not_specified.nil?
  if not_specified
    ::TaxonName.where(table[:cached].matches("%NOT SPECIFIED%").or(
      table[:cached_original_combination].matches("%NOT SPECIFIED%")))
  else
    ::TaxonName.where(table[:cached].does_not_match("%NOT SPECIFIED%").and(
      table[:cached_original_combination].does_not_match("%NOT SPECIFIED%")))
  end
end

#order_clause(query) ⇒ Object



526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
# File 'lib/queries/taxon_name/filter.rb', line 526

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



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

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



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

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

#tableArel::Table

Returns:

  • (Arel::Table)


193
194
195
# File 'lib/queries/taxon_name/filter.rb', line 193

def table
  ::TaxonName.arel_table
end

#taxon_name_classification_facetObject

Returns Scope.

Returns:

  • Scope



313
314
315
316
317
318
319
320
321
322
# File 'lib/queries/taxon_name/filter.rb', line 313

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



442
443
444
445
# File 'lib/queries/taxon_name/filter.rb', line 442

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!



299
300
301
302
303
304
305
306
307
308
309
310
# File 'lib/queries/taxon_name/filter.rb', line 299

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



286
287
288
289
# File 'lib/queries/taxon_name/filter.rb', line 286

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



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

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



354
355
356
357
358
# File 'lib/queries/taxon_name/filter.rb', line 354

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



428
429
430
431
# File 'lib/queries/taxon_name/filter.rb', line 428

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

#validity_facetObject



433
434
435
436
437
438
439
440
# File 'lib/queries/taxon_name/filter.rb', line 433

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



278
279
280
281
282
283
# File 'lib/queries/taxon_name/filter.rb', line 278

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



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

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



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

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

#year_facetObject



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

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