veritas-optimizer 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. data/.travis.yml +4 -2
  2. data/Gemfile +8 -9
  3. data/Guardfile +3 -2
  4. data/README.rdoc +55 -0
  5. data/Rakefile +2 -2
  6. data/TODO +100 -98
  7. data/config/flay.yml +2 -2
  8. data/config/flog.yml +1 -1
  9. data/config/roodi.yml +2 -2
  10. data/config/site.reek +2 -2
  11. data/lib/veritas/optimizer/algebra/difference.rb +1 -1
  12. data/lib/veritas/optimizer/algebra/intersection.rb +1 -1
  13. data/lib/veritas/optimizer/algebra/join.rb +117 -1
  14. data/lib/veritas/optimizer/algebra/product.rb +1 -1
  15. data/lib/veritas/optimizer/algebra/rename.rb +12 -1
  16. data/lib/veritas/optimizer/algebra/restriction.rb +148 -0
  17. data/lib/veritas/optimizer/algebra/union.rb +1 -1
  18. data/lib/veritas/optimizer/function/connective/binary.rb +61 -8
  19. data/lib/veritas/optimizer/function/connective/conjunction.rb +1 -1
  20. data/lib/veritas/optimizer/function/connective/disjunction.rb +1 -1
  21. data/lib/veritas/optimizer/function/predicate/comparable.rb +4 -4
  22. data/lib/veritas/optimizer/relation/operation/binary.rb +1 -1
  23. data/lib/veritas/optimizer/support/predicate_partition.rb +182 -0
  24. data/lib/veritas/optimizer/version.rb +1 -1
  25. data/lib/veritas/optimizer.rb +2 -0
  26. data/spec/integration/veritas/algebra/rename/optimize_spec.rb +30 -30
  27. data/spec/integration/veritas/algebra/restriction/optimize_spec.rb +3 -3
  28. data/spec/integration/veritas/relation/operation/limit/optimize_spec.rb +1 -1
  29. data/spec/integration/veritas/relation/operation/offset/optimize_spec.rb +1 -1
  30. data/spec/integration/veritas/relation/operation/order/optimize_spec.rb +6 -6
  31. data/spec/integration/veritas/relation/operation/reverse/optimize_spec.rb +3 -3
  32. data/spec/spec_helper.rb +2 -1
  33. data/spec/unit/veritas/optimizer/algebra/extension/order_operand/optimizable_spec.rb +1 -1
  34. data/spec/unit/veritas/optimizer/algebra/extension/order_operand/optimize_spec.rb +1 -1
  35. data/spec/unit/veritas/optimizer/algebra/join/left_materialized_operand/optimizable_spec.rb +42 -0
  36. data/spec/unit/veritas/optimizer/algebra/join/left_materialized_operand/optimize_spec.rb +55 -0
  37. data/spec/unit/veritas/optimizer/algebra/join/right_materialized_operand/optimizable_spec.rb +42 -0
  38. data/spec/unit/veritas/optimizer/algebra/join/right_materialized_operand/optimize_spec.rb +55 -0
  39. data/spec/unit/veritas/optimizer/algebra/rename/limit_operand/optimizable_spec.rb +1 -1
  40. data/spec/unit/veritas/optimizer/algebra/rename/limit_operand/optimize_spec.rb +1 -1
  41. data/spec/unit/veritas/optimizer/algebra/rename/offset_operand/optimizable_spec.rb +1 -1
  42. data/spec/unit/veritas/optimizer/algebra/rename/offset_operand/optimize_spec.rb +1 -1
  43. data/spec/unit/veritas/optimizer/algebra/rename/order_operand/optimizable_spec.rb +1 -1
  44. data/spec/unit/veritas/optimizer/algebra/rename/order_operand/optimize_spec.rb +1 -1
  45. data/spec/unit/veritas/optimizer/algebra/rename/reverse_operand/optimizable_spec.rb +1 -1
  46. data/spec/unit/veritas/optimizer/algebra/rename/reverse_operand/optimize_spec.rb +1 -1
  47. data/spec/unit/veritas/optimizer/algebra/restriction/combination_operand/optimizable_spec.rb +41 -0
  48. data/spec/unit/veritas/optimizer/algebra/restriction/combination_operand/optimize_spec.rb +35 -0
  49. data/spec/unit/veritas/optimizer/algebra/restriction/join_operand/optimizable_spec.rb +51 -0
  50. data/spec/unit/veritas/optimizer/algebra/restriction/join_operand/optimize_spec.rb +48 -0
  51. data/spec/unit/veritas/optimizer/algebra/restriction/order_operand/optimizable_spec.rb +1 -1
  52. data/spec/unit/veritas/optimizer/algebra/restriction/order_operand/optimize_spec.rb +1 -1
  53. data/spec/unit/veritas/optimizer/algebra/restriction/product_operand/optimizable_spec.rb +44 -0
  54. data/spec/unit/veritas/optimizer/algebra/restriction/product_operand/optimize_spec.rb +48 -0
  55. data/spec/unit/veritas/optimizer/algebra/restriction/unoptimized_operand/optimize_spec.rb +4 -4
  56. data/spec/unit/veritas/optimizer/algebra/summarization/empty_operand/optimize_spec.rb +1 -1
  57. data/spec/unit/veritas/optimizer/algebra/summarization/empty_summarize_per/optimize_spec.rb +7 -7
  58. data/spec/unit/veritas/optimizer/algebra/summarization/order_operand/optimizable_spec.rb +1 -1
  59. data/spec/unit/veritas/optimizer/algebra/summarization/order_operand/optimize_spec.rb +1 -1
  60. data/spec/unit/veritas/optimizer/function/connective/conjunction/optimizable_to_exclusion/optimizable_spec.rb +38 -6
  61. data/spec/unit/veritas/optimizer/function/connective/conjunction/optimizable_to_exclusion/optimize_spec.rb +44 -6
  62. data/spec/unit/veritas/optimizer/function/connective/disjunction/optimizable_to_inclusion/optimizable_spec.rb +38 -6
  63. data/spec/unit/veritas/optimizer/function/connective/disjunction/optimizable_to_inclusion/optimize_spec.rb +44 -6
  64. data/spec/unit/veritas/optimizer/predicate_partition/left_spec.rb +149 -0
  65. data/spec/unit/veritas/optimizer/predicate_partition/remainder_spec.rb +149 -0
  66. data/spec/unit/veritas/optimizer/predicate_partition/right_spec.rb +149 -0
  67. data/spec/unit/veritas/optimizer/relation/operation/binary/left_order_operand/optimizable_spec.rb +1 -1
  68. data/spec/unit/veritas/optimizer/relation/operation/binary/left_order_operand/optimize_spec.rb +1 -1
  69. data/spec/unit/veritas/optimizer/relation/operation/binary/{materialized_operand → materialized_operands}/optimizable_spec.rb +1 -1
  70. data/spec/unit/veritas/optimizer/relation/operation/binary/{materialized_operand → materialized_operands}/optimize_spec.rb +1 -1
  71. data/spec/unit/veritas/optimizer/relation/operation/binary/right_order_operand/optimizable_spec.rb +1 -1
  72. data/spec/unit/veritas/optimizer/relation/operation/binary/right_order_operand/optimize_spec.rb +1 -1
  73. data/spec/unit/veritas/optimizer/relation/operation/limit/equal_limit_operand/optimizable_spec.rb +3 -3
  74. data/spec/unit/veritas/optimizer/relation/operation/limit/equal_limit_operand/optimize_spec.rb +4 -4
  75. data/spec/unit/veritas/optimizer/relation/operation/limit/limit_operand/optimizable_spec.rb +3 -3
  76. data/spec/unit/veritas/optimizer/relation/operation/limit/limit_operand/optimize_spec.rb +3 -3
  77. data/spec/unit/veritas/optimizer/relation/operation/limit/unoptimized_operand/optimizable_spec.rb +4 -4
  78. data/spec/unit/veritas/optimizer/relation/operation/limit/unoptimized_operand/optimize_spec.rb +4 -4
  79. data/spec/unit/veritas/optimizer/relation/operation/limit/zero_limit/optimizable_spec.rb +3 -3
  80. data/spec/unit/veritas/optimizer/relation/operation/limit/zero_limit/optimize_spec.rb +4 -4
  81. data/spec/unit/veritas/optimizer/relation/operation/offset/offset_operand/optimizable_spec.rb +3 -3
  82. data/spec/unit/veritas/optimizer/relation/operation/offset/offset_operand/optimize_spec.rb +3 -3
  83. data/spec/unit/veritas/optimizer/relation/operation/offset/unoptimized_operand/optimizable_spec.rb +4 -4
  84. data/spec/unit/veritas/optimizer/relation/operation/offset/unoptimized_operand/optimize_spec.rb +4 -4
  85. data/spec/unit/veritas/optimizer/relation/operation/offset/zero_offset/optimizable_spec.rb +3 -3
  86. data/spec/unit/veritas/optimizer/relation/operation/offset/zero_offset/optimize_spec.rb +3 -3
  87. data/spec/unit/veritas/optimizer/relation/operation/order/one_limit_operand/optimizable_spec.rb +3 -3
  88. data/spec/unit/veritas/optimizer/relation/operation/order/one_limit_operand/optimize_spec.rb +2 -2
  89. data/spec/unit/veritas/optimizer/relation/operation/order/order_operand/optimizable_spec.rb +2 -2
  90. data/spec/unit/veritas/optimizer/relation/operation/order/order_operand/optimize_spec.rb +2 -2
  91. data/spec/unit/veritas/optimizer/relation/operation/order/unoptimized_operand/optimizable_spec.rb +1 -1
  92. data/spec/unit/veritas/optimizer/relation/operation/order/unoptimized_operand/optimize_spec.rb +1 -1
  93. data/spec/unit/veritas/optimizer/relation/operation/reverse/order_operand/optimizable_spec.rb +2 -2
  94. data/spec/unit/veritas/optimizer/relation/operation/reverse/order_operand/optimize_spec.rb +1 -1
  95. data/spec/unit/veritas/optimizer/relation/operation/reverse/reverse_operand/optimizable_spec.rb +3 -3
  96. data/spec/unit/veritas/optimizer/relation/operation/reverse/reverse_operand/optimize_spec.rb +1 -1
  97. data/spec/unit/veritas/optimizer/relation/operation/reverse/unoptimized_operand/optimizable_spec.rb +3 -3
  98. data/spec/unit/veritas/optimizer/relation/operation/reverse/unoptimized_operand/optimize_spec.rb +1 -1
  99. data/spec/unit/veritas/optimizer/relation/operation/unary/order_operand/optimizable_spec.rb +1 -1
  100. data/spec/unit/veritas/optimizer/relation/operation/unary/order_operand/optimize_spec.rb +1 -1
  101. data/tasks/metrics/heckle.rake +1 -0
  102. data/veritas-optimizer.gemspec +33 -19
  103. metadata +37 -23
@@ -137,6 +137,152 @@ module Veritas
137
137
 
138
138
  end # class RestrictionOperand
139
139
 
140
+ # Optimize when the operand is a combine operation
141
+ class CombinationOperand < self
142
+
143
+ # Test if the restriction is commutative
144
+ #
145
+ # @return [Boolean]
146
+ #
147
+ # @api private
148
+ def optimizable?
149
+ restriction_commutative?
150
+ end
151
+
152
+ # Distribute the restriction across the operation and apply to the operands
153
+ #
154
+ # @return [Restriction]
155
+ #
156
+ # @api private
157
+ def optimize
158
+ left_restriction.send(relation_method, right_restriction).restrict { partition.remainder }
159
+ end
160
+
161
+ private
162
+
163
+ # Return a predicate partition for the restriction and operand headers
164
+ #
165
+ # @return [PredicatePartition]
166
+ #
167
+ # @api private
168
+ def partition
169
+ operand = self.operand
170
+ PredicatePartition.new(predicate, operand.left.header, operand.right.header)
171
+ end
172
+
173
+ # Test if the restriction can be distributed over the operation
174
+ #
175
+ # If the predicates for the left and right operands would match
176
+ # everything, there is no point in distributing the restriction
177
+ # across the operation, since it will not affect the result.
178
+ #
179
+ # @return [Boolean]
180
+ #
181
+ # @api private
182
+ def restriction_commutative?
183
+ !(partition_left_tautology? && partition_right_tautology?)
184
+ end
185
+
186
+ # Test if the predicate for the left operand would match everything
187
+ #
188
+ # @return [Boolean]
189
+ #
190
+ # @api private
191
+ def partition_left_tautology?
192
+ partition.left.equal?(Veritas::Function::Proposition::Tautology.instance)
193
+ end
194
+
195
+ # Test if the predicate for the right operand would match everything
196
+ #
197
+ # @return [Boolean]
198
+ #
199
+ # @api private
200
+ def partition_right_tautology?
201
+ partition.right.equal?(Veritas::Function::Proposition::Tautology.instance)
202
+ end
203
+
204
+ # Abstract method to return the relation method name
205
+ #
206
+ # @raise [NotImplementedError]
207
+ # raised when the subclass does not implement the method
208
+ #
209
+ # @api private
210
+ def relation_method
211
+ raise NotImplementedError, "#{self.class}#relation_method must be implemented"
212
+ end
213
+
214
+ # Restrict the left operand with the left predicate partition
215
+ #
216
+ # @return [Restriction]
217
+ #
218
+ # @api private
219
+ def left_restriction
220
+ operand.left.restrict { partition.left }
221
+ end
222
+
223
+ # Restrict the right operand with the right predicate partition
224
+ #
225
+ # @return [Restriction]
226
+ #
227
+ # @api private
228
+ def right_restriction
229
+ operand.right.restrict { partition.right }
230
+ end
231
+
232
+ memoize :partition
233
+ end
234
+
235
+ # Optimize when the operand is a Join
236
+ class JoinOperand < CombinationOperand
237
+
238
+ # Test if the operand is a Join and the restriction is commutative
239
+ #
240
+ # @return [Boolean]
241
+ #
242
+ # @api private
243
+ def optimizable?
244
+ operand.kind_of?(Veritas::Algebra::Join) && super
245
+ end
246
+
247
+ private
248
+
249
+ # Return the relation method name for a Join operation
250
+ #
251
+ # @return [Symbol]
252
+ #
253
+ # @api private
254
+ def relation_method
255
+ :join
256
+ end
257
+
258
+ end # class JoinOperand
259
+
260
+ # Optimize when the operand is a Product
261
+ class ProductOperand < CombinationOperand
262
+
263
+ # Test if the operand is a Join and the restriction is commutative
264
+ #
265
+ # @return [Boolean]
266
+ #
267
+ # @api private
268
+ def optimizable?
269
+ operand.kind_of?(Veritas::Algebra::Product) && super
270
+ end
271
+
272
+ private
273
+
274
+ # Return the relation method name for a Product operation
275
+ #
276
+ # @return [Symbol]
277
+ #
278
+ # @api private
279
+
280
+ def relation_method
281
+ :product
282
+ end
283
+
284
+ end # class ProductOperand
285
+
140
286
  # Optimize when the operand is a Set
141
287
  class SetOperand < self
142
288
 
@@ -213,6 +359,8 @@ module Veritas
213
359
  Tautology,
214
360
  Contradiction,
215
361
  RestrictionOperand,
362
+ JoinOperand,
363
+ ProductOperand,
216
364
  SetOperand,
217
365
  OrderOperand,
218
366
  EmptyOperand,
@@ -58,7 +58,7 @@ module Veritas
58
58
  EmptyLeft,
59
59
  LeftOrderOperand,
60
60
  RightOrderOperand,
61
- MaterializedOperand,
61
+ MaterializedOperands,
62
62
  UnoptimizedOperands
63
63
  )
64
64
 
@@ -11,27 +11,27 @@ module Veritas
11
11
 
12
12
  private
13
13
 
14
- # Test if the operands are equality predicates for the same attribute
14
+ # Test if the operands are equality/inclusion predicates for the same attribute
15
15
  #
16
16
  # @return [Boolean]
17
17
  #
18
18
  # @api private
19
19
  def equality_with_same_attributes?
20
- left.kind_of?(Veritas::Function::Predicate::Equality) &&
21
- right.kind_of?(Veritas::Function::Predicate::Equality) &&
22
- same_attribute? &&
20
+ left_equality? &&
21
+ right_equality? &&
22
+ same_attribute? &&
23
23
  constant_value?
24
24
  end
25
25
 
26
- # Test if the operands are inequality predicates for the same attribute
26
+ # Test if the operands are inequality/exclusion predicates for the same attribute
27
27
  #
28
28
  # @return [Boolean]
29
29
  #
30
30
  # @api private
31
31
  def inequality_with_same_attributes?
32
- left.kind_of?(Veritas::Function::Predicate::Inequality) &&
33
- right.kind_of?(Veritas::Function::Predicate::Inequality) &&
34
- same_attribute? &&
32
+ left_inequality? &&
33
+ right_inequality? &&
34
+ same_attribute? &&
35
35
  constant_value?
36
36
  end
37
37
 
@@ -54,6 +54,50 @@ module Veritas
54
54
  util.constant?(left.right) && util.constant?(right.right)
55
55
  end
56
56
 
57
+ # Test if the left is an equality or inclusion
58
+ #
59
+ # @return [Boolean]
60
+ #
61
+ # @api private
62
+ def left_equality?
63
+ util = Veritas::Function::Predicate
64
+ left = self.left
65
+ left.kind_of?(util::Equality) || left.kind_of?(util::Inclusion)
66
+ end
67
+
68
+ # Test if the right is an equality or inclusion
69
+ #
70
+ # @return [Boolean]
71
+ #
72
+ # @api private
73
+ def right_equality?
74
+ util = Veritas::Function::Predicate
75
+ right = self.right
76
+ right.kind_of?(util::Equality) || right.kind_of?(util::Inclusion)
77
+ end
78
+
79
+ # Test if the left is an inequality or exclusion
80
+ #
81
+ # @return [Boolean]
82
+ #
83
+ # @api private
84
+ def left_inequality?
85
+ util = Veritas::Function::Predicate
86
+ left = self.left
87
+ left.kind_of?(util::Inequality) || left.kind_of?(util::Exclusion)
88
+ end
89
+
90
+ # Test if the right is an inequality or exclusion
91
+ #
92
+ # @return [Boolean]
93
+ #
94
+ # @api private
95
+ def right_inequality?
96
+ util = Veritas::Function::Predicate
97
+ right = self.right
98
+ right.kind_of?(util::Inequality) || right.kind_of?(util::Exclusion)
99
+ end
100
+
57
101
  # Test if the left is a tautology
58
102
  #
59
103
  # @return [Boolean]
@@ -103,6 +147,15 @@ module Veritas
103
147
  left.inverse.eql?(right)
104
148
  end
105
149
 
150
+ # Merge the right enumerables from the operands
151
+ #
152
+ # @return [Array]
153
+ #
154
+ # @api private
155
+ def merged_right_enumerables
156
+ [ left.right, right.right ].flatten
157
+ end
158
+
106
159
  # Optimize when the operands are constants
107
160
  class ConstantOperands < self
108
161
  include Function::Binary::ConstantOperands
@@ -73,7 +73,7 @@ module Veritas
73
73
  # @api private
74
74
  def optimize
75
75
  left = self.left
76
- Veritas::Function::Predicate::Exclusion.new(left.left, [ left.right, right.right ]).optimize
76
+ Veritas::Function::Predicate::Exclusion.new(left.left, merged_right_enumerables).optimize
77
77
  end
78
78
 
79
79
  end # class OptimizableToExclusion
@@ -73,7 +73,7 @@ module Veritas
73
73
  # @api private
74
74
  def optimize
75
75
  left = self.left
76
- Veritas::Function::Predicate::Inclusion.new(left.left, [ left.right, right.right ]).optimize
76
+ Veritas::Function::Predicate::Inclusion.new(left.left, merged_right_enumerables).optimize
77
77
  end
78
78
 
79
79
  end # class OptimizableToInclusion
@@ -45,7 +45,7 @@ module Veritas
45
45
  if util.constant?(left) then left_invalid_constant?
46
46
  elsif util.constant?(right) then right_invalid_constant?
47
47
  else
48
- not_joinable?
48
+ !joinable?
49
49
  end
50
50
  end
51
51
 
@@ -69,14 +69,14 @@ module Veritas
69
69
  !left.valid_value?(right)
70
70
  end
71
71
 
72
- # Test if the left and right operand are not joinable
72
+ # Test if the left and right operand are joinable
73
73
  #
74
74
  # @return [Boolean]
75
75
  #
76
76
  # @api private
77
- def not_joinable?
77
+ def joinable?
78
78
  left = self.left
79
- !(left.respond_to?(:joinable?) && left.joinable?(right))
79
+ left.respond_to?(:joinable?) && left.joinable?(right)
80
80
  end
81
81
 
82
82
  end # module NeverEquivalent
@@ -100,7 +100,7 @@ module Veritas
100
100
  end # class RightOrderOperand
101
101
 
102
102
  # Optimize when the operands are Materialized
103
- class MaterializedOperand < self
103
+ class MaterializedOperands < self
104
104
 
105
105
  # Test if the operands are materialized
106
106
  #
@@ -0,0 +1,182 @@
1
+ # encoding: utf-8
2
+
3
+ module Veritas
4
+ class Optimizer
5
+
6
+ # Partition a predicate to distribute it over binary operations
7
+ class PredicatePartition
8
+ include Immutable
9
+
10
+ TAUTOLOGY = Veritas::Function::Proposition::Tautology.instance
11
+
12
+ # Returns the predicate for the left header
13
+ #
14
+ # @return [Function]
15
+ #
16
+ # @api private
17
+ attr_reader :left
18
+
19
+ # Returns the predicate for the right header
20
+ #
21
+ # @return [Function]
22
+ #
23
+ # @api private
24
+ attr_reader :right
25
+
26
+ # Returns the remainder predicate
27
+ #
28
+ # @return [Function]
29
+ #
30
+ # @api private
31
+ attr_reader :remainder
32
+
33
+ # Initialize a Predication Partition
34
+ #
35
+ # @example
36
+ # partition = PredicatePartition.new(predicate, left_header, right_header)
37
+ #
38
+ # @param [Function] predicate
39
+ #
40
+ # @param [Header] left_header
41
+ #
42
+ # @param [Header] right_header
43
+ #
44
+ # @return [undefined]
45
+ #
46
+ # @api private
47
+ def initialize(predicate, left_header, right_header)
48
+ @left = TAUTOLOGY
49
+ @right = TAUTOLOGY
50
+ @remainder = TAUTOLOGY
51
+
52
+ @left_header = left_header
53
+ @right_header = right_header
54
+
55
+ partition!(predicate)
56
+ end
57
+
58
+ private
59
+
60
+ # Partition the predicate into a left, right and remainder predicates
61
+ #
62
+ # @param [Function] predicate
63
+ #
64
+ # @return [undefined]
65
+ #
66
+ # @api private
67
+ def partition!(predicate)
68
+ each_operand(predicate) do |operand|
69
+ case operand
70
+ when Veritas::Function::Binary then partition_binary!(operand)
71
+ when Veritas::Function::Unary then partition_unary!(operand)
72
+ when Veritas::Attribute::Boolean then partition_attribute!(operand)
73
+ else
74
+ partition_proposition!(operand)
75
+ end
76
+ end
77
+ end
78
+
79
+ # Partition the binary function up into a left, right and remainder predicates
80
+ #
81
+ # @param [Function::Binary] function
82
+ #
83
+ # @return [undefined]
84
+ #
85
+ # @api private
86
+ def partition_binary!(function)
87
+ operands = [ function.left, function.right ]
88
+
89
+ left_operands = @left_header & operands
90
+ right_operands = @right_header & operands
91
+
92
+ if (left_operands - right_operands).empty? || (right_operands - left_operands).empty?
93
+ @left &= function if left_operands.any?
94
+ @right &= function if right_operands.any?
95
+ else
96
+ @remainder &= function
97
+ end
98
+ end
99
+
100
+ # Partition the unary function up into the left and right predicates
101
+ #
102
+ # @param [Function::Unary] function
103
+ #
104
+ # @return [undefined]
105
+ #
106
+ # @api private
107
+ def partition_unary!(function)
108
+ operand = function.operand
109
+ @left &= function if @left_header.include?(operand)
110
+ @right &= function if @right_header.include?(operand)
111
+ end
112
+
113
+ # Partition the attribute up into the left and right predicates
114
+ #
115
+ # @param [Attribute] attribute
116
+ #
117
+ # @return [undefined]
118
+ #
119
+ # @api private
120
+ def partition_attribute!(attribute)
121
+ @left &= attribute if @left_header.include?(attribute)
122
+ @right &= attribute if @right_header.include?(attribute)
123
+ end
124
+
125
+ # Partition the proposition up into the left, right and remainder predicates
126
+ #
127
+ # @param [Function::Proposition] proposition
128
+ #
129
+ # @return [undefined]
130
+ #
131
+ # @api private
132
+ def partition_proposition!(proposition)
133
+ @remainder &= proposition
134
+ @left &= proposition
135
+ @right &= proposition
136
+ end
137
+
138
+ # Yield each operand in the predicate recursively
139
+ #
140
+ # @param [Function] predicate
141
+ #
142
+ # @yield [operand]
143
+ #
144
+ # @yieldparam [Function] operand
145
+ # each operand in the predicate
146
+ #
147
+ # @yieldreturn [undefined]
148
+ #
149
+ # @return [undefined]
150
+ #
151
+ # @api private
152
+ def each_operand(predicate, &block)
153
+ case predicate
154
+ when Veritas::Function::Connective::Disjunction then each_operand(predicate.inverse.optimize, &block)
155
+ when Veritas::Function::Connective::Conjunction then each_conjunction(predicate, &block)
156
+ else
157
+ block.call(predicate)
158
+ end
159
+ end
160
+
161
+ # Yield each operand of the conjunction
162
+ #
163
+ # @param [Function::Connective::Conjunction] conjunction
164
+ #
165
+ # @yield [operand]
166
+ #
167
+ # @yieldparam [Function] operand
168
+ # each operand in the conjunction
169
+ #
170
+ # @yieldreturn [undefined]
171
+ #
172
+ # @return [undefined]
173
+ #
174
+ # @api private
175
+ def each_conjunction(conjunction, &block)
176
+ each_operand(conjunction.left, &block)
177
+ each_operand(conjunction.right, &block)
178
+ end
179
+
180
+ end # class PredicatePartition
181
+ end # class Optimizer
182
+ end # module Veritas
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Veritas
4
4
  class Optimizer
5
- VERSION = '0.0.4'
5
+ VERSION = '0.0.5'
6
6
  end # class Optimizer
7
7
  end # module Veritas
@@ -95,6 +95,8 @@ end # module Veritas
95
95
 
96
96
  require 'veritas/optimizer/version'
97
97
 
98
+ require 'veritas/optimizer/support/predicate_partition'
99
+
98
100
  require 'veritas/optimizer/optimizable'
99
101
 
100
102
  require 'veritas/optimizer/function'
@@ -183,7 +183,7 @@ describe Algebra::Rename, '#optimize' do
183
183
  end
184
184
 
185
185
  context 'containing a restriction' do
186
- let(:operand) { relation.restrict { |r| r[:id].eq(1) } }
186
+ let(:operand) { relation.restrict { |r| r.id.eq(1) } }
187
187
 
188
188
  it { should be_kind_of(Algebra::Restriction) }
189
189
 
@@ -204,11 +204,11 @@ describe Algebra::Rename, '#optimize' do
204
204
  end
205
205
 
206
206
  context 'containing a restriction, containing a object that cancels out' do
207
- let(:operand) { relation.rename(:id => :other_id).restrict { |r| r[:other_id].eq(1) } }
208
- let(:aliases) { { :other_id => :id } }
207
+ let(:operand) { relation.rename(:id => :other_id).restrict { |r| r.other_id.eq(1) } }
208
+ let(:aliases) { { :other_id => :id } }
209
209
 
210
210
  it 'pushes the object before the restriction, and then cancel it out' do
211
- should eql(relation.restrict { |r| r[:id].eq(1) })
211
+ should eql(relation.restrict { |r| r.id.eq(1) })
212
212
  end
213
213
 
214
214
  it 'returns an equivalent relation to the unoptimized operation' do
@@ -267,11 +267,11 @@ describe Algebra::Rename, '#optimize' do
267
267
  end
268
268
 
269
269
  context 'containing a reverse operation' do
270
- let(:limit) { relation.order.take(2) }
271
- let(:operand) { limit.reverse }
270
+ let(:limit) { relation.sort_by { |r| [ r.id, r.name ] }.take(2) }
271
+ let(:operand) { limit.reverse }
272
272
 
273
273
  it 'pushes the object under the order, limit and reverse' do
274
- should eql(relation.rename(aliases).order.take(2).reverse)
274
+ should eql(relation.rename(aliases).sort_by { |r| [ r.other_id, r.name ] }.take(2).reverse)
275
275
  end
276
276
 
277
277
  it 'returns an equivalent relation to the unoptimized operation' do
@@ -287,12 +287,12 @@ describe Algebra::Rename, '#optimize' do
287
287
  end
288
288
 
289
289
  context 'containing a reverse operation, containing a object that cancels out' do
290
- let(:limit) { relation.order.take(2) }
291
- let(:operand) { limit.rename(:id => :other_id).reverse }
292
- let(:aliases) { { :other_id => :id } }
290
+ let(:limit) { relation.sort_by { |r| [ r.id, r.name ] }.take(2) }
291
+ let(:operand) { limit.rename(:id => :other_id).reverse }
292
+ let(:aliases) { { :other_id => :id } }
293
293
 
294
294
  it 'pushes the object under the order, limit and reverse, and then cancel it out' do
295
- should eql(relation.order.take(2).reverse)
295
+ should eql(relation.sort_by { |r| [ r.id, r.name ] }.take(2).reverse)
296
296
  end
297
297
 
298
298
  it 'returns an equivalent relation to the unoptimized operation' do
@@ -308,10 +308,10 @@ describe Algebra::Rename, '#optimize' do
308
308
  end
309
309
 
310
310
  context 'containing an order operation' do
311
- let(:operand) { relation.order }
311
+ let(:operand) { relation.sort_by { |r| [ r.id, r.name ] } }
312
312
 
313
313
  it 'pushes the object under the order' do
314
- should eql(relation.rename(aliases).order)
314
+ should eql(relation.rename(aliases).sort_by { |r| [ r.other_id, r.name ] })
315
315
  end
316
316
 
317
317
  it 'returns an equivalent relation to the unoptimized operation' do
@@ -327,11 +327,11 @@ describe Algebra::Rename, '#optimize' do
327
327
  end
328
328
 
329
329
  context 'containing an order operation, containing a object that cancels out' do
330
- let(:operand) { relation.rename(:id => :other_id).order }
331
- let(:aliases) { { :other_id => :id } }
330
+ let(:operand) { relation.rename(:id => :other_id).sort_by { |r| [ r.other_id, r.name ] } }
331
+ let(:aliases) { { :other_id => :id } }
332
332
 
333
333
  it 'pushes the object under the order, and then cancel it out' do
334
- should eql(relation.order)
334
+ should eql(relation.sort_by { |r| [ r.id, r.name ] })
335
335
  end
336
336
 
337
337
  it 'returns an equivalent relation to the unoptimized operation' do
@@ -347,11 +347,11 @@ describe Algebra::Rename, '#optimize' do
347
347
  end
348
348
 
349
349
  context 'containing a limit operation' do
350
- let(:order) { relation.order }
351
- let(:operand) { order.take(2) }
350
+ let(:order) { relation.sort_by { |r| [ r.id, r.name ] } }
351
+ let(:operand) { order.take(2) }
352
352
 
353
353
  it 'pushes the object under the limit and order' do
354
- should eql(relation.rename(aliases).order.take(2))
354
+ should eql(relation.rename(aliases).sort_by { |r| [ r.other_id, r.name ] }.take(2))
355
355
  end
356
356
 
357
357
  it 'returns an equivalent relation to the unoptimized operation' do
@@ -367,12 +367,12 @@ describe Algebra::Rename, '#optimize' do
367
367
  end
368
368
 
369
369
  context 'containing a limit operation, containing a object that cancels out' do
370
- let(:order) { relation.order }
371
- let(:operand) { order.rename(:id => :other_id).take(2) }
372
- let(:aliases) { { :other_id => :id } }
370
+ let(:order) { relation.sort_by { |r| [ r.id, r.name ] } }
371
+ let(:operand) { order.rename(:id => :other_id).take(2) }
372
+ let(:aliases) { { :other_id => :id } }
373
373
 
374
374
  it 'pushes the object under the limit and order, and then cancel it out' do
375
- should eql(relation.order.take(2))
375
+ should eql(relation.sort_by { |r| [ r.id, r.name ] }.take(2))
376
376
  end
377
377
 
378
378
  it 'returns an equivalent relation to the unoptimized operation' do
@@ -388,11 +388,11 @@ describe Algebra::Rename, '#optimize' do
388
388
  end
389
389
 
390
390
  context 'containing an offset operation' do
391
- let(:order) { relation.order }
392
- let(:operand) { order.drop(1) }
391
+ let(:order) { relation.sort_by { |r| [ r.id, r.name ] } }
392
+ let(:operand) { order.drop(1) }
393
393
 
394
394
  it 'pushes the object under the offset and order' do
395
- should eql(relation.rename(aliases).order.drop(1))
395
+ should eql(relation.rename(aliases).sort_by { |r| [ r.other_id, r.name ] }.drop(1))
396
396
  end
397
397
 
398
398
  it 'returns an equivalent relation to the unoptimized operation' do
@@ -408,12 +408,12 @@ describe Algebra::Rename, '#optimize' do
408
408
  end
409
409
 
410
410
  context 'containing an offset operation, containing a object that cancels out' do
411
- let(:order) { relation.order }
412
- let(:operand) { order.rename(:id => :other_id).drop(1) }
413
- let(:aliases) { { :other_id => :id } }
411
+ let(:order) { relation.sort_by { |r| [ r.id, r.name ] } }
412
+ let(:operand) { order.rename(:id => :other_id).drop(1) }
413
+ let(:aliases) { { :other_id => :id } }
414
414
 
415
415
  it 'pushes the object under the offset and order, and then cancel it out' do
416
- should eql(relation.order.drop(1))
416
+ should eql(relation.sort_by { |r| [ r.id, r.name ] }.drop(1))
417
417
  end
418
418
 
419
419
  it 'returns an equivalent relation to the unoptimized operation' do