lib/queries/asserted_distribution/filter.rb
module Queries
module AssertedDistribution
class Filter < Query::Filter
include Queries::Concerns::Tags
include Queries::Concerns::Notes
include Queries::Concerns::DataAttributes
include Queries::Concerns::Citations
PARAMS = [
:asserted_distribution_id,
:descendants,
:geo_json,
:geographic_area_id,
:geographic_item_id,
:geographic_area_mode,
:otu_id,
:presence,
:radius,
:taxon_name_id,
:wkt,
asserted_distribution_id: [],
geographic_area_id: [],
geographic_item_id: [],
otu_id: [],
taxon_name_id: [],
].freeze
# @param asserted_distribution_id [Array, Integer, String]
# @return [Array]
attr_accessor :asserted_distribution_id
# @param otu_id [Array, Integer, String]
# @return [Array]
attr_accessor :otu_id
# @param geographic_area_id [Array, Integer, String]
# @return [Array]
attr_accessor :geographic_area_id
# @param geographic_item_id [Array, Integer, String]
# @return [Array]
attr_accessor :geographic_item_id
# @return [Boolean, nil]
# How to treat GeographicAreas
# nil - non-spatial match by only those records matching the geographic_area_id exactly
# true - spatial match
# false - non-spatial match (descendants)
attr_accessor :geographic_area_mode
attr_accessor :wkt
attr_accessor :geo_json
# @return [Boolean, nil]
# true - Return AssertedDistributions where the OTU is asserted as present according to the Source
# false - Return AssertedDistributions where the OTU is asserted as absent according to the Source
# nil - both
attr_accessor :presence
# @return [Array]
# @param [taxon name ids, nil]
# all Otus matching these taxon names
attr_accessor :taxon_name_id
# @return [Boolean, nil]
# true - include descendants of taxon_name_id in scope
# false, nil - only exact matches
attr_accessor :descendants
# Integer in Meters
# !! defaults to 100m
attr_accessor :radius
def initialize(query_params)
super
@asserted_distribution_id = params[:asserted_distribution_id]
@descendants = boolean_param(params, :descendants)
@geo_json = params[:geo_json]
@geographic_area_id = params[:geographic_area_id]
@geographic_item_id = params[:geographic_item_id]
@geographic_area_mode = boolean_param(params, :geographic_area_mode)
@geographic_area_mode = boolean_param(params, :geographic_area_mode)
@otu_id = params[:otu_id]
@presence = boolean_param(params, :presence)
@radius = params[:radius].presence || 100.0
@taxon_name_id = params[:taxon_name_id]
@wkt = params[:wkt]
set_citations_params(params)
set_data_attributes_params(params)
set_notes_params(params)
set_tags_params(params)
end
def asserted_distribution_id
[@asserted_distribution_id].flatten.compact
end
def otu_id
[@otu_id].flatten.compact
end
def geographic_area_id
[@geographic_area_id].flatten.compact
end
def geographic_item_id
[@geographic_item_id].flatten.compact
end
def taxon_name_id
[@taxon_name_id].flatten.compact
end
def presence_facet
return nil if presence.nil?
if presence
table[:is_absent].eq_any(['f', nil]) # !! not eq_any()
else
table[:is_absent].eq('t')
end
end
def wkt_facet
return nil if wkt.nil?
from_wkt(wkt)
end
def from_wkt(wkt_shape)
i = ::GeographicItem.joins(:geographic_areas).where(::GeographicItem.contained_by_wkt_sql(wkt_shape))
j = ::GeographicArea.joins(:geographic_items).where(geographic_items: i)
k = ::GeographicArea.descendants_of(j) # Add children that might not be caught because they don't have a shapes
l = ::GeographicArea.from("((#{j.to_sql}) UNION (#{k.to_sql})) as geographic_areas").distinct
s = 'WITH query_wkt_ad AS (' + l.all.to_sql + ') ' +
::AssertedDistribution
.joins('JOIN query_wkt_ad as query_wkt_ad1 on query_wkt_ad1.id = asserted_distributions.geographic_area_id')
.to_sql
::AssertedDistribution.from('(' + s + ') as asserted_distributions')
end
# Shape is a Hash in GeoJSON format
def geo_json_facet
return nil if geo_json.nil?
if i = spatial_query
# All spatial records
j = ::GeographicArea.joins(:geographic_items).where(geographic_items: i)
# Expand to include all descendants of any spatial match!
# We only care about areas actually used here.
k = ::GeographicArea.joins(:asserted_distributions).descendants_of(j)
l = ::GeographicArea.from("((#{j.to_sql}) UNION (#{k.to_sql})) as geographic_areas").distinct
return ::AssertedDistribution.where( geographic_area: l )
else
return nil
end
end
# @return [GeographicItem scope]
def spatial_query
if geometry = RGeo::GeoJSON.decode(geo_json)
case geometry.geometry_type.to_s
when 'Point'
::GeographicItem.joins(:geographic_areas).where( ::GeographicItem.within_radius_of_wkt_sql(geometry.to_s, radius ) )
when 'Polygon', 'MultiPolygon'
::GeographicItem.joins(:geographic_areas).where(::GeographicItem.contained_by_wkt_sql(geometry.to_s))
else
nil
end
else
nil
end
end
def geographic_area_id_facet
return nil if geographic_area_id.empty?
a = nil
case geographic_area_mode
when nil, true # exact and spatial start the same
a = ::GeographicArea.where(id: geographic_area_id)
when false # descendants
a = ::GeographicArea.descendants_of_any(geographic_area_id)
end
b = nil # from AssertedDistributions
case geographic_area_mode
when nil, false # exact, descendants
b = ::AssertedDistribution.where(geographic_area: a)
when true # spatial
i = ::GeographicItem.joins(:geographic_areas).where(geographic_areas: a) # .unscope
wkt_shape = ::GeographicItem.st_union(i).to_a.first['collection'].to_s # todo, check
return from_wkt(wkt_shape)
end
b
end
def otu_id_facet
return nil if otu_id.empty?
table[:otu_id].in(otu_id)
end
def geographic_item_id_facet
return nil if geographic_item_id.empty?
::GeographicArea.joins(:geographic_areas_geographic_items).where(
geographic_areas_geographic_items: {geographic_item_id:}
)
end
def taxon_name_id_facet
return nil if taxon_name_id.empty?
if descendants
h = Arel::Table.new(:taxon_name_hierarchies)
o = Arel::Table.new(:otus)
j = o.join(h, Arel::Nodes::InnerJoin).on(o[:taxon_name_id].eq(h[:descendant_id]))
z = h[:ancestor_id].in(taxon_name_id)
::AssertedDistribution.joins(:otu).joins(j.join_sources).where(z)
else
::AssertedDistribution.joins(:otu).where(otus: {taxon_name_id:})
end
end
def otu_query_facet
return nil if otu_query.nil?
s = 'WITH query_otu_ad AS (' + otu_query.all.to_sql + ') ' +
::AssertedDistribution
.joins('JOIN query_otu_ad as query_otu_ad1 on query_otu_ad1.id = asserted_distributions.otu_id')
.to_sql
::AssertedDistribution.from('(' + s + ') as asserted_distributions')
end
def taxon_name_query_facet
return nil if taxon_name_query.nil?
s = 'WITH query_tn_ad AS (' + taxon_name_query.all.to_sql + ') ' +
::AssertedDistribution
.joins(:otu)
.joins('JOIN query_tn_ad as query_tn_ad1 on query_tn_ad1.id = otus.taxon_name_id')
.to_sql
::AssertedDistribution.from('(' + s + ') as asserted_distributions')
end
def biological_association_query_facet
return nil if biological_association_query.nil?
s = 'WITH query_ad_ba AS (' + biological_association_query.all.to_sql + ') '
a = ::AssertedDistribution
.joins("JOIN query_ad_ba as query_ad_ba1 on asserted_distributions.otu_id = query_ad_ba1.biological_association_subject_id AND query_ad_ba1.biological_association_subject_type = 'Otu'")
b = ::AssertedDistribution
.joins("JOIN query_ad_ba as query_ad_ba2 on asserted_distributions.otu_id = query_ad_ba2.biological_association_object_id AND query_ad_ba2.biological_association_object_type = 'Otu'")
s << referenced_klass_union([a,b]).to_sql
::AssertedDistribution.from('(' + s + ') as asserted_distributions').distinct
end
def and_clauses
[
otu_id_facet,
presence_facet,
]
end
def merge_clauses
[
biological_association_query_facet,
geo_json_facet,
otu_query_facet,
taxon_name_query_facet,
geographic_area_id_facet,
geographic_item_id_facet,
taxon_name_id_facet,
wkt_facet,
]
end
end
end
end