lib/bmg/operator/autowrap.rb in bmg-0.16.0.pre.rc1 vs lib/bmg/operator/autowrap.rb in bmg-0.16.0.pre.rc2
- old
+ new
@@ -27,20 +27,23 @@
def initialize(type, operand, options = {})
@type = type
@operand = operand
@original_options = options
- @options = DEFAULT_OPTIONS.merge(options)
- @options[:postprocessor] = NoLeftJoinNoise.new(@options[:postprocessor])
+ @options = normalize_options(options)
end
private
attr_reader :options
public
+ def same_options?(opts)
+ normalize_options(opts) == options
+ end
+
def each
@operand.each do |tuple|
yield autowrap_tuple(tuple)
end
end
@@ -49,40 +52,100 @@
[ :autowrap, operand.to_ast, @original_options.dup ]
end
protected ### optimization
+ def _autowrap(type, opts)
+ if same_options?(opts)
+ self
+ else
+ super
+ end
+ end
+
+ def _join(type, right, on)
+ if _join_optimizable?(type, right, on)
+ operand.join(right, on).autowrap(options)
+ else
+ super
+ end
+ end
+
+ def _joined_with(type, right, on)
+ if _join_optimizable?(type, right, on)
+ right.join(operand, on).autowrap(options)
+ else
+ super
+ end
+ end
+
+ def _join_optimizable?(type, right, on)
+ # 1. Can't optimize if wrapped roots are used in join clause
+ # 2. Can't optimize if other attributes would be autowrapped
+ (wrapped_roots! & on).empty? && wrapped_roots_of!(right, options).empty?
+ rescue UnknownAttributesError
+ false
+ end
+
def _page(type, ordering, page_index, opts)
- return super unless operand.type.knows_attrlist?
- roots = Support.wrapped_roots(operand.type.to_attrlist, options[:split])
attrs = ordering.map{|(a,d)| a }
- if (roots & attrs).empty?
+ if (wrapped_roots! & attrs).empty?
operand.page(ordering, page_index, opts).autowrap(options)
else
super
end
+ rescue UnknownAttributesError
+ super
end
+ def _rename(type, renaming)
+ # 1. Can't optimize if renaming applies to a wrapped one
+ return super unless (wrapped_roots! & renaming.keys).empty?
+
+ # 2. Can't optimize if new attributes would be autowrapped
+ new_roots = Support.wrapped_roots(renaming.values, options[:split])
+ return super unless new_roots.empty?
+
+ operand.rename(renaming).autowrap(options)
+ rescue UnknownAttributesError
+ super
+ end
+
def _restrict(type, predicate)
- return super unless operand.type.knows_attrlist?
- roots = Support.wrapped_roots(operand.type.to_attrlist, options[:split])
vars = predicate.free_variables
- if (roots & vars).empty?
+ if (wrapped_roots! & vars).empty?
operand.restrict(predicate).autowrap(options)
else
super
end
+ rescue UnknownAttributesError
+ super
end
protected ### inspect
def args
[ options ]
end
private
+ def wrapped_roots!
+ @wrapped_roots ||= wrapped_roots_of!(operand, options)
+ end
+
+ def wrapped_roots_of!(r, opts)
+ raise UnknownAttributesError unless r.type.knows_attrlist?
+ Support.wrapped_roots(r.type.to_attrlist, opts[:split])
+ end
+
+ def normalize_options(options)
+ opts = DEFAULT_OPTIONS.merge(options)
+ opts[:postprocessor] = NoLeftJoinNoise.new(opts[:postprocessor])
+ opts
+ end
+
def autowrap_tuple(tuple)
separator = @options[:split]
autowrapped = tuple.each_with_object({}){|(k,v),h|
parts = k.to_s.split(separator).map(&:to_sym)
sub = h
@@ -135,10 +198,11 @@
when Hash then ->(t,k){ REMOVERS[remover[k] || :none].call(t,k) }
else
raise "Invalid remover `#{remover}`"
end
end
+ attr_reader :remover
def call(tuple)
tuple.each_key do |k|
@remover.call(tuple, k) if tuple[k].is_a?(Hash) && all_nil?(tuple[k])
end
@@ -152,9 +216,17 @@
def inspect
@remover_to_s.inspect
end
alias :to_s :inspect
+
+ def hash
+ remover.hash
+ end
+
+ def ==(other)
+ other.is_a?(NoLeftJoinNoise) && remover.eql?(other.remover)
+ end
end # NoLeftJoinNoise
module Support