lib/locomotive/steam/adapters/mongodb/query.rb
module Locomotive::Steam
module Adapters
module MongoDB
class Query
SYMBOL_OPERATORS = %w(all elem_match exists gt gte in lt lte mod ne near near_sphere nin with_size with_type within_box within_circle within_polygon within_spherical_circle)
attr_reader :criteria, :sort
def initialize(scope, localized_attributes, &block)
@criteria, @sort, @fields, @skip, @limit = {}, nil, nil, nil, nil
@scope, @localized_attributes = scope, localized_attributes
apply_default_scope
instance_eval(&block) if block_given?
end
def where(criterion = nil)
self.tap do
@criteria.merge!(decode_symbol_operators(criterion)) unless criterion.nil?
end
end
def order_by(*args)
self.tap do
@sort = decode_order_by(*args)
end
end
def only(*args)
self.tap do
@fields = [*args]
end
end
def offset(offset)
self.tap { @skip = offset }
end
def limit(limit)
self.tap { @limit = limit }
end
def against(collection)
_query = to_origin
selector, fields, sort = _query.selector, _query.options[:fields], _query.options[:sort]
results = collection.find(selector)
results = results.sort(sort) if sort
results = results.projection(fields) if fields
results = results.skip(@skip) if @skip
results = results.limit(@limit) if @limit
results
end
def to_origin
build_origin_query.only(@fields).where(@criteria).order_by(*@sort)
end
def key(name, operator)
:"#{name}".send(operator.to_sym)
end
alias :k :key
private
def build_origin_query
::Origin::Query.new(build_aliases(@localized_attributes, @scope.locale))
end
def build_aliases(localized_attributes, locale)
localized_attributes.inject({}) do |aliases, name|
aliases.tap do
aliases[name.to_s] = "#{name}.#{locale}"
end
end
end
def apply_default_scope
where(site_id: @scope.site._id) if @scope.site
end
def decode_symbol_operators(criterion)
criterion.dup.tap do |_criterion|
criterion.each do |key, value|
next unless key.is_a?(String)
_key, operator = key.split('.')
if operator && SYMBOL_OPERATORS.include?(operator)
_criterion.delete(key)
_key = _key.to_s.to_sym.public_send(operator.to_sym)
_criterion[_key] = value
end
end
end
end
def decode_order_by(*spec)
[*spec].compact.map do |arg|
_decode_order_by(arg)
end
end
def _decode_order_by(arg)
case arg
when String
if arg.include?(',')
_decode_order_by(arg.split(','))
else
arg.strip.split(/[\s|.]/)
end
when Array then arg.map { |_arg| _decode_order_by(_arg) }
else arg
end
end
end
end
end
end