Class: Queries::FieldOccurrence::Filter

Inherits:
Query::Filter show all
Includes:
Concerns::Citations, Concerns::DataAttributes, Concerns::Depictions, Concerns::Notes, Concerns::Protocols, Concerns::Tags, Concerns::TaxonDetermination, Helpers
Defined in:
lib/queries/field_occurrence/filter.rb

Constant Summary collapse

PARAMS =
[
  *::Queries::CollectingEvent::Filter::BASE_PARAMS,
  *Queries::Concerns::DateRanges.params, # Fead to CE query. Revisit possibly.

  :biocuration_class_id,
  :biological_association_id,
  :biological_associations,
  # :biological_relationship_id,
  :collecting_event_id,
  :determiner_id,
  :field_occurrence_id,
  # :collectors,
  :dates,
  # :determiner_id_all,
  # :determiner_name_regex,
  # :determiners,
  # :dwc_indexed,
  :georeferences,
  # :import_dataset_id,
  # :sled_image_id,
  :spatial_geographic_areas,
  # :taxon_determination_id,
  biocuration_class_id: [],
  biological_association_id: [],
  # biological_relationship_id: [],
  collecting_event_id: [],
  field_occurrence_id: [],
  determiner_id: [],
  # import_dataset_id: [],
  # is_type: [],
].inject([{}]) { |ary, k| k.is_a?(Hash) ? ary.last.merge!(k) : ary.unshift(k); ary }.freeze

Constants inherited from Query::Filter

Query::Filter::FILTER_QUERIES, Query::Filter::SUBQUERIES

Instance Attribute Summary collapse

Attributes inherited from Query::Filter

#anatomical_part_query, #api, #asserted_distribution_query, #asserted_environment_query, #biological_association_query, #biological_associations_graph_query, #collecting_event_query, #collection_object_query, #content_query, #controlled_vocabulary_term_query, #conveyance_query, #data_attribute_query, #depiction_query, #descriptor_query, #document_query, #dwc_occurrence_query, #extract_query, #field_occurrence_query, #image_query, #loan_query, #object_global_id, #observation_query, #order_by, #otu_query, #page, #paginate, #params, #per, #person_query, #project_id, #recent, #recent_target, #roll_call, #sound_query, #taxon_name_query, #taxon_name_relationship_query, #venn, #venn_ignore_pagination, #venn_mode

Attributes inherited from Query

#query_string, #terms

Instance Method Summary collapse

Methods included from Helpers

#boolean_param, #integer_param, #negate_facet, #split_pairs, #split_repeated_pairs, #tri_value_array

Methods inherited from Query::Filter

#all, #all_and_clauses, #all_merge_clauses, #annotator_and_clauses, #annotator_merge_clauses, annotator_params, api_except_params, api_excluded_params, #apply_venn, #attribute_exact_facet, base_filter, base_query_name, base_query_to_h, #deep_permit, #disable_paging, included_annotator_facets, instantiated_base_filter, inverted_subqueries, #model_id_facet, #object_global_id_facet, #only_project?, #only_project_or_less?, #paging_state, params, #permitted_params, #process_url_into_params, #project_id_facet, query_name, #set_nested_queries, #set_paging, set_paging, #shared_and_clauses, #subquery_vector, #target_and_clauses, #venn_query

Methods inherited from Query

#alphabetic_strings, #alphanumeric_strings, base_name, #base_name, #base_query, #build_terms, #cached_facet, #end_wildcard, #levenshtein_distance, #match_ordered_wildcard_pieces_in_cached, #no_terms?, referenced_klass, #referenced_klass, #referenced_klass_except, #referenced_klass_intersection, #referenced_klass_union, #start_and_end_wildcard, #start_wildcard, #table, #wildcard_pieces

Constructor Details

#initialize(query_params) ⇒ Filter

rubocop:disable Metric/MethodLength



141
142
143
144
145
146
147
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
# File 'lib/queries/field_occurrence/filter.rb', line 141

def initialize(query_params)
  super

  # Only CollectingEvent fields are permitted, for advanced nesting (e.g. tags on CEs), use collecting_event_query
  collecting_event_params = ::Queries::CollectingEvent::Filter.base_params + Queries::Concerns::DateRanges.params

  # project_id is handled during use
  @base_collecting_event_query = ::Queries::CollectingEvent::Filter.new(
    params.select{ |a, b| collecting_event_params.include?(a) } # maintain this to avoid sub query initialization for now
  )

  @biological_association_id = params[:biological_association_id]
  @biocuration_class_id = params[:biocuration_class_id]
  @biological_relationship_id = params[:biological_relationship_id] # TODO: no reference?
  @biological_associations = boolean_param(params, :biological_associations)
  @collectors = boolean_param(params, :collectors)
  @collecting_event_id = params[:collecting_event_id]
  @field_occurrence_id = params[:field_occurrence_id]
  @dates = boolean_param(params, :dates)
  @determiners = boolean_param(params, :determiners)
  @determiner_id = params[:determiner_id]
  @determiner_id_all = boolean_param(params, :determiner_id_all)
  @determiner_name_regex = params[:determiner_name_regex]
  @dwc_indexed = boolean_param(params, :dwc_indexed)
  @import_dataset_id = params[:import_dataset_id]
  @is_type = params[:is_type] || []
  @otu_descendants = boolean_param(params, :otu_descendants)
  @sled_image_id = (params[:sled_image_id].presence)

  set_citations_params(params)
  set_data_attributes_params(params)
  set_depiction_params(params)
  set_notes_params(params)
  set_protocols_params(params)
  set_tags_params(params)
  set_taxon_determination_params(params)
end

Instance Attribute Details

#base_collecting_event_query ⇒ Object

All params managed by CollectingEvent filter are available here as well



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

def base_collecting_event_query
  @base_collecting_event_query
end

#biocuration_class_id ⇒ Array

Returns of biocuration_class ids.

Returns:

  • (Array) —

    of biocuration_class ids



85
86
87
# File 'lib/queries/field_occurrence/filter.rb', line 85

def biocuration_class_id
  @biocuration_class_id
end

#biological_association_id ⇒ Object

Returns Array Matching records in this BiologicalAssociation.

Returns:

  • Array Matching records in this BiologicalAssociation



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

def biological_association_id
  @biological_association_id
end

#biological_associations ⇒ True, ...

Returns true - is used in a bilogical association false - is not used in a biological association nil - not applied.

Returns:

  • (True, False, nil) —

    true - is used in a bilogical association false - is not used in a biological association nil - not applied



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

def biological_associations
  @biological_associations
end

#biological_relationship_id ⇒ Array

Returns of biological_relationship#id.

Returns:

  • (Array) —

    of biological_relationship#id



89
90
91
# File 'lib/queries/field_occurrence/filter.rb', line 89

def biological_relationship_id
  @biological_relationship_id
end

#collecting_event_id ⇒ Object

[Array] only return objects with these collecting event ID



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

def collecting_event_id
  @collecting_event_id
end

#collectors ⇒ Boolean?

Returns true - A collector role exists false - A collector role exists nil - not applied.

Returns:

  • (Boolean, nil) —

    true - A collector role exists false - A collector role exists nil - not applied



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

def collectors
  @collectors
end

#dates ⇒ Object

Returns Boolean true - any of start/end date or verbatim date are populated false - none of start/end date or verbatim date are populated nil - ignored.

Returns:

  • Boolean true - any of start/end date or verbatim date are populated false - none of start/end date or verbatim date are populated nil - ignored



138
139
140
# File 'lib/queries/field_occurrence/filter.rb', line 138

def dates
  @dates
end

#determiner_id ⇒ Array

Parameters:

  • determiner (Array or Person#id) —

    one ore more people id

Returns:

  • (Array)


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

def determiner_id
  @determiner_id
end

#determiner_id_all ⇒ Boolean

Parameters:

  • determiner_id_all (String, nil) —

    false, nil - treat the ids in determiner_id as "or" 'true' - treat the ids in determiner_id as "and" (only collection objects with all and only all will match)

Returns:

  • (Boolean)


126
127
128
# File 'lib/queries/field_occurrence/filter.rb', line 126

def determiner_id_all
  @determiner_id_all
end

#determiner_name_regex ⇒ Object

A PostgreSQL valid regular expression. Note that simple strings evaluate as wildcard matches. !! Probably shouldn't expose to external API.

Returns:

  • String



132
133
134
# File 'lib/queries/field_occurrence/filter.rb', line 132

def determiner_name_regex
  @determiner_name_regex
end

#determiners ⇒ Boolean?

Returns true - A determiner role exists false - No determiner role exists nil - not applied.

Returns:

  • (Boolean, nil) —

    true - A determiner role exists false - No determiner role exists nil - not applied



75
76
77
# File 'lib/queries/field_occurrence/filter.rb', line 75

def determiners
  @determiners
end

#dwc_indexed ⇒ True, ...

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



95
96
97
# File 'lib/queries/field_occurrence/filter.rb', line 95

def dwc_indexed
  @dwc_indexed
end

#field_occurrence_id ⇒ Object

Parameters:

  • Array (String, nil) —

    or Integer of FieldOccurrence ids



62
63
64
# File 'lib/queries/field_occurrence/filter.rb', line 62

def field_occurrence_id
  @field_occurrence_id
end

#geographic_area ⇒ True, ...

Returns true - has collecting event that has geographic_area false - does not have collecting event that has geographic area nil - not applied.

Returns:

  • (True, False, nil) —

    true - has collecting event that has geographic_area false - does not have collecting event that has geographic area nil - not applied



115
116
117
# File 'lib/queries/field_occurrence/filter.rb', line 115

def geographic_area
  @geographic_area
end

#import_dataset_id ⇒ Array

Returns of ImportDataset ids.

Returns:

  • (Array) —

    of ImportDataset ids



48
49
50
# File 'lib/queries/field_occurrence/filter.rb', line 48

def import_dataset_id
  @import_dataset_id
end

#is_type ⇒ Array?

Returns one of holotype, lectotype etc. nil - not applied.

Returns:

  • (Array, nil) —

    one of holotype, lectotype etc. nil - not applied



100
101
102
# File 'lib/queries/field_occurrence/filter.rb', line 100

def is_type
  @is_type
end

#sled_image_id ⇒ SledImage#id?

Returns:



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

def sled_image_id
  @sled_image_id
end

#taxon_name ⇒ True, ...

Returns true - Otu has taxon name false - Otu without taxon name nil - not applied.

Returns:

  • (True, False, nil) —

    true - Otu has taxon name false - Otu without taxon name nil - not applied



109
110
111
# File 'lib/queries/field_occurrence/filter.rb', line 109

def taxon_name
  @taxon_name
end

Instance Method Details

#anatomical_part_query_facet ⇒ Object



442
443
444
445
446
447
448
449
450
# File 'lib/queries/field_occurrence/filter.rb', line 442

def anatomical_part_query_facet
  return nil if anatomical_part_query.nil?

  ::FieldOccurrence
    .joins(:origin_relationships)
    .where(origin_relationships: { new_object_type: 'AnatomicalPart' })
    .where("origin_relationships.new_object_id IN (#{ anatomical_part_query.all.select(:id).to_sql })")
    .distinct
end

#and_clauses ⇒ Object



452
453
454
455
456
457
# File 'lib/queries/field_occurrence/filter.rb', line 452

def and_clauses
  [
    collecting_event_id_facet,
    field_occurrence_id_facet,
  ]
end

#base_collecting_event_query_facet ⇒ Object



326
327
328
329
330
331
332
333
334
335
# File 'lib/queries/field_occurrence/filter.rb', line 326

def base_collecting_event_query_facet
  return nil if base_collecting_event_query.only_project?

  s = 'WITH query_ce_base_co AS (' + base_collecting_event_query.all.to_sql + ') ' +
    ::FieldOccurrence
    .joins('JOIN query_ce_base_co as query_ce_base_co1 on query_ce_base_co1.id = field_occurrences.collecting_event_id')
    .to_sql

  ::FieldOccurrence.from('(' + s + ') as field_occurrences').distinct
end

#biocuration_facet ⇒ Object



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

def biocuration_facet
  return nil if biocuration_class_id.empty?
  ::FieldOccurrence::BiologicalFieldOccurrence.joins(:biocuration_classifications)
    .where(biocuration_classifications: { biocuration_class_id: })
end

#biological_association_id_facet ⇒ Object

TODO: turn into UNION!



370
371
372
373
374
375
376
377
378
379
380
381
# File 'lib/queries/field_occurrence/filter.rb', line 370

def biological_association_id_facet
  return nil if biological_association_id.empty?
  b = ::BiologicalAssociation.where(id: biological_association_id)
  s = 'WITH query_ba_id_co AS (' + b.all.to_sql + ') ' +
    ::FieldOccurrence
    .joins("LEFT JOIN query_ba_id_co as query_ba_id_co1 on field_occurrences.id = query_ba_id_co1.biological_association_subject_id AND query_ba_id_co1.biological_association_subject_type = 'FieldOccurrence'")
    .joins("LEFT JOIN query_ba_id_co as query_ba_id_co2 on field_occurrences.id = query_ba_id_co2.biological_association_object_id AND query_ba_id_co2.biological_association_object_type = 'FieldOccurrence'")
    .where('(query_ba_id_co1.id) IS NOT NULL OR (query_ba_id_co2.id IS NOT NULL)')
    .to_sql

  ::FieldOccurrence.from('(' + s + ') as field_occurrences').distinct
end

#biological_association_query_facet ⇒ Object

TODO: turn into UNION!



384
385
386
387
388
389
390
391
392
393
394
# File 'lib/queries/field_occurrence/filter.rb', line 384

def biological_association_query_facet
  return nil if biological_association_query.nil?
  s = 'WITH query_ba_co AS (' + biological_association_query.all.to_sql + ') ' +
    ::FieldOccurrence
    .joins("LEFT JOIN query_ba_co as query_ba_co1 on field_occurrences.id = query_ba_co1.biological_association_subject_id AND query_ba_co1.biological_association_subject_type = 'FieldOccurrence'")
    .joins("LEFT JOIN query_ba_co as query_ba_co2 on field_occurrences.id = query_ba_co2.biological_association_object_id AND query_ba_co2.biological_association_object_type = 'FieldOccurrence'")
    .where('(query_ba_co1.id) IS NOT NULL OR (query_ba_co2.id IS NOT NULL)')
    .to_sql

  ::FieldOccurrence.from('(' + s + ') as field_occurrences').distinct
end

#biological_associations_facet ⇒ Object



361
362
363
364
365
366
367
# File 'lib/queries/field_occurrence/filter.rb', line 361

def biological_associations_facet
  return nil if biological_associations.nil?
  a = ::FieldOccurrence.joins(:biological_associations)
  b = ::FieldOccurrence.joins(:related_biological_associations)

  referenced_klass_union([a, b])
end

#biological_relationship_id_facet ⇒ Object



281
282
283
284
# File 'lib/queries/field_occurrence/filter.rb', line 281

def biological_relationship_id_facet
  return nil if biological_relationship_id.empty?
  ::FieldOccurrence.with_biological_relationship_id(biological_relationship_id)
end

#collecting_event_id_facet ⇒ Object



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

def collecting_event_id_facet
  return nil if collecting_event_id.empty?
  table[:collecting_event_id].in(collecting_event_id)
end

#collecting_event_query_facet ⇒ Object



396
397
398
399
400
401
402
403
404
# File 'lib/queries/field_occurrence/filter.rb', line 396

def collecting_event_query_facet
  return nil if collecting_event_query.nil?
  s = 'WITH query_ce_co AS (' + collecting_event_query.all.select(:id).to_sql + ') ' +
    ::FieldOccurrence
    .joins('JOIN query_ce_co as query_ce_co1 on query_ce_co1.id = field_occurrences.collecting_event_id')
    .to_sql

  ::FieldOccurrence.from('(' + s + ') as field_occurrences').distinct
end

#collecting_event_table ⇒ Arel::Table

Returns:

  • (Arel::Table)


180
181
182
# File 'lib/queries/field_occurrence/filter.rb', line 180

def collecting_event_table
  ::CollectingEvent.arel_table
end

#dates_facet ⇒ Object

TODO: Probably just move to CE



299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
# File 'lib/queries/field_occurrence/filter.rb', line 299

def dates_facet
  return nil if dates.nil?
  if dates
    ::FieldOccurrence.left_joins(:collecting_event).where(
      'start_date_year IS NOT NULL OR ' \
      'start_date_month IS NOT NULL OR ' \
      'start_date_day IS NOT NULL OR ' \
      'end_date_year IS NOT NULL OR ' \
      'end_date_month IS NOT NULL OR ' \
      'end_date_day IS NOT NULL OR ' \
      'verbatim_date IS NOT NULL'
    )
  else
    ::FieldOccurrence.left_joins(:collecting_event).where(
      collecting_event: {
        start_date_year: nil,
        start_date_month: nil,
        start_date_day: nil,
        end_date_year: nil,
        end_date_month: nil,
        end_date_day: nil,
        verbatim_date: nil,
      },
    )
  end
end

#determiner_facet ⇒ Object

TODO: DRY with Source (author), TaxonName, etc. See Queries::ColletingEvent::Filter for other use



225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
# File 'lib/queries/field_occurrence/filter.rb', line 225

def determiner_facet
  return nil if determiner_id.empty?
  tt = table

  o = ::TaxonDetermination.arel_table
  r = ::Role.arel_table

  a = o.alias('a_det__')
  b = o.project(a[Arel.star]).from(a)

  c = r.alias('det_r1')

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

  e = c[:id].not_eq(nil)
  f = c[:person_id].in(determiner_id)

  b = b.where(e.and(f))
  b = b.group(a['id'])
  b = b.having(a['id'].count.eq(determiner_id.length)) if determiner_id_all

  b = b.as('det_z1_')

  ::FieldOccurrence.joins(Arel::Nodes::InnerJoin.new(b, Arel::Nodes::On.new(b['taxon_determination_object_id'].eq(tt['id']))))
end

#determiner_name_regex_facet ⇒ Object



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

def determiner_name_regex_facet
  return nil if determiner_name_regex.nil?
  ::FieldOccurrence.joins(:determiners).where('people.cached ~* ?', determiner_name_regex)
end

#determiners_facet ⇒ Object



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

def determiners_facet
  return nil if determiners.nil?
  if determiners
    ::FieldOccurrence.joins(:determiners)
  else
    ::FieldOccurrence.where.missing(:determiners)
  end
end

#dwc_indexed_facet ⇒ Object



286
287
288
289
290
291
# File 'lib/queries/field_occurrence/filter.rb', line 286

def dwc_indexed_facet
  return nil if dwc_indexed.nil?
  dwc_indexed ?
    ::FieldOccurrence.dwc_indexed :
    ::FieldOccurrence.dwc_not_indexed
end

#dwc_occurrence_query_facet ⇒ Object



406
407
408
409
410
411
412
413
414
415
416
# File 'lib/queries/field_occurrence/filter.rb', line 406

def dwc_occurrence_query_facet
  return nil if dwc_occurrence_query.nil?

  s = ::FieldOccurrence
    .with(query_dwc_fo: dwc_occurrence_query.all.select(:dwc_occurrence_object_id, :dwc_occurrence_object_type, :id))
    .joins(:dwc_occurrence)
    .joins('JOIN query_dwc_fo as query_dwc_fo1 on query_dwc_fo1.id = dwc_occurrences.id')
    .to_sql

  ::FieldOccurrence.from('(' + s + ') as field_occurrences').distinct
end

#field_occurrence_id_facet ⇒ Object



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

def field_occurrence_id_facet
  return nil if field_occurrence_id.empty?
  table[:id].in(field_occurrence_id)
end

#image_query_facet ⇒ Object



418
419
420
421
422
423
424
425
426
427
428
# File 'lib/queries/field_occurrence/filter.rb', line 418

def image_query_facet
  return nil if image_query.nil?

  s = ::FieldOccurrence
    .with(query_image: image_query.all)
    .joins(:depictions)
    .joins('JOIN query_image ON query_image.id = depictions.image_id')
    .to_sql

  ::FieldOccurrence.from('(' + s + ') as field_occurrences').distinct
end

#import_dataset_id_facet ⇒ Object



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

def import_dataset_id_facet
  return nil if import_dataset_id.blank?
  ::FieldOccurrence.joins(:related_origin_relationships)
    .where(origin_relationships: {old_object_id: import_dataset_id, old_object_type: 'ImportDataset'})
end

#merge_clauses ⇒ Object



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/field_occurrence/filter.rb', line 459

def merge_clauses
  [
    #  import_dataset_id_facet,
    anatomical_part_query_facet,
    biological_association_id_facet,
    base_collecting_event_query_facet,
    biological_association_query_facet,
    collecting_event_query_facet,
    dwc_occurrence_query_facet,
    image_query_facet,
    observation_query_facet,
    otu_query_facet,
    taxon_name_query_facet,
    biocuration_facet,
    biological_associations_facet,
    #  biological_relationship_id_facet,
    dates_facet,
    determiner_facet,
    # determiner_name_regex_facet,
    determiners_facet,
    # dwc_indexed_facet,
    # sled_image_facet,
  ]
end

#observation_query_facet ⇒ Object



430
431
432
433
434
435
436
437
438
439
440
# File 'lib/queries/field_occurrence/filter.rb', line 430

def observation_query_facet
  return nil if observation_query.nil?

  s = ::FieldOccurrence
    .with(obs_query_fo: observation_query.all)
    .joins(:observations)
    .joins('JOIN obs_query_fo on observations.id = obs_query_fo.id')
    .to_sql

  ::FieldOccurrence.from('(' + s + ') as field_occurrences').distinct
end

#otu_query_facet ⇒ Object



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

def otu_query_facet
  return nil if otu_query.nil?
  s = 'WITH query_otu_co AS (' + otu_query.all.to_sql + ') ' +
    ::FieldOccurrence
    .joins(:taxon_determinations)
    .joins('JOIN query_otu_co as query_otu_co1 on query_otu_co1.id = taxon_determinations.otu_id')
    .where(taxon_determinations: { position: 1 })
    .to_sql

  ::FieldOccurrence.from('(' + s + ') as field_occurrences').distinct
end

#sled_image_facet ⇒ Object



276
277
278
279
# File 'lib/queries/field_occurrence/filter.rb', line 276

def sled_image_facet
  return nil if sled_image_id.nil?
  ::FieldOccurrence::BiologicalFieldOccurrence.joins(:depictions).where('depictions.sled_image_id = ?', sled_image_id)
end

#taxon_name_query_facet ⇒ Object



337
338
339
340
341
342
343
344
345
346
347
# File 'lib/queries/field_occurrence/filter.rb', line 337

def taxon_name_query_facet
  return nil if taxon_name_query.nil?

  s = ::FieldOccurrence
    .with(query_tn_fo: taxon_name_query.all.select(:id))
    .joins(:taxon_names)
    .joins('JOIN query_tn_fo on query_tn_fo.id = taxon_names.id')
    .to_sql

  ::FieldOccurrence.from('(' + s + ') as field_occurrences').distinct
end