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