sparkql 1.1.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- Y2ExNzBkYzg0N2RiZmFkOWRlMGI2NTg0NzJiOGQ5NzNlNTA2OTA5Mw==
4
+ ODFlYzJkZDViM2E4OWI2YmRmZDc0MTIyYTVmZDFlNzdhMzlkYzJkMg==
5
5
  data.tar.gz: !binary |-
6
- YmYyYTBkZGQxMTFkNGZhNDg2MTA1NmZjN2M4YjcyZTZiMWQ0Njk5Ng==
6
+ YmYzYWIyZDJiZTRkZTYyZjg4NzQyYzYxZGI4OTQ1OWQzNDIzN2M4ZQ==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- Mzk4MmY3OTE2MzNiYmEyOTg3NGMzOTdhZTRjY2NhZjhmZjgxMzE1NTJkM2Vj
10
- OWIxZGU0YTBjNTI3M2ZhZjUxNjdlOTY4MTgzMDRmNjhiOWE3MjEwNTU4YWU0
11
- NmQ4MDYwZDRkMDIxYzkyNWI1MjQxMThhYTFiNGFmOTZmNzA1MjI=
9
+ MGRjNTRhZDU5NjQ1NmFkOGUzMjQ4M2JhOTNiY2Q1NTdiOWM2ODQ0NGVmYzY0
10
+ Yjk0OTQyZWEwY2I3ZGUyMTA3YWI4YmJkOTFjOGUzMDMyN2QwNWYzNjg1MTRl
11
+ OTNiOTkyNDY2OTYxYWI0ZDQ2YjUyMjZkYWRkZDgzNDI4NGViYTU=
12
12
  data.tar.gz: !binary |-
13
- ZGEyMzQ1YzU2NDU5YjNhNmVkOTEwNjBlYTI2MWIxMmRiY2E3NWU1MjU3Nzg1
14
- OTgxMzc3MTc3ZGI5Njc1NWZmNGJkY2FkMWNhZDdkNmY4NThkOWUyZmUzMWRj
15
- NDViYmQzNzEzNjBkOThiMmViYjQ4YTRhNjBhYmVlYzE1NWQwMDg=
13
+ YTFjOTA1YjUyMzc2MWQzMTE5YWUzMDY5MjVjNGFhMDg5NzVmNTBjZDNkZDlm
14
+ YWJkMDBjNzYwNmI5MTcwY2QzZGYyNWEyODNiNDAzNTdiNTA3YjQ1NzQ4MTI1
15
+ YjIzYTdmZDQwNjA0OTFiNzhhNWJjODJjMTk1YzZhMjI5N2ZhNDk=
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ v1.1.1, 2016-09-09 ([changes](https://github.com/sparkapi/sparkql/compare/v1.1.0...v1.1.1))
2
+ -------------------
3
+ * [BUGFIX] Fix `Not` handling in the new Evaluation class
4
+
1
5
  v1.1.0, 2016-07-28 ([changes](https://github.com/sparkapi/sparkql/compare/v1.0.3...v1.1.0))
2
6
  -------------------
3
7
  * [IMPROVEMENT] Evaluation class for sparkql boolean algebra processing
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.1.0
1
+ 1.1.1
@@ -1,10 +1,23 @@
1
1
  # Using an instance of ExpressionResolver to resolve the individual expressions,
2
2
  # this class will evaluate the rest of a parsed sparkql string to true or false.
3
- # Namely, this class will handle all the nesting, boolean algebra, and dropped
3
+ # Namely, this class will handle all the nesting, boolean algebra, and dropped
4
4
  # fields. Plus, it has some optimizations built in to skip the processing for
5
5
  # any expressions that don't contribute to the net result of the filter.
6
6
  class Sparkql::Evaluator
7
7
 
8
+ # The struct here mimics some of the parser information about an expression,
9
+ # but should not be confused for an expression. Nodes reduce the expressions
10
+ # to a result based on conjunction logic, and only one exists per block group.
11
+ Node = Struct.new(
12
+ :level,
13
+ :block_group,
14
+ :conjunction,
15
+ :conjunction_level,
16
+ :match,
17
+ :good_ors,
18
+ :expressions,
19
+ :unary)
20
+
8
21
  attr_reader :processed_count
9
22
 
10
23
  def initialize expression_resolver
@@ -13,15 +26,7 @@ class Sparkql::Evaluator
13
26
 
14
27
  def evaluate(expressions)
15
28
  @processed_count = 0
16
- @index = {
17
- level: 0,
18
- block_group: 0,
19
- conjunction: "And",
20
- conjunction_level: 0,
21
- match: true,
22
- good_ors: false,
23
- expressions: 0
24
- }
29
+ @index = Node.new(0, 0, "And", 0, true, false, 0, nil)
25
30
  @groups = [@index]
26
31
  expressions.each do |expression|
27
32
  handle_group(expression)
@@ -66,8 +71,8 @@ class Sparkql::Evaluator
66
71
  end
67
72
  if node[:conjunction] == 'Not' &&
68
73
  (node[:conjunction_level] == node[:level] ||
69
- node[:conjunction_level] == @index[:level])
70
- @index[:match] = !result
74
+ node[:conjunction_level] == @index[:level])
75
+ @index[:match] = !result if @index[:match]
71
76
  elsif node[:conjunction] == 'And' || @index[:expressions] == 0
72
77
  @index[:match] = result if @index[:match]
73
78
  elsif node[:conjunction] == 'Or' && result
@@ -106,18 +111,12 @@ class Sparkql::Evaluator
106
111
  end
107
112
 
108
113
  def new_group(expression)
109
- {
110
- level: expression[:level],
111
- block_group: expression[:block_group],
112
- conjunction: expression[:conjunction],
113
- conjunction_level: expression[:conjunction_level],
114
- match: true,
115
- good_ors: false,
116
- expressions: 0
117
- }
114
+ Node.new(expression[:level], expression[:block_group],
115
+ expression[:conjunction], expression[:conjunction_level],
116
+ true, false, 0, nil)
118
117
  end
119
118
 
120
- # When the last expression was dropped, we need to repair the filter by
119
+ # When the last expression was dropped, we need to repair the filter by
121
120
  # stealing the conjunction of that dropped field.
122
121
  def adjust_expression_for_dropped_field(expression)
123
122
  if @dropped_expression.nil?
@@ -129,8 +128,8 @@ class Sparkql::Evaluator
129
128
  @dropped_expression = nil
130
129
  end
131
130
 
132
- # This is similar to the cleanup step, but happens when we return from a
133
- # nesting level. Before we can proceed, we need wrap up the result of the
131
+ # This is similar to the cleanup step, but happens when we return from a
132
+ # nesting level. Before we can proceed, we need wrap up the result of the
134
133
  # nested group.
135
134
  def smoosh_group(expression)
136
135
  until @groups.last[:block_group] == expression[:block_group]
@@ -1,6 +1,12 @@
1
1
  # Base class for handling expression resolution
2
2
  class Sparkql::ExpressionResolver
3
3
 
4
+ # Accepted results from the resolve method:
5
+ # * true and false reflect the expression's boolean result (as all expressions
6
+ # should).
7
+ # * :drop is a special symbol indicating that the expression should be omitted
8
+ # from the filter. Special rules apply for a dropped expression, such as
9
+ # keeping the conjunction of the dropped expression.
4
10
  VALID_RESULTS = [true, false, :drop]
5
11
 
6
12
  # Evaluate the result of this expression. Allows for any of the values in
@@ -21,6 +21,46 @@ class EvaluatorTest < Test::Unit::TestCase
21
21
  assert !sample("Test Eq false Or Test Eq false")
22
22
  end
23
23
 
24
+ # One passing or expression in the set should always affirm a match this tests
25
+ # every permutation of one passing expression
26
+ def test_ors_stay_good
27
+ 5.times do |i|
28
+ expressions = []
29
+ 5.times do |j|
30
+ expressions << "Test Eq #{i == j}"
31
+ end
32
+ filter = expressions.join(" Or ")
33
+ assert sample(filter), "Filter: #{filter}"
34
+ end
35
+ end
36
+
37
+ # One failing AND expression in a set should always fail. Here we ensure every
38
+ # permutation of one failing
39
+ def test_ands_stay_bad
40
+ 5.times do |i|
41
+ expressions = []
42
+ 5.times do |j|
43
+ expressions << "Test Eq #{i != j}"
44
+ end
45
+ filter = expressions.join(" And ")
46
+ assert !sample(filter), "Filter: #{filter}"
47
+ end
48
+ end
49
+
50
+ # One failing Not expression in a set should always fail. Here we ensure every
51
+ # permutation of one failing
52
+ def test_nots_stay_bad
53
+ 5.times do |i|
54
+ expressions = []
55
+ 5.times do |j|
56
+ expressions << "Test Eq #{i == j}"
57
+ end
58
+ # Add the unary not to the front!
59
+ filter = "Not " + expressions.join(" Not ")
60
+ assert !sample(filter), "Filter: #{filter}"
61
+ end
62
+ end
63
+
24
64
  def test_dropped_field_handling
25
65
  assert sample("Test Eq 'Drop' And Test Eq true")
26
66
  assert !sample("Test Eq 'Drop' And Test Eq false")
@@ -57,6 +97,24 @@ class EvaluatorTest < Test::Unit::TestCase
57
97
  assert !sample("Test Eq true Not (Not Test Eq false)")
58
98
  assert sample("Not (Not Test Eq true)")
59
99
  assert sample("Not (Not(Not Test Eq true))")
100
+ assert !sample("Test Eq false And Test Eq true Not Test Eq false")
101
+ end
102
+
103
+ def test_examples
104
+ # This one is based on a real life example that had problems.
105
+ #
106
+ # CurrentPrice Bt 130000.00,180000.00 And PropertySubType Eq 'Single Family Residence' And
107
+ # SchoolDistrict Eq 'Byron Center','Grandville','Jenison' And MlsStatus Eq 'Active' And
108
+ # BathsTotal Bt 1.50,9999.00 And BedsTotal Bt 3,99 And PropertyType Eq 'A'
109
+ # Not "Garage"."Garage2" Eq 'No' And "Pool"."OutdoorAbove" Eq true
110
+ # And "Pool"."OutdoorInground" Eq true Not "Substructure"."Michigan Basement" Eq true
111
+
112
+ assert !sample("Test Eq false And Test Eq true And " +
113
+ "Test Eq false And Test Eq true And " +
114
+ "Test Eq true And Test Eq true And Test Eq true " +
115
+ "Not Test Eq false And Test Eq false " +
116
+ "And Test Eq false Not Test Eq false"
117
+ )
60
118
  end
61
119
 
62
120
  def test_optimizations
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sparkql
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Wade McEwen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-07-28 00:00:00.000000000 Z
11
+ date: 2016-09-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: georuby