Class: Queries::CollectingEvent::Filter

Inherits:
Object
  • Object
show all
Includes:
Queries::Concerns::DataAttributes, Queries::Concerns::DateRanges, Queries::Concerns::Identifiers, Queries::Concerns::Notes, Queries::Concerns::Tags, Queries::Concerns::Users, Helpers
Defined in:
lib/queries/collecting_event/filter.rb

Constant Summary collapse

ATTRIBUTES =

TODO: likely move to model (replicated in Source too) Params exists for all CollectingEvent attributes except these

(::CollectingEvent.column_names - %w{project_id created_by_id updated_by_id created_at updated_at geographic_area_id})
PARAMS =
%w{
  collector_id
  collector_ids_or
  spatial_geographic_areas
  wkt
  geographic_area_id
  start_date
  end_date
  radius
  partial_overlap_dates
  md5_verbatim_label
  in_verbatim_locality
  in_labels
  geo_json
  collecting_event_wildcards
}

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Helpers

#boolean_param

Constructor Details

#initialize(params) ⇒ Filter

Returns a new instance of Filter.



104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/queries/collecting_event/filter.rb', line 104

def initialize(params)
  # @spatial_geographic_area_ids = params[:spatial_geographic_areas].blank? ? [] : params[:spatial_geographic_area_ids]

  @collecting_event_wildcards = params[:collecting_event_wildcards] || []
  @collector_id = params[:collector_id]
  @collector_ids_or = boolean_param(params, :collector_ids_or )
  @collection_objects = boolean_param(params, :collection_objects )
  @depictions = boolean_param(params, :depictions)
  @geo_json = params[:geo_json]
  @geographic_area_id = params[:geographic_area_id]
  @in_labels = params[:in_labels]
  @in_verbatim_locality = params[:in_verbatim_locality]
  @md5_verbatim_label = params[:md5_verbatim_label]&.to_s&.downcase == 'true'
  @otu_id = params[:otu_id].blank? ? [] : params[:otu_id]
  @radius = params[:radius].blank? ? 100 : params[:radius]
  @recent = params[:recent].blank? ? nil : params[:recent].to_i
  @spatial_geographic_areas = params[:spatial_geographic_areas]&.to_s&.downcase == 'true'
  @wkt = params[:wkt]

  set_identifier(params)
  set_tags_params(params)
  set_attributes(params)
  set_dates(params)
  set_user_dates(params)
  set_data_attributes_params(params)
  set_notes_params(params)
end

Instance Attribute Details

#collecting_event_wildcardsArray

Returns values are ATTRIBUTES that should be wildcarded.

Returns:

  • (Array)

    values are ATTRIBUTES that should be wildcarded



73
74
75
# File 'lib/queries/collecting_event/filter.rb', line 73

def collecting_event_wildcards
  @collecting_event_wildcards
end

#collection_objectsObject

Parameters:

  • collection_objects (String, nil)

    legal values are 'true', 'false' `true` - match only CollectingEvents with associated CollectionObjects `false` - match only CollectingEvents without associated CollectionObjects



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

def collection_objects
  @collection_objects
end

#collector_idArray

DONE: singularize and handle array or single

Returns:

  • (Array)


81
82
83
# File 'lib/queries/collecting_event/filter.rb', line 81

def collector_id
  @collector_id
end

#collector_ids_orBoolean

Parameters:

  • collector_ids_or (String)

    'true' - all ids treated as “or” 'false', nil - all ids treated as “and”

Returns:

  • (Boolean)


87
88
89
# File 'lib/queries/collecting_event/filter.rb', line 87

def collector_ids_or
  @collector_ids_or
end

#depictionsTrue, ...

Returns true - index is built false - index is not built nil - not applied.

Returns:

  • (True, False, nil)

    true - index is built false - index is not built nil - not applied



102
103
104
# File 'lib/queries/collecting_event/filter.rb', line 102

def depictions
  @depictions
end

#geo_jsonHash?

Returns in geo_json format (no Feature …) ?!.

Returns:

  • (Hash, nil)

    in geo_json format (no Feature …) ?!



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

def geo_json
  @geo_json
end

#geographic_area_idArray

Returns match only CollectionObjects mapped to CollectingEvents that have these specific ids. No spatial calculations are included in this parameter by default. See 'spatial_geographic_areas = true'.

Returns:

  • (Array)

    match only CollectionObjects mapped to CollectingEvents that have these specific ids. No spatial calculations are included in this parameter by default. See 'spatial_geographic_areas = true'.



69
70
71
# File 'lib/queries/collecting_event/filter.rb', line 69

def geographic_area_id
  @geographic_area_id
end

#in_labelsObject

Wildcard wrapped matching any label



38
39
40
# File 'lib/queries/collecting_event/filter.rb', line 38

def in_labels
  @in_labels
end

#in_verbatim_localityObject

TODO: remove for exact/array Wildcard wrapped matching verbatim_locality via ATTRIBUTES



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

def in_verbatim_locality
  @in_verbatim_locality
end

#md5_verbatim_labelObject

If true then in_labels checks only the MD5



41
42
43
# File 'lib/queries/collecting_event/filter.rb', line 41

def md5_verbatim_label
  @md5_verbatim_label
end

#otu_idArray

DONE: singularize and handle array or single

Returns:

  • (Array)


77
78
79
# File 'lib/queries/collecting_event/filter.rb', line 77

def otu_id
  @otu_id
end

#radiusObject

Integer in Meters



55
56
57
# File 'lib/queries/collecting_event/filter.rb', line 55

def radius
  @radius
end

#recentTrue?

TODO: reffactor to Concern or remove (likely doesn't belong here)

Returns:

  • (True, nil)


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

def recent
  @recent
end

#spatial_geographic_areasTrue?

Reference geographic areas to do a spatial query

Returns:

  • (True, nil)


63
64
65
# File 'lib/queries/collecting_event/filter.rb', line 63

def spatial_geographic_areas
  @spatial_geographic_areas
end

#wktObject

A spatial representation in well known text



52
53
54
# File 'lib/queries/collecting_event/filter.rb', line 52

def wkt
  @wkt
end

Instance Method Details

#allActiveRecord::Relation

Returns:

  • (ActiveRecord::Relation)


363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
# File 'lib/queries/collecting_event/filter.rb', line 363

def all
  a = and_clauses
  b = merge_clauses

  q = nil
  if a && b
    q = b.where(a).distinct
  elsif a
    q = ::CollectingEvent.where(a).distinct
  elsif b
    q = b.distinct
  else
    q = ::CollectingEvent.includes(:identifiers, :roles, :pinboard_items, :geographic_area, georeferences: [:geographic_item, :error_geographic_item]).all
  end

  q = q.order(updated_at: :desc).limit(recent) if recent
  q
end

#and_clausesActiveRecord::Relation

Returns:

  • (ActiveRecord::Relation)


340
341
342
343
344
345
346
347
348
349
# File 'lib/queries/collecting_event/filter.rb', line 340

def and_clauses
  clauses = base_and_clauses
  return nil if clauses.empty?

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

#attribute_clausesObject



159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/queries/collecting_event/filter.rb', line 159

def attribute_clauses
  c = []
  ATTRIBUTES.each do |a|
    if v = send(a)
      if !v.blank?
        if collecting_event_wildcards.include?(a)
          c.push Arel::Nodes::NamedFunction.new("CAST", [table[a.to_sym].as("TEXT")]).matches('%' + v.to_s + '%')
        else
          c.push table[a.to_sym].eq(v)
        end
      end
    end
  end
  c
end

#base_and_clausesArray

Returns:

  • (Array)


300
301
302
303
304
305
306
307
308
309
310
311
312
313
# File 'lib/queries/collecting_event/filter.rb', line 300

def base_and_clauses
  clauses = []
  clauses += attribute_clauses

  clauses += [
    between_date_range,
    matching_geographic_area_id,
    matching_verbatim_label_md5,
    matching_any_label,
    matching_verbatim_locality,
  ].compact!

  clauses
end

#base_merge_clausesObject



315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
# File 'lib/queries/collecting_event/filter.rb', line 315

def base_merge_clauses
  clauses = [
    collection_objects_facet,
    collector_ids_facet,
    created_updated_facet,
    data_attribute_predicate_facet,
    data_attribute_value_facet,
    data_attributes_facet,
    depictions_facet,
    geo_json_facet,
    identifier_between_facet,
    identifier_facet,       # See Queries::Concerns::Identifiers
    identifier_namespace_facet,
    identifiers_facet,      # See Queries::Concerns::Identifiers
    keyword_id_facet,
    matching_otu_ids,
    matching_spatial_via_geographic_area_ids,
    note_text_facet,        # See Queries::Concerns::Notes
    notes_facet,            # See Queries::Concerns::Notes
    wkt_facet,
  ].compact!
  clauses
end

#base_queryObject



155
156
157
# File 'lib/queries/collecting_event/filter.rb', line 155

def base_query
  ::CollectingEvent.select('collecting_events.*')
end

#collection_objects_facetObject

Returns Scope.

Returns:

  • Scope



188
189
190
191
192
# File 'lib/queries/collecting_event/filter.rb', line 188

def collection_objects_facet
  return nil if collection_objects.nil?
  subquery = ::CollectionObject.where(::CollectionObject.arel_table[:collecting_event_id].eq(::CollectingEvent.arel_table[:id])).arel.exists
  ::CollectingEvent.where(collection_objects ? subquery : subquery.not)
end

#collector_ids_facetObject

TODO: dry with Source, TaxonName, etc.



195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
# File 'lib/queries/collecting_event/filter.rb', line 195

def collector_ids_facet
  return nil if collector_id.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('CollectingEvent'))
    .and(c[:type].eq('Collector'))
    )

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

  b = b.where(e.and(f))
  b = b.group(a['id'])
  b = b.having(a['id'].count.eq(collector_id.length)) unless collector_ids_or
  b = b.as('col_z_')

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

#depictions_facetObject



175
176
177
178
179
180
181
182
183
184
185
# File 'lib/queries/collecting_event/filter.rb', line 175

def depictions_facet
  return nil if depictions.nil?

  if depictions
    ::CollectingEvent.joins(:depictions).distinct
  else
    ::CollectingEvent.left_outer_joins(:depictions)
      .where(depictions: {id: nil})
      .distinct
  end
end

#geo_json_facetObject

Shape is a Hash in GeoJSON format



239
240
241
242
243
244
245
246
# File 'lib/queries/collecting_event/filter.rb', line 239

def geo_json_facet
  return nil if geo_json.nil?
  if a = RGeo::GeoJSON.decode(geo_json)
    return spatial_query(a.geometry_type.to_s, a.to_s)
  else
    return nil
  end
end

#matching_any_labelObject



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

def matching_any_label
  return nil if in_labels.blank?
  t = "%#{in_labels}%"
  table[:verbatim_label].matches(t).or(table[:print_label].matches(t)).or(table[:document_label].matches(t))
end

#matching_geographic_area_idObject



283
284
285
286
# File 'lib/queries/collecting_event/filter.rb', line 283

def matching_geographic_area_id
  return nil if geographic_area_id.empty? || spatial_geographic_areas
  table[:geographic_area_id].eq_any(geographic_area_id)
end

#matching_otu_idsObject



288
289
290
291
# File 'lib/queries/collecting_event/filter.rb', line 288

def matching_otu_ids
  return nil if otu_id.empty?
  ::CollectingEvent.joins(:otus).where(otus: {id: otu_id}) #  table[:geographic_area_id].eq_any(geographic_area_ids)
end

#matching_spatial_via_geographic_area_idsObject

TODO: throttle by size?



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

def matching_spatial_via_geographic_area_ids
  return nil unless spatial_geographic_areas && !geographic_area_id.empty?
  a = ::GeographicItem.default_by_geographic_area_ids(geographic_area_id).ids
  ::CollectingEvent.joins(:geographic_items).where( ::GeographicItem.contained_by_where_sql( a ) )
end

#matching_verbatim_label_md5Object



276
277
278
279
280
281
# File 'lib/queries/collecting_event/filter.rb', line 276

def matching_verbatim_label_md5
  return nil unless md5_verbatim_label && !in_labels.blank?
  md5 = ::Utilities::Strings.generate_md5(in_labels)

  table[:md5_of_verbatim_label].eq(md5)
end

#matching_verbatim_localityObject



293
294
295
296
297
# File 'lib/queries/collecting_event/filter.rb', line 293

def matching_verbatim_locality
  return nil if in_verbatim_locality.blank?
  t = "%#{in_verbatim_locality}%"
  table[:verbatim_locality].matches(t)
end

#merge_clausesObject



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

def merge_clauses
  clauses = base_merge_clauses
  return nil if clauses.empty?

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

#set_attributes(params) ⇒ Object



132
133
134
135
136
# File 'lib/queries/collecting_event/filter.rb', line 132

def set_attributes(params)
  ATTRIBUTES.each do |a|
    send("#{a}=", params[a.to_sym])
  end
end

#spatial_query(geometry_type, wkt) ⇒ Object



248
249
250
251
252
253
254
255
256
257
258
259
260
261
# File 'lib/queries/collecting_event/filter.rb', line 248

def spatial_query(geometry_type, wkt)
  case geometry_type
  when 'Point'
    ::CollectingEvent
      .joins(:geographic_items)
      .where(::GeographicItem.within_radius_of_wkt_sql(wkt, radius ))
  when 'Polygon', 'MultiPolygon'
    ::CollectingEvent
      .joins(:geographic_items)
      .where(::GeographicItem.contained_by_wkt_sql(wkt))
  else
    nil
  end
end

#tableArel::Table

Returns:

  • (Arel::Table)


151
152
153
# File 'lib/queries/collecting_event/filter.rb', line 151

def table
  ::CollectingEvent.arel_table
end

#wkt_facetObject

TODO: what is it @param value [String] ?! In def shape=(value)

@shape = ::RGeo::GeoJSON.decode(value, json_parser: :json)
@shape

end



231
232
233
234
235
236
# File 'lib/queries/collecting_event/filter.rb', line 231

def wkt_facet
  return nil if wkt.blank?
  a = RGeo::WKRep::WKTParser.new
  b = a.parse(wkt)
  spatial_query(b.geometry_type.to_s, wkt)
end