Skip to content

Instantly share code, notes, and snippets.

@amkisko
Last active April 6, 2020 20:14
Show Gist options
  • Save amkisko/bdb164af4f98bc97db0b5bb62e533c9a to your computer and use it in GitHub Desktop.
Save amkisko/bdb164af4f98bc97db0b5bb62e533c9a to your computer and use it in GitHub Desktop.
ActiveRecord extended filter for relations
class ActiveRecord::Relation
def efilter(filter)
where(ExtFilter.new(self, filter).to_s)
end
end
class ExtFilter
attr_accessor :relation
attr_accessor :conn
attr_accessor :filter
def initialize(arg_relation, arg_filter)
self.relation = arg_relation
self.conn = relation.connection
self.filter = arg_filter
end
def to_s
parse_filter
end
private
def parse_field_value(args)
field, value = args
raise Error::CustomError, code: "QUERY_EFILTER_EMPTY_FIELD" if field.blank?
raise Error::CustomError, code: "QUERY_EFILTER_EMPTY_VALUE" if value.blank?
field = conn.quote_table_name(field.to_s.underscore)
if value.is_a?(Array)
value = value.map {|v| conn.quote(v) }
elsif value.is_a?(Hash)
raise Error::CustomError, code: "QUERY_EFILTER_INVALID_VALUE"
else
value = conn.quote(value)
end
[field, value]
end
def parse_filter(arg_filter = filter)
# {operator: [arg1, arg2, arg3]}
# {operator: {arg}}
unless arg_filter.is_a?(Hash) && arg_filter.keys.length == 1
raise Error::CustomError,
code: "QUERY_EFILTER_INVALID",
message: "Wrong filter passed: #{arg_filter}"
end
operator, args = arg_filter.first
case operator.to_s.downcase
when "not"
query = parse_filter(args)
"NOT (#{query})"
when "and"
args.map {|arg|
query = parse_filter(arg)
"(#{query})"
}.join(" AND ")
when "or"
args.map {|arg|
query = parse_filter(arg)
"(#{query})"
}.join(" OR ")
when "eq"
field, value = parse_field_value(args)
"#{field} = #{value}"
when "lt"
field, value = parse_field_value(args)
"#{field} < #{value}"
when "le"
field, value = parse_field_value(args)
"#{field} <= #{value}"
when "gt"
field, value = parse_field_value(args)
"#{field} > #{value}"
when "ge"
field, value = parse_field_value(args)
"#{field} >= #{value}"
when "is"
field, value = parse_field_value(args)
if %w[null true false].include? value.to_s.downcase
"#{field} IS #{value}"
else
raise Error::CustomError, code: "QUERY_EFILTER_INVALID_IS_VALUE"
end
when "in"
field, values = parse_field_value(args)
unless values.is_a? Array
raise Error::CustomError, code: "QUERY_EFILTER_INVALID_IN_VALUE"
end
"#{field} IN (#{values.join(',')})"
when "like"
field, value = parse_field_value(args)
"#{field}::TEXT LIKE #{value}"
when "ilike"
field, value = parse_field_value(args)
"#{field}::TEXT ILIKE #{value}"
when "substr"
field, value = parse_field_value(args)
value = value.gsub(/$(')/, "'%").gsub(/(')^/, "%'")
"#{field}::TEXT ILIKE #{value}"
else
raise Error::CustomError, code: "QUERY_EFILTER_INVALID_OPERATOR"
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment