Class: Queries::CollectingEvent::Filter

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

Constant Summary collapse

ATTRIBUTES =

TODO: likely move to model Params exists for all CollectingEvent attributes except these

(::CollectingEvent.column_names - %w{project_id created_by_id updated_by_id created_at updated_at})

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(params) ⇒ Filter

Returns a new instance of Filter.



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/queries/collecting_event/filter.rb', line 65

def initialize(params)
  @in_labels = params[:in_labels]
  @in_verbatim_locality = params[:in_verbatim_locality]
  @recent = params[:recent].blank? ? nil : params[:recent].to_i

  @wkt = params[:wkt]
  @geo_json = params[:geo_json]
  @radius = params[:radius].blank? ? 100 : params[:radius] 

  @keyword_ids = params[:keyword_ids].blank? ? [] : params[:keyword_ids]

  # @spatial_geographic_area_ids = params[:spatial_geographic_areas].blank? ? [] : params[:spatial_geographic_area_ids]

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

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

  @geographic_area_ids = params[:geographic_area_ids].blank? ? [] : params[:geographic_area_ids]

  @otu_ids = params[:otu_ids].blank? ? [] : params[:otu_ids]

  @collector_ids = params[:collector_ids].blank? ? [] : params[:collector_ids]

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

  @collecting_event_wildcards = params[:collecting_event_wildcards] || []

  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



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

def collecting_event_wildcards
  @collecting_event_wildcards
end

#collector_idsArray

Returns:

  • (Array)


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

def collector_ids
  @collector_ids
end

#collector_ids_orObject

Returns the value of attribute collector_ids_or.



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

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 …) ?!



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

def geo_json
  @geo_json
end

#geographic_area_idsArray

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



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

def geographic_area_ids
  @geographic_area_ids
end

#in_labelsObject

Wildcard wrapped matching any label



20
21
22
# File 'lib/queries/collecting_event/filter.rb', line 20

def in_labels
  @in_labels
end

#in_verbatim_localityObject

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



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

def in_verbatim_locality
  @in_verbatim_locality
end

#md5_verbatim_labelObject

If true then in_labels checks only the MD5



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

def md5_verbatim_label
  @md5_verbatim_label
end

#otu_idsArray

Returns:

  • (Array)


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

def otu_ids
  @otu_ids
end

#radiusObject

Integer in Meters



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

def radius
  @radius
end

#recentTrue?

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

Returns:

  • (True, nil)


31
32
33
# File 'lib/queries/collecting_event/filter.rb', line 31

def recent
  @recent
end

#spatial_geographic_areasTrue?

Reference geographic areas to do a spatial query

Returns:

  • (True, nil)


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

def spatial_geographic_areas
  @spatial_geographic_areas
end

#wktObject

A spatial representation in well known text



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

def wkt
  @wkt
end

Instance Method Details

#allActiveRecord::Relation

Returns:

  • (ActiveRecord::Relation)


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

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)


258
259
260
261
262
263
264
265
266
267
# File 'lib/queries/collecting_event/filter.rb', line 258

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



111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/queries/collecting_event/filter.rb', line 111

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)


230
231
232
233
234
235
236
237
238
239
240
241
242
243
# File 'lib/queries/collecting_event/filter.rb', line 230

def base_and_clauses
  clauses = []
  clauses += attribute_clauses

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

  clauses
end

#base_merge_clausesObject



245
246
247
248
249
250
251
252
253
254
255
# File 'lib/queries/collecting_event/filter.rb', line 245

def base_merge_clauses
  clauses = [
    matching_keyword_ids,
    matching_otu_ids,
    wkt_facet,
    geo_json_facet,
    collector_ids_facet,
    matching_spatial_via_geographic_area_ids
  ].compact!
  clauses
end

#base_queryObject



107
108
109
# File 'lib/queries/collecting_event/filter.rb', line 107

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

#collector_ids_facetObject

TODO: dry with Source, TaxonName, etc.



128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/queries/collecting_event/filter.rb', line 128

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

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

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

  ::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



172
173
174
175
176
# File 'lib/queries/collecting_event/filter.rb', line 172

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



200
201
202
203
204
# File 'lib/queries/collecting_event/filter.rb', line 200

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_idsObject



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

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

#matching_otu_idsObject



218
219
220
221
# File 'lib/queries/collecting_event/filter.rb', line 218

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

#matching_spatial_via_geographic_area_idsObject

TODO: throttle by size?



194
195
196
197
198
# File 'lib/queries/collecting_event/filter.rb', line 194

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

#matching_verbatim_label_md5Object



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

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



223
224
225
226
227
# File 'lib/queries/collecting_event/filter.rb', line 223

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

#merge_clausesObject



269
270
271
272
273
274
275
276
277
278
# File 'lib/queries/collecting_event/filter.rb', line 269

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



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

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

#spatial_query(geometry_type, wkt) ⇒ Object



178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/queries/collecting_event/filter.rb', line 178

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)


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

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



164
165
166
167
168
169
# File 'lib/queries/collecting_event/filter.rb', line 164

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