class Mongery::Query
Constants
- OPERATOR_MAP
Attributes
custom_operators[R]
mapped_properties[R]
schema[R]
table[R]
Public Class Methods
new(table, schema, mapped_properties, custom_operators)
click to toggle source
# File lib/mongery.rb, line 53 def initialize(table, schema, mapped_properties, custom_operators) @table = table @schema = schema @mapped_properties = mapped_properties @custom_operators = custom_operators @condition = nil end
Public Instance Methods
arel()
click to toggle source
# File lib/mongery.rb, line 66 def arel @arel ||= build_arel end
count()
click to toggle source
# File lib/mongery.rb, line 123 def count table.project('COUNT(*)').tap do |t| t.where(condition) if condition end end
delete()
click to toggle source
# File lib/mongery.rb, line 144 def delete Arel::DeleteManager.new(table.engine).tap do |manager| manager.from(table) manager.where(condition) if condition end end
index(col)
click to toggle source
# File lib/mongery.rb, line 109 def index(col) Arel.sql(%Q[CREATE INDEX "#{table.name}_#{col}_idx" ON "#{table.name}" ((#{sql_json_exp(col)}))]) end
insert(args)
click to toggle source
# File lib/mongery.rb, line 129 def insert(args) Arel::InsertManager.new(table.engine).tap do |manager| manager.into(table) manager.insert([[table[:id], args['_id']], [table[:data], args.to_json], *mapped_values(args)]) end end
limit(number)
click to toggle source
# File lib/mongery.rb, line 78 def limit(number) arel.take(number) self end
skip(number)
click to toggle source
# File lib/mongery.rb, line 83 def skip(number) arel.skip(number) self end
sort(params)
click to toggle source
# File lib/mongery.rb, line 88 def sort(params) params.each do |col, val| order = val > 0 ? :asc : :desc case col.to_s when "_id" arel.order(table[:id].send(order)) else arel.order(sql_json_path(col).send(order)) end end self end
sql_json_exp(col)
click to toggle source
# File lib/mongery.rb, line 101 def sql_json_exp(col) if schema && numeric?(col) sql_json_path(col) + "::numeric" else sql_json_path(col) end end
to_arel()
click to toggle source
# File lib/mongery.rb, line 70 def to_arel arel end
to_sql()
click to toggle source
# File lib/mongery.rb, line 74 def to_sql to_arel.to_sql end
update(args)
click to toggle source
# File lib/mongery.rb, line 136 def update(args) Arel::UpdateManager.new(table.engine).tap do |manager| manager.table(table) manager.set([[table[:data], args.to_json], *mapped_values(args)]) manager.where(condition) if condition end end
where(args)
click to toggle source
# File lib/mongery.rb, line 61 def where(args) @where = args self end
Private Instance Methods
build_arel()
click to toggle source
# File lib/mongery.rb, line 161 def build_arel table.project(table[:data]).tap do |t| t.where(condition) if condition end end
chain(op, conditions)
click to toggle source
# File lib/mongery.rb, line 347 def chain(op, conditions) result = nil conditions.each do |cond| result = result ? result.send(op, cond) : cond end result end
compare(col, val, op)
click to toggle source
# File lib/mongery.rb, line 286 def compare(col, val, op) operator(wrap(col, val), op, val) end
compare_schema(col, val, type, op)
click to toggle source
# File lib/mongery.rb, line 308 def compare_schema(col, val, type, op) case type when "string" operator(Arel.sql("(#{col})"), op, val) when "number", "integer" operator(Arel.sql("(#{col})::numeric"), op, val) else case val when Numeric operator(Arel.sql("(#{col})"), op, val.to_s) else operator(Arel.sql("(#{col})"), op, val) end end end
condition()
click to toggle source
# File lib/mongery.rb, line 157 def condition @condition ||= translate(@where) end
has_operator?(value)
click to toggle source
# File lib/mongery.rb, line 282 def has_operator?(value) value.keys.any? {|key| key =~ /^\$/ } end
json_pathize(paths)
click to toggle source
# File lib/mongery.rb, line 338 def json_pathize(paths) quote("{#{paths.join(',')}}") end
mapped_keys()
click to toggle source
# File lib/mongery.rb, line 153 def mapped_keys mapped_properties.keys end
mapped_values(args)
click to toggle source
# File lib/mongery.rb, line 113 def mapped_values(args) pairs = [] mapped_properties.each do |key, column| pairs.push([table[column], args[key]]) if args.key?(key) end pairs end
numeric?(col)
click to toggle source
# File lib/mongery.rb, line 303 def numeric?(col) type = schema.column_type(col.to_s) ["number", "integer"].include?(type) end
operator(col, op, val)
click to toggle source
# File lib/mongery.rb, line 324 def operator(col, op, val) case op when Symbol col.send(op, val) else op.call(col, val) end end
quote(str)
click to toggle source
# File lib/mongery.rb, line 342 def quote(str) # FIXME there should be a better way to do this table.engine.connection.quote(str) end
sql_json_path(col)
click to toggle source
# File lib/mongery.rb, line 333 def sql_json_path(col) paths = col.to_s.split('.') Arel.sql("data#>>#{json_pathize(paths)}") end
translate(query)
click to toggle source
# File lib/mongery.rb, line 167 def translate(query) chain(:and, query.map {|col, value| translate_cv(col, value) }) end
translate_cv(col, value)
click to toggle source
# File lib/mongery.rb, line 171 def translate_cv(col, value) case col.to_s when "$or" chain(:or, value.map {|q| translate(q) }) when "$and" chain(:and, value.map {|q| translate(q) }) when /^\$/ raise UnsupportedQuery, "Unsupported operator #{col}" when "_id" translate_value(table[:id], value) when *mapped_keys translate_value(table[mapped_properties[col.to_s]], value) else if schema translate_value_schema(col, sql_json_path(col), value) else translate_value_dynamic(sql_json_path(col), value) end end end
translate_value(col, value)
click to toggle source
# File lib/mongery.rb, line 197 def translate_value(col, value) case value when Hash if has_operator?(value) chain(:and, value.map {|op, val| if custom_operators[op] operator(col, custom_operators[op], val) elsif OPERATOR_MAP.key?(op) col.send(OPERATOR_MAP[op], val) else raise UnsupportedQuery, "Unknown operator #{op}" end }) else col.eq(value.to_json) end else col.eq(value) end end
translate_value_dynamic(col, value)
click to toggle source
# File lib/mongery.rb, line 250 def translate_value_dynamic(col, value) case value when String, TrueClass, FalseClass col.eq(value.to_s) when Numeric, NilClass compare(col, value, :eq) when Hash if has_operator?(value) chain(:and, value.map {|op, val| case op when "$in" if val.all? {|v| v.is_a? Numeric } wrap(col, val.first).in(val) else col.in(val.map(&:to_s)) end when *(custom_operators.keys) compare(col, val, custom_operators[op]) when "$eq", "$ne", "$gt", "$gte", "$lt", "$lte" compare(col, val, OPERATOR_MAP[op]) else raise UnsupportedQuery, "Unknown operator #{op}" end }) else col.eq(value.to_json) end else col.eq(value.to_json) end end
translate_value_schema(column, col, value)
click to toggle source
# File lib/mongery.rb, line 218 def translate_value_schema(column, col, value) type = schema.column_type(column.to_s) case value when Hash if has_operator?(value) chain(:and, value.map {|op, val| case op when "$in" case type when "array" chain(:or, val.map { |v| col.matches(%Q[%"#{v}"%]) }) else compare_schema(col, val, type, :in) end when *(custom_operators.keys) compare_schema(col, val, type, custom_operators[op]) when "$eq", "$ne", "$gt", "$gte", "$lt", "$lte" compare_schema(col, val, type, OPERATOR_MAP[op]) else raise UnsupportedQuery, "Unknown operator #{op}" end }) else col.eq(value.to_json) end when String, Numeric, NilClass compare_schema(col, value, type, :eq) else compare_schema(col, value.to_json, type, :eq) end end
wrap(col, val)
click to toggle source
# File lib/mongery.rb, line 290 def wrap(col, val) case val when NilClass # data#>>'{foo}' IS NULL is invalid # (data#>>'{foo}') IS NULL is valid Arel.sql("(#{col})") when Numeric Arel.sql("(#{col})::numeric") else col end end