lib/queries/concerns/tags.rb
# Helpers and facets for queries that reference Tags.
#
# Test coverage is currently in spec/lib/queries/source/filter_spec.rb.
#
module Queries::Concerns::Tags
extend ActiveSupport::Concern
def self.params
[
:keyword_id_and,
:keyword_id_or,
:tags,
keyword_id_and: [],
keyword_id_or: []
]
end
included do
# @return [Array]
# @params keyword_id_and
# match all objects tagged with all of the keywords referenced in this array
attr_accessor :keyword_id_and
# @return [Array]
# @params keyword_id_or
# match all objects tagged with any of the keywords referenced in this array
attr_accessor :keyword_id_or
# @return [Boolean, nil]
# @params tags ['true', 'false', nil]
attr_accessor :tags
def keyword_id_and
[@keyword_id_and].flatten.compact.uniq
end
def keyword_id_or
[@keyword_id_or].flatten.compact.uniq
end
end
def self.merge_clauses
[
:keyword_id_facet,
:tags_facet
]
end
def set_tags_params(params)
@keyword_id_and = params[:keyword_id_and]
@keyword_id_or = params[:keyword_id_or]
@tags = boolean_param(params, :tags) # (params[:tags]&.to_s&.downcase == 'true' ? true : false) if !params[:tags].nil?
end
def keyword_id_and
[@keyword_id_and].flatten.compact.uniq
end
def keyword_id_or
[@keyword_id_or].flatten.compact.uniq
end
# @return [Arel::Table]
def tag_table
::Tag.arel_table
end
# TODO: why here?
def keyword_ids=(value = [])
@keyword_ids = value
end
# @return
# all sources that match all _and ids OR any OR id
def keyword_id_facet
return nil if keyword_id_or.empty? && keyword_id_and.empty?
k = table.name.classify.safe_constantize
a = matching_keyword_id_or
b = matching_keyword_id_and
if a.nil?
b
elsif b.nil?
a
else
k.from("( (#{a.to_sql}) UNION (#{b.to_sql})) as #{table.name}")
end
end
def tags_facet
return nil if tags.nil?
if tags
referenced_klass.joins(:tags).distinct
else
referenced_klass.where.missing(:tags)
end
end
def matching_keyword_id_or
return nil if keyword_id_or.empty?
k = table.name.classify.safe_constantize
t = ::Tag.arel_table
w = t[:tag_object_id].eq(table[:id]).and( t[:tag_object_type].eq(table.name.classify))
w = w.and( t[:keyword_id].in(keyword_id_or) ) if keyword_id_or.any?
k.where( ::Tag.where(w).arel.exists )
end
def matching_keyword_id_and
return nil if keyword_id_and.empty?
l = table.name
k = l.classify.safe_constantize
t = ::Tag.arel_table
a = table.alias("k_#{l}")
b = table.project(a[Arel.star]).from(a)
.join(t)
.on(
t[:tag_object_id].eq(a[:id]),
t[:tag_object_type].eq(k.name)
)
i = 0
keyword_id_and.each do |j|
t_a = t.alias("tk_#{l[0..5]}_#{i}")
b = b.join(t_a).on(
t_a['tag_object_id'].eq(a['id']),
t_a[:tag_object_type].eq(k),
t_a[:keyword_id].eq(j)
)
i += 1
end
b = b.group(a[:id]).having(t[:keyword_id].count.gteq(keyword_id_and.count))
b = b.as("#{l}_ai")
k.joins(Arel::Nodes::InnerJoin.new(b, Arel::Nodes::On.new(b[:id].eq(table[:id]))))
end
end