class Mongomery::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/mongomery.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/mongomery.rb, line 66
def arel
  @arel ||= build_arel
end
condition() click to toggle source
# File lib/mongomery.rb, line 147
def condition
  @condition ||= translate(@where)
end
count() click to toggle source
# File lib/mongomery.rb, line 119
def count
  table.project('COUNT(*)').tap do |t|
    t.where(condition) if condition
  end
end
delete() click to toggle source
# File lib/mongomery.rb, line 140
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/mongomery.rb, line 105
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/mongomery.rb, line 125
def insert(args)
  Arel::InsertManager.new(table.engine).tap do |manager|
    manager.into(table)
    manager.insert(Hash[args.map {|k, v| [table[k], v] }].to_a)
  end
end
limit(number) click to toggle source
# File lib/mongomery.rb, line 78
def limit(number)
  arel.take(number)
  self
end
skip(number) click to toggle source
# File lib/mongomery.rb, line 83
def skip(number)
  arel.skip(number)
  self
end
sort(params) click to toggle source
# File lib/mongomery.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/mongomery.rb, line 101
def sql_json_exp(col)
  sql_json_path(col)
end
to_arel() click to toggle source
# File lib/mongomery.rb, line 70
def to_arel
  arel
end
to_sql() click to toggle source
# File lib/mongomery.rb, line 74
def to_sql
  to_arel.to_sql
end
update(args) click to toggle source
# File lib/mongomery.rb, line 132
def update(args)
  Arel::UpdateManager.new(table.engine).tap do |manager|
    manager.table(table)
    manager.set(Hash[args.map {|k, v| [table[k], v] }].to_a)
    manager.where(condition) if condition
  end
end
where(args) click to toggle source
# File lib/mongomery.rb, line 61
def where(args)
  @where = args
  self
end

Private Instance Methods

build_arel() click to toggle source
# File lib/mongomery.rb, line 157
def build_arel
  table.project("*").tap do |t|
    t.where(condition) if condition
  end
end
chain(op, conditions) click to toggle source
# File lib/mongomery.rb, line 338
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/mongomery.rb, line 282
def compare(col, val, op)
  operator(wrap(col, val), op, val)
end
compare_schema(col, val, type, op) click to toggle source
# File lib/mongomery.rb, line 302
def compare_schema(col, val, type, op)
  case type
    when "string"
      operator(Arel.sql("(#{col})"), 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
has_operator?(value) click to toggle source
# File lib/mongomery.rb, line 278
def has_operator?(value)
  value.keys.any? {|key| key =~ /^\$/ }
end
json_pathize(paths) click to toggle source
# File lib/mongomery.rb, line 329
def json_pathize(paths)
  quote("{#{paths.join(',')}}")
end
mapped_keys() click to toggle source
# File lib/mongomery.rb, line 153
def mapped_keys
  mapped_properties.keys
end
mapped_values(args) click to toggle source
# File lib/mongomery.rb, line 109
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/mongomery.rb, line 297
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/mongomery.rb, line 316
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/mongomery.rb, line 333
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/mongomery.rb, line 325
def sql_json_path(col)
  Arel.sql("#{col.to_s}")
end
translate(query) click to toggle source
# File lib/mongomery.rb, line 163
def translate(query)
  chain(:and, query.map {|col, value| translate_cv(col, value) })
end
translate_cv(col, value) click to toggle source
# File lib/mongomery.rb, line 167
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/mongomery.rb, line 193
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/mongomery.rb, line 246
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/mongomery.rb, line 214
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/mongomery.rb, line 286
def wrap(col, val)
  case val
    when NilClass
      # data#>>'{foo}' IS NULL    is invalid
      # (data#>>'{foo}') IS NULL  is valid
      Arel.sql("(#{col})")
    else
      col
  end
end