Class: Queries::CollectingEvent::Filter

Inherits:
Object
  • Object
show all
Includes:
Queries::Concerns::DateRanges, Queries::Concerns::Identifiers, Queries::Concerns::Tags, 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
}

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.



93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/queries/collecting_event/filter.rb', line 93

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 )
  @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]&.downcase == 'true' ? true : false) if !params[:md5_verbatim_label].nil?
  @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]&.downcase == 'true' ? true : false) if !params[:spatial_geographic_areas].nil?
  @wkt = params[:wkt]

  set_identifier(params)
  set_tags_params(params)
  set_attributes(params)
  set_dates(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



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

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



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

def collection_objects
  @collection_objects
end

#collector_idArray

DONE: singularize and handle array or single

Returns:

  • (Array)


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

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)


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

def collector_ids_or
  @collector_ids_or
end

#geo_jsonHash?

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

Returns:

  • (Hash, nil)

    in geo_json format (no Feature …) ?!



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

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'.



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

def geographic_area_id
  @geographic_area_id
end

#in_labelsObject

Wildcard wrapped matching any label



33
34
35
# File 'lib/queries/collecting_event/filter.rb', line 33

def in_labels
  @in_labels
end

#in_verbatim_localityObject

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



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

def in_verbatim_locality
  @in_verbatim_locality
end

#md5_verbatim_labelObject

If true then in_labels checks only the MD5



36
37
38
# File 'lib/queries/collecting_event/filter.rb', line 36

def md5_verbatim_label
  @md5_verbatim_label
end

#otu_idArray

DONE: singularize and handle array or single

Returns:

  • (Array)


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

def otu_id
  @otu_id
end

#radiusObject

Integer in Meters



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

def radius
  @radius
end

#recentTrue?

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

Returns:

  • (True, nil)


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

def recent
  @recent
end

#spatial_geographic_areasTrue?

Reference geographic areas to do a spatial query

Returns:

  • (True, nil)


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

def spatial_geographic_areas
  @spatial_geographic_areas
end

#wktObject

A spatial representation in well known text



47
48
49
# File 'lib/queries/collecting_event/filter.rb', line 47

def wkt
  @wkt
end

Instance Method Details

#allActiveRecord::Relation

Returns:

  • (ActiveRecord::Relation)


326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
# File 'lib/queries/collecting_event/filter.rb', line 326

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)


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

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



144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/queries/collecting_event/filter.rb', line 144

def attribute_clauses
  c = []
  ATTRIBUTES.each do |a|
    if v = send(a)
      if !v.blank?
        if collecting_event_wildcards.include?(a)
          c.push table[a.to_sym].matches('%' + v.to_s + '%')
        else
          c.push table[a.to_sym].eq(v)
        end
      end
    end
  end
  c
end

#base_and_clausesArray

Returns:

  • (Array)


270
271
272
273
274
275
276
277
278
279
280
281
282
283
# File 'lib/queries/collecting_event/filter.rb', line 270

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



285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
# File 'lib/queries/collecting_event/filter.rb', line 285

def base_merge_clauses
  clauses = [
    collection_objects_facet,
    keyword_id_facet,
    matching_otu_ids,
    wkt_facet,
    geo_json_facet,
    collector_ids_facet,
    matching_spatial_via_geographic_area_ids,
    identifiers_facet,      # See Queries::Concerns::Identifiers
    identifier_between_facet,
    identifier_facet, # See Queries::Concerns::Identifiers
    identifier_namespace_facet,
  ].compact!
  clauses
end

#base_queryObject



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

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

#collection_objects_facetObject

Returns Scope.

Returns:

  • Scope



161
162
163
164
165
# File 'lib/queries/collecting_event/filter.rb', line 161

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.



168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
# File 'lib/queries/collecting_event/filter.rb', line 168

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

#geo_json_facetObject

Shape is a Hash in GeoJSON format



212
213
214
215
216
# File 'lib/queries/collecting_event/filter.rb', line 212

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

#matching_any_labelObject



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

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



253
254
255
256
# File 'lib/queries/collecting_event/filter.rb', line 253

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



258
259
260
261
# File 'lib/queries/collecting_event/filter.rb', line 258

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?



234
235
236
237
238
# File 'lib/queries/collecting_event/filter.rb', line 234

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



246
247
248
249
250
251
# File 'lib/queries/collecting_event/filter.rb', line 246

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



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

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

#merge_clausesObject



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

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



117
118
119
120
121
# File 'lib/queries/collecting_event/filter.rb', line 117

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

#spatial_query(geometry_type, wkt) ⇒ Object



218
219
220
221
222
223
224
225
226
227
228
229
230
231
# File 'lib/queries/collecting_event/filter.rb', line 218

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)


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

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



204
205
206
207
208
209
# File 'lib/queries/collecting_event/filter.rb', line 204

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