lib/queries/image/filter.rb
module Queries
module Image
class Filter < Query::Filter
include Queries::Concerns::Tags
include Queries::Concerns::Citations
PARAMS = [
:biocuration_class_id,
:collection_object_id,
:collection_object_scope,
:depiction_object_type,
:depictions,
:freeform_svg,
:image_id,
:otu_id,
:sled_image,
:sled_image_id,
:source_id,
:sqed_image,
:taxon_name_id,
:taxon_name_id_target,
:type_material_depictions,
biocuration_class_id: [],
collection_object_id: [],
depiction_object_type: [],
image_id: [],
otu_id: [],
otu_scope: [],
sled_image_id: [],
source_id: [],
taxon_name_id: [],
].freeze
# @return [Array]
# images depicting collecting_object
attr_accessor :collection_object_id
# @param collection_object_scope
# One or more of:
# :all (default, includes all below)
#
# :collection_objects (those on the collection_object)
# :observations (those on just the CollectionObject observations)
# collecting_events (those on the associated collecting event)
# # maybe those on the CE
attr_accessor :collection_object_scope
# @return [Boolean, nil]
# true - image is used (in a depiction)
# false - image is not used
# nil - either
attr_accessor :depictions
# @return [Boolean, nil]
# true - image is used (in a depiction) that has svg
# false - is not a true image
# nil - ignored
attr_accessor :freeform_svg
# @return [Array]
# @param depiction_object_type
# one or more names of classes.
# Restricts Images to those that depict these classes
attr_accessor :depiction_object_type
# @return [Array]
# images depicting otus
attr_accessor :otu_id
# @return [Protonym.id, nil]
# return all images depicting an Otu that is self or descendant linked
# to this TaxonName
attr_accessor :taxon_name_id
# @return [Array]
# A sub scope of sorts. Purpose is to gather all images
# possible under an OTU that are of an OTU, CollectionObject or Observation.
#
# !! Must be used with an otu_id !!
# @param otu_scope
# One or more of:
# :all (default, includes all below)
#
# :otu (those on the OTU)
# :otu_observations (those on just the OTU)
# :collection_object_observations (those on just those determined as the OTU)
# :collection_objects (those on just those on the collection objects)
# :type_material (those on CollectionObjects that have TaxonName used in OTU)
# :type_material_observations (those on CollectionObjects that have TaxonName used in OTU)
#
# :coordinate_otus If present adds both. Use downstream substraction to to diffs of with/out?
#
attr_accessor :otu_scope
# @return [Array]
attr_accessor :image_id
# @return [Array]
# of biocuration_class ids
attr_accessor :biocuration_class_id
# @return [Array]
attr_accessor :sled_image_id
# @return [Boolean]
# true - yes
# false - is not
# nil - ignored
attr_accessor :sled_image
# @return [Array]
attr_accessor :sqed_depiction_id
# @return [Boolean]
# true - yes
# false - is not
# nil - ignored
attr_accessor :sqed_image
# @return [Array]
# one or both of 'Otu', 'CollectionObject', defaults to both if nothing provided
# Only used when `taxon_name_id` provided
attr_accessor :taxon_name_id_target
# @return [Array]
# depicts some that is a type specimen
attr_accessor :type_material_depictions
# @param params [Hash]
def initialize(query_params)
super
@biocuration_class_id = params[:biocuration_class_id]
@collection_object_id = params[:collection_object_id]
@collection_object_scope = params[:collection_object_scope]
@depiction_object_type = params[:depiction_object_type]
@depictions = boolean_param(params, :depictions)
@freeform_svg = boolean_param(params, :freeform_svg)
@image_id = params[:image_id]
@otu_id = params[:otu_id]
@otu_scope = params[:otu_scope]&.map(&:to_sym)
@sled_image = boolean_param(params, :sled_image)
@sled_image_id = params[:sled_image_id]
@sqed_depiction_id = params[:sqed_depiction_id]
@sqed_image = boolean_param(params, :sqed_image)
@taxon_name_id = params[:taxon_name_id]
@taxon_name_id_target = params[:taxon_name_id_target]
@type_material_depictions = boolean_param(params, :type_material_depictions)
set_citations_params(params)
set_tags_params(params)
end
def image_id
[ @image_id ].flatten.compact
end
def depiction_object_type
[ @depiction_object_type ].flatten.compact
end
def taxon_name_id
[ @taxon_name_id ].flatten.compact
end
def collection_object_id
[ @collection_object_id ].flatten.compact
end
def otu_id
[ @otu_id ].flatten.compact
end
def taxon_name_id_target
a = [ @taxon_name_id_target ].flatten.compact
a = ['Otu', 'CollectionObject'] if a.empty?
a
end
def biocuration_class_id
[ @biocuration_class_id ].flatten.compact
end
def otu_scope
[ @otu_scope ].flatten.compact.map(&:to_sym)
end
def collection_object_scope
[ @collection_object_scope ].flatten.compact.map(&:to_sym)
end
def sled_image_id
[ @sled_image_id ].flatten.compact
end
def sqed_depiction_id
[ @sqed_depiction_id ].flatten.compact
end
# @return [Arel::Table]
def taxon_determination_table
::TaxonDetermination.arel_table
end
# @return [Arel::Table]
def otu_table
::Otu.arel_table
end
# @return [Arel::Table]
def collection_object_table
::CollectionObject.arel_table
end
# @return [Arel::Table]
def type_materials_table
::TypeMaterial.arel_table
end
# @return [Arel::Table]
def depiction_table
::Depiction.arel_table
end
def biocuration_facet
return nil if biocuration_class_id.empty?
::Image.joins(collection_objects: [:depictions]).merge(
::CollectionObject::BiologicalCollectionObject.joins(:biocuration_classifications)
.where(biocuration_classifications: {biocuration_class_id:})
)
end
def depiction_object_type_facet
return nil if depiction_object_type.empty?
::Image.joins(:depictions).where(depictions: {depiction_object_type:})
end
def depictions_facet
return nil if depictions.nil?
if depictions
::Image.joins(:depictions)
else
::Image.where.missing(:depictions)
end
end
def freeform_svg_facet
return nil if freeform_svg.nil?
q = ::Image.left_joins(depictions: [:sled_image, :sqed_depiction])
.where.missing(:sled_image)
.where.not(depictions: {svg_clip: nil}).distinct
if freeform_svg
q
else
::Queries.except(::Image.where(project_id:), q)
end
end
def sqed_image_facet
return nil if sqed_image.nil?
if sqed_image
::Image.joins(depictions: [:sqed_depiction]).distinct
else
::Image.left_joins(depictions: [:sqed_depiction]).where(sqed_depiction: {id: nil}).distinct
end
end
def sled_image_facet
return nil if sled_image.nil?
if sled_image
::Image.joins(:sled_image).distinct
else
::Image.where.missing(:sled_image).distinct
end
end
def sled_image_id_facet
return nil if sled_image_id.empty?
::Image.joins(:sled_image).where(sled_images: {id: sled_image_id})
end
def sqed_depiction_id_facet
return nil if sqed_depiction_id.empty?
::Image.joins(depictions: [:sqed_depiction]).where(sqed_depictions: {id: sqed_depiction_id})
end
def coordinate_otu_ids
ids = []
otu_id.each do |id|
ids += ::Otu.coordinate_otus(id).pluck(:id)
end
ids.uniq
end
def otu_id_facet
# only run this when scope not provided
return nil if otu_id.empty? || !otu_scope.empty?
::Image.joins(:otus).where(otus: {id: otu_id})
end
def otu_scope_facet
return nil if otu_id.empty? || otu_scope.empty?
otu_ids = otu_id
otu_ids += coordinate_otu_ids if otu_scope.include?(:coordinate_otus)
otu_ids.uniq!
selected = []
if otu_scope.include?(:all)
selected = [
:otu_facet_otus,
:otu_facet_collection_objects,
:otu_facet_otu_observations,
:otu_facet_collection_object_observations,
:otu_facet_type_material,
:otu_facet_type_material_observations
]
elsif otu_scope.empty?
selected = [:otu_facet_otus]
else
selected.push :otu_facet_otus if otu_scope.include?(:otus)
selected.push :otu_facet_collection_objects if otu_scope.include?(:collection_objects)
selected.push :otu_facet_collection_object_observations if otu_scope.include?(:collection_object_observations)
selected.push :otu_facet_otu_observations if otu_scope.include?(:otu_observations)
selected.push :otu_facet_type_material if otu_scope.include?(:type_material)
selected.push :otu_facet_type_material_observations if otu_scope.include?(:type_material_observations)
end
q = selected.collect{|a| '(' + send(a, otu_ids).to_sql + ')'}.join(' UNION ')
d = ::Image.from('(' + q + ')' + ' as images')
d
end
def collection_object_scope_facet
return nil if collection_object_id.empty?
selected = []
if collection_object_scope.include?(:all)
selected = [
:collection_object_facet_collection_objects,
:collection_object_facet_observations,
:collection_object_facet_collecting_events,
]
elsif collection_object_scope.present?
selected.push :collection_object_facet_collection_objects if collection_object_scope.include?(:collection_objects)
selected.push :collection_object_facet_observations if collection_object_scope.include?(:observations)
selected.push :collection_object_facet_collecting_events if collection_object_scope.include?(:collecting_events)
else
selected.push :collection_object_facet_collection_objects
end
q = selected.collect{|a| '(' + send(a).to_sql + ')'}.join(' UNION ')
d = ::Image.from('(' + q + ')' + ' as images')
d
end
def collection_object_facet_collection_objects
::Image.joins(:collection_objects).where(collection_objects: {id: collection_object_id})
end
def collection_object_facet_observations
::Image.joins(:observations).where(observations: {observation_object_type: 'CollectionObject', observation_object_id: collection_object_id })
end
def collection_object_facet_collecting_events
::Image.joins(:observations)
.joins("INNER JOIN collecting_events on collecting_events.id = observations.observation_object_id AND observations.observation_object_type = 'CollectingEvent'")
.joins('INNER JOIN collection_objects on collection_objects.collecting_event_id = collecting_events.id')
.where(collection_objects: {id: collection_object_id})
end
def otu_facet_type_material_observations(otu_ids)
::Image.joins(:observations)
.joins("INNER JOIN type_materials on type_materials.collection_object_id = observations.observation_object_id AND observations.observation_object_type = 'CollectionObject'")
.joins('INNER JOIN otus on otus.taxon_name_id = type_materials.protonym_id')
.where(otus: {id: otu_ids})
end
# Find all TaxonNames, and their synonyms
def otu_facet_type_material(otu_ids)
# Double check that there are otu_ids,
# this check exists in calling methods, but re-inforce here.
protonyms = if otu_ids.any?
::Queries::TaxonName::Filter.new(
otu_query: { otu_id: otu_ids},
synonymify: true,
project_id:
).all.where(type: 'Protonym')
else
TaxonName.none
end
::Image.joins(collection_objects: [type_materials: [:protonym]]).where(collection_objects: {type_materials: {protonym: protonyms}})
end
def otu_facet_otus(otu_ids)
::Image.joins(:depictions).where(depictions: {depiction_object_type: 'Otu', depiction_object_id: otu_ids})
end
def otu_facet_collection_objects(otu_ids)
::Image.joins(collection_objects: [:taxon_determinations])
.where(taxon_determinations: {otu_id: otu_ids})
end
def otu_facet_collection_object_observations(otu_ids)
::Image.joins(:observations)
.joins("INNER JOIN taxon_determinations on taxon_determinations.taxon_determination_object_id = observations.observation_object_id AND taxon_determinations.taxon_determination_object_type = 'CollectionObject'")
.where(taxon_determinations: {otu_id: otu_ids}, observations: {observation_object_type: 'CollectionObject'})
end
def otu_facet_otu_observations(otu_ids)
::Image.joins(:observations)
.where(observations: {observation_object_id: otu_ids, observation_object_type: 'Otu'})
end
# @return [Scope]
def type_material_depictions_facet
return nil if type_material_depictions.nil?
::Image.joins(collection_objects: [:type_materials])
end
def build_depiction_facet(kind, ids)
return nil if ids.empty?
::Image.joins(:depictions).where(depictions: {depiction_object_id: ids, depiction_object_type: kind})
end
def taxon_name_id_facet
# Image -> Depictions -> Otu -> TaxonName -> Ancestors
return nil if taxon_name_id.empty?
h = Arel::Table.new(:taxon_name_hierarchies)
t = ::TaxonName.arel_table
j1, j2, q1, q2 = nil, nil, nil, nil
if taxon_name_id_target.include?('Otu')
a = otu_table.alias('oj1')
b = t.alias('tj1')
h_alias = h.alias('th1')
j1 = table
.join(depiction_table, Arel::Nodes::InnerJoin).on(table[:id].eq(depiction_table[:image_id]))
.join(a, Arel::Nodes::InnerJoin).on( depiction_table[:depiction_object_id].eq(a[:id]).and( depiction_table[:depiction_object_type].eq('Otu') ))
.join(b, Arel::Nodes::InnerJoin).on( a[:taxon_name_id].eq(b[:id]))
.join(h_alias, Arel::Nodes::InnerJoin).on(b[:id].eq(h_alias[:descendant_id]))
z = h_alias[:ancestor_id].in(taxon_name_id)
q1 = ::Image.joins(j1.join_sources).where(z)
end
if taxon_name_id_target.include?('CollectionObject')
a = otu_table.alias('oj2')
b = t.alias('tj2')
h_alias = h.alias('th2')
j2 = table
.join(depiction_table, Arel::Nodes::InnerJoin).on(table[:id].eq(depiction_table[:image_id]))
.join(collection_object_table, Arel::Nodes::InnerJoin).on( depiction_table[:depiction_object_id].eq(collection_object_table[:id]).and( depiction_table[:depiction_object_type].eq('CollectionObject') ))
.join(taxon_determination_table, Arel::Nodes::InnerJoin).on(
collection_object_table[:id].eq(taxon_determination_table[:taxon_determination_object_id])
.and(taxon_determination_table[:taxon_determination_object_type].eq('CollectionObject'))
)
.join(a, Arel::Nodes::InnerJoin).on( taxon_determination_table[:otu_id].eq(a[:id]) )
.join(b, Arel::Nodes::InnerJoin).on( a[:taxon_name_id].eq(b[:id]))
.join(h_alias, Arel::Nodes::InnerJoin).on(b[:id].eq(h_alias[:descendant_id]))
z = h_alias[:ancestor_id].in(taxon_name_id)
q2 = ::Image.joins(j2.join_sources).where(z)
end
if q1 && q2
::Image.from("((#{q1.to_sql}) UNION (#{q2.to_sql})) as images")
elsif q1
q1.distinct
else
q2.distinct
end
end
def query_facets_facet(name = nil)
return nil if name.nil?
q = send((name + '_query').to_sym)
return nil if q.nil?
n = "query_#{name}_img"
s = "WITH #{n} AS (" + q.all.to_sql + ') ' +
::Image
.joins(:depictions)
.joins("JOIN #{n} as #{n}1 on depictions.depiction_object_id = #{n}1.id AND depictions.depiction_object_type = '#{name.treetop_camelize}'")
.to_sql
::Image.from('(' + s + ') as images').distinct
end
def merge_clauses
s = ::Queries::Query::Filter::SUBQUERIES.select{|k,v| v.include?(:image)}.keys.map(&:to_s) - ['source']
[
*s.collect{|m| query_facets_facet(m)}, # Reference all the Image referencing SUBQUERIES
biocuration_facet,
collection_object_scope_facet,
depiction_object_type_facet,
depictions_facet,
freeform_svg_facet,
otu_id_facet,
otu_scope_facet,
sled_image_facet,
sled_image_id_facet,
sqed_image_facet,
sqed_depiction_id_facet,
sqed_image_facet,
taxon_name_id_facet,
type_material_depictions_facet,
]
end
end
end
end