sparkql 1.3.1 → 1.3.3
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 +4 -4
- data/CHANGELOG.md +10 -0
- data/VERSION +1 -1
- data/lib/sparkql/evaluator.rb +22 -12
- data/lib/sparkql/token.rb +10 -3
- data/test/unit/evaluator_test.rb +67 -12
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1fb6060ec97e59958c12e0ed3338540afa180bf28eab0224af3ffef568dbf469
|
4
|
+
data.tar.gz: 982d3956830686faeb210e6abf52125d18d4d68fa0d231b00a251a4876e1bd14
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6754d10ca449bbd3467127a8470bb4233dd00b5162984ca636bcd93aff41743b25ace78fdd30ba77cbb6a79bef55d36118f67f2102b676ca9184db04c2437c01
|
7
|
+
data.tar.gz: d337d7169ceb202c6f4d8a066196c472404cccf5e1b6f0fc13bf07a4fba62fa6d135c04bfc017544c73fb84ca1834f5d06c78bbf5ee2848ac16b06372b09b711
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
|
2
|
+
v1.3.3, 2025-08-12
|
3
|
+
-------------------
|
4
|
+
* [BUGFIX] Evaluator fix for Not regression
|
5
|
+
|
6
|
+
v1.3.2, 2025-08-06
|
7
|
+
-------------------
|
8
|
+
* [BUGFIX] More Evaluator fixes
|
9
|
+
* [BUGFIX] fixed the build.
|
10
|
+
|
1
11
|
v1.3.1, 2025-08-06
|
2
12
|
-------------------
|
3
13
|
* [BUGFIX] Evaluator fix for Not expressions
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.3.
|
1
|
+
1.3.3
|
data/lib/sparkql/evaluator.rb
CHANGED
@@ -1,9 +1,12 @@
|
|
1
|
+
|
1
2
|
# Using an instance of ExpressionResolver to resolve the individual expressions,
|
2
3
|
# this class will evaluate the rest of a parsed sparkql string to true or false.
|
3
4
|
# Namely, this class will handle all the nesting, boolean algebra, and dropped
|
4
5
|
# fields. Plus, it has some optimizations built in to skip the processing for
|
5
6
|
# any expressions that don't contribute to the net result of the filter.
|
6
7
|
class Sparkql::Evaluator
|
8
|
+
include Sparkql::Token
|
9
|
+
|
7
10
|
attr_reader :processed_count
|
8
11
|
|
9
12
|
def initialize(expression_resolver)
|
@@ -40,6 +43,11 @@ class Sparkql::Evaluator
|
|
40
43
|
block = expression[:block_group]
|
41
44
|
block_group = block_groups[block]
|
42
45
|
|
46
|
+
if expression[:conjunction] == NOT && expression[:conjunction_level] == level
|
47
|
+
expression[:conjunction] = AND
|
48
|
+
expression[:unary] = NOT
|
49
|
+
end
|
50
|
+
|
43
51
|
unless block_group
|
44
52
|
block_groups[block] ||= block_builder(expression, level)
|
45
53
|
block_group = block_groups[block]
|
@@ -48,9 +56,9 @@ class Sparkql::Evaluator
|
|
48
56
|
|
49
57
|
# When dealing with Not expression conjunctions at the block level,
|
50
58
|
# it's far simpler to convert it into the equivalent "And Not"
|
51
|
-
if block_group[:conjunction] ==
|
52
|
-
block_group[:unary] =
|
53
|
-
block_group[:conjunction] =
|
59
|
+
if block_group[:conjunction] == NOT
|
60
|
+
block_group[:unary] = NOT
|
61
|
+
block_group[:conjunction] = AND
|
54
62
|
end
|
55
63
|
|
56
64
|
# Every block group _must_ be seen as an expression in another block
|
@@ -60,20 +68,22 @@ class Sparkql::Evaluator
|
|
60
68
|
# with a level -1 here to turn the top level expressions into a block
|
61
69
|
# group for processing.
|
62
70
|
current_level = level
|
71
|
+
last_block_group = block_group
|
63
72
|
while current_level >= 0
|
64
73
|
current_level -= 1
|
65
74
|
levels[current_level] ||= []
|
66
75
|
last_block_group_id = levels[current_level].last
|
67
76
|
if last_block_group_id
|
68
|
-
block_groups[last_block_group_id][:expressions] <<
|
77
|
+
block_groups[last_block_group_id][:expressions] << last_block_group
|
69
78
|
break
|
70
79
|
else
|
71
80
|
block_id = "placeholder_for_#{block}_#{current_level}"
|
72
|
-
placeholder_block = block_builder(
|
73
|
-
placeholder_block[:expressions] <<
|
81
|
+
placeholder_block = block_builder(last_block_group, current_level)
|
82
|
+
placeholder_block[:expressions] << last_block_group
|
74
83
|
|
75
84
|
levels[current_level] << block_id
|
76
85
|
block_groups[block_id] = placeholder_block
|
86
|
+
last_block_group = placeholder_block
|
77
87
|
end
|
78
88
|
end
|
79
89
|
end
|
@@ -98,7 +108,7 @@ class Sparkql::Evaluator
|
|
98
108
|
block_group[:expressions].each do |expression|
|
99
109
|
# If we encounter any or's in the same block group, we can cheat at
|
100
110
|
# resolving the rest, if we are at a true
|
101
|
-
if block_result && expression[:conjunction] ==
|
111
|
+
if block_result && expression[:conjunction] == OR
|
102
112
|
break
|
103
113
|
end
|
104
114
|
|
@@ -112,7 +122,7 @@ class Sparkql::Evaluator
|
|
112
122
|
end
|
113
123
|
next if expression_result == :drop
|
114
124
|
|
115
|
-
if expression[:unary] ==
|
125
|
+
if expression[:unary] == NOT
|
116
126
|
expression_result = !expression_result
|
117
127
|
end
|
118
128
|
|
@@ -122,11 +132,11 @@ class Sparkql::Evaluator
|
|
122
132
|
end
|
123
133
|
|
124
134
|
case expression[:conjunction]
|
125
|
-
when
|
135
|
+
when NOT
|
126
136
|
block_result &= !expression_result
|
127
|
-
when
|
137
|
+
when AND
|
128
138
|
block_result &= expression_result
|
129
|
-
when
|
139
|
+
when OR
|
130
140
|
block_result |= expression_result
|
131
141
|
else
|
132
142
|
# Not a supported conjunction. We skip over this for backwards
|
@@ -134,7 +144,7 @@ class Sparkql::Evaluator
|
|
134
144
|
end
|
135
145
|
end
|
136
146
|
|
137
|
-
|
147
|
+
block_group.delete(:expressions)
|
138
148
|
block_group[:result] = block_result
|
139
149
|
final_result = block_result
|
140
150
|
end
|
data/lib/sparkql/token.rb
CHANGED
@@ -24,8 +24,15 @@ module Sparkql::Token
|
|
24
24
|
NULL = /NULL|null|Null/.freeze
|
25
25
|
# Reserved words
|
26
26
|
RANGE_OPERATOR = 'Bt'.freeze
|
27
|
-
|
27
|
+
EQUAL = 'Eq'.freeze
|
28
|
+
NOT_EQUAL = 'Ne'.freeze
|
29
|
+
EQUALITY_OPERATORS = [EQUAL, NOT_EQUAL].freeze
|
30
|
+
|
28
31
|
OPERATORS = %w[Gt Ge Lt Le] + EQUALITY_OPERATORS
|
29
|
-
|
30
|
-
|
32
|
+
|
33
|
+
NOT = 'Not'.freeze
|
34
|
+
AND = 'And'.freeze
|
35
|
+
OR = 'Or'.freeze
|
36
|
+
UNARY_CONJUNCTIONS = [NOT].freeze
|
37
|
+
CONJUNCTIONS = [AND, OR].freeze
|
31
38
|
end
|
data/test/unit/evaluator_test.rb
CHANGED
@@ -64,35 +64,93 @@ class EvaluatorTest < Test::Unit::TestCase
|
|
64
64
|
def test_dropped_field_handling
|
65
65
|
assert sample("Test Eq 'Drop' And Test Eq true")
|
66
66
|
assert !sample("Test Eq 'Drop' And Test Eq false")
|
67
|
-
|
67
|
+
|
68
68
|
assert sample("Test Eq 'Drop' Or Test Eq true")
|
69
|
+
assert !sample("Test Eq 'Drop' Or Test Eq false")
|
70
|
+
|
69
71
|
assert sample("Test Eq false And Test Eq 'Drop' Or Test Eq true")
|
72
|
+
assert !sample("Test Eq false And Test Eq 'Drop' Or Test Eq false")
|
73
|
+
|
70
74
|
assert sample("Test Eq false Or (Test Eq 'Drop' And Test Eq true)")
|
75
|
+
assert !sample("Test Eq false Or (Test Eq 'Drop' And Test Eq false)")
|
76
|
+
|
77
|
+
assert sample("Test Eq false Or (Not Test Eq 'Drop' And Test Eq true)")
|
78
|
+
assert !sample("Test Eq false Or (Not Test Eq 'Drop' And Test Eq false)")
|
79
|
+
|
80
|
+
assert sample("Test Eq true Not Test Eq 'Drop' And Test Eq true")
|
81
|
+
assert !sample("Test Eq true Not Test Eq 'Drop' And Test Eq false")
|
82
|
+
assert !sample("Test Eq false Not Test Eq 'Drop' And Test Eq false")
|
83
|
+
|
84
|
+
assert sample("Test Eq true And Test Eq 'Drop' Not Test Eq false")
|
85
|
+
assert !sample("Test Eq true And Test Eq 'Drop' Not Test Eq true")
|
86
|
+
assert !sample("Test Eq true And Test Eq 'Drop' Not Test Eq true")
|
87
|
+
|
88
|
+
assert sample("Test Eq true Not (Test Eq 'Drop' And Test Eq false)")
|
89
|
+
assert !sample("Test Eq true Not (Test Eq 'Drop' And Test Eq true)")
|
90
|
+
assert !sample("Test Eq true Not (Test Eq 'Drop' And Test Eq true)")
|
71
91
|
end
|
72
92
|
|
73
93
|
def test_nesting
|
74
94
|
assert sample("Test Eq true Or (Test Eq true) And Test Eq false And (Test Eq true)")
|
95
|
+
assert sample("Test Eq true Or (Test Eq false) And Test Eq false And (Test Eq false)")
|
96
|
+
assert sample("Test Eq false Or (Test Eq true) And Test Eq true And (Test Eq true)")
|
97
|
+
assert !sample("Test Eq false Or (Test Eq false) And Test Eq false And (Test Eq false)")
|
98
|
+
assert !sample("Test Eq false Or (Test Eq true) And Test Eq false And (Test Eq false)")
|
99
|
+
assert !sample("Test Eq false Or (Test Eq false) And Test Eq true And (Test Eq false)")
|
100
|
+
assert !sample("Test Eq false Or (Test Eq false) And Test Eq false And (Test Eq true)")
|
101
|
+
|
75
102
|
assert sample("Test Eq true Or ((Test Eq false) And Test Eq false) And (Test Eq false)")
|
76
103
|
assert sample("(Test Eq false Or Test Eq true) Or (Test Eq false Or Test Eq false)")
|
77
104
|
assert sample("(Test Eq true And Test Eq true) Or (Test Eq false)")
|
78
105
|
assert sample("(Test Eq true And Test Eq true) Or (Test Eq false And Test Eq true)")
|
79
106
|
assert !sample("(Test Eq false And Test Eq true) Or (Test Eq false)")
|
107
|
+
|
80
108
|
assert sample("Test Eq true And ((Test Eq true And Test Eq false) Or Test Eq true)")
|
81
109
|
assert !sample("Test Eq true And ((Test Eq true And Test Eq false) Or Test Eq false) And Test Eq true")
|
82
110
|
assert !sample("Test Eq true And ((Test Eq true And Test Eq false) Or Test Eq false) Or Test Eq false")
|
83
111
|
assert sample("Test Eq true And ((Test Eq true And Test Eq false) Or Test Eq false) Or Test Eq true")
|
112
|
+
assert !sample("(Test Eq true Or Test Eq true) And Test Eq false")
|
113
|
+
assert !sample("(Test Eq true Or Test Eq true) And (Test Eq false)")
|
114
|
+
|
115
|
+
assert sample("(Test Eq true Or Test Eq true) And (Test Eq false Or Test Eq true)")
|
116
|
+
assert !sample("(Test Eq true Or Test Eq true) And (Test Eq false Or Test Eq false)")
|
117
|
+
|
118
|
+
assert sample("(Test Eq true) Not Test Eq false And (Test Eq true)")
|
119
|
+
assert !sample("(Test Eq true) Not Test Eq true And (Test Eq true)")
|
120
|
+
assert !sample("(Test Eq false) Not Test Eq false And (Test Eq true)")
|
121
|
+
assert !sample("(Test1 Eq true) Not Test2 Eq false And (Test3 Eq false)")
|
84
122
|
end
|
85
123
|
|
86
124
|
def test_nots
|
87
125
|
assert sample("Test Eq true Not Test Eq false")
|
88
126
|
assert !sample("Test Eq true Not Test Eq true")
|
127
|
+
assert !sample("Test Eq false Not Test Eq true")
|
128
|
+
assert !sample("Test Eq false Not Test Eq false")
|
129
|
+
|
130
|
+
assert sample("Test Eq true And Test Eq true Not Test Eq false")
|
131
|
+
assert !sample("Test Eq false And Test Eq true Not Test Eq false")
|
132
|
+
assert !sample("Test Eq true And Test Eq true Not Test Eq true")
|
133
|
+
assert !sample("Test Eq true And Test Eq false Not Test Eq false")
|
134
|
+
|
89
135
|
assert sample("Test Eq true Not (Test Eq false Or Test Eq false)")
|
90
|
-
assert sample("Test Eq true Not (Test Eq false And Test Eq false)")
|
91
136
|
assert !sample("Test Eq true Not (Test Eq false Or Test Eq true)")
|
92
137
|
assert !sample("Test Eq true Not (Test Eq true Or Test Eq false)")
|
93
|
-
assert !sample("Test Eq true Not (
|
94
|
-
assert !sample("Test Eq false
|
138
|
+
assert !sample("Test Eq true Not (Test Eq true Or Test Eq true)")
|
139
|
+
assert !sample("Test Eq false Not (Test Eq false Or Test Eq false)")
|
140
|
+
|
141
|
+
assert sample("Test Eq true Not (Test Eq false And Test Eq false)")
|
142
|
+
assert sample("Test Eq true Not (Test Eq true And Test Eq false)")
|
143
|
+
assert sample("Test Eq true Not (Test Eq false And Test Eq true)")
|
144
|
+
assert !sample("Test Eq true Not (Test Eq true And Test Eq true)")
|
145
|
+
assert !sample("Test Eq false Not (Test Eq false And Test Eq false)")
|
146
|
+
|
147
|
+
assert sample("Test Eq true Not (Test Eq false Or Test Eq false) And (Test Eq true Or Test Eq false)")
|
148
|
+
assert sample("Test Eq true Not (Test Eq false Or Test Eq false) And (Test Eq false Or Test Eq true)")
|
149
|
+
assert sample("Test Eq true Not (Test Eq false Or Test Eq false) And (Test Eq true Or Test Eq true)")
|
95
150
|
assert !sample("Test Eq true Not (Test Eq false Or Test Eq false) And (Test Eq false Or Test Eq false)")
|
151
|
+
assert !sample("Test Eq true Not (Test Eq false Or Test Eq true) And (Test Eq true Or Test Eq false)")
|
152
|
+
assert !sample("Test Eq true Not (Test Eq true Or Test Eq false) And (Test Eq true Or Test Eq false)")
|
153
|
+
assert !sample("Test Eq false Not (Test Eq false Or Test Eq false) And (Test Eq true Or Test Eq false)")
|
96
154
|
end
|
97
155
|
|
98
156
|
def test_unary_nots
|
@@ -105,17 +163,14 @@ class EvaluatorTest < Test::Unit::TestCase
|
|
105
163
|
|
106
164
|
def test_unary_double_nots
|
107
165
|
assert sample("Not (Not(Not Test Eq true))")
|
166
|
+
assert !sample("Not (Not(Not Test Eq false))")
|
167
|
+
|
168
|
+
assert sample("Test Eq true Not (Not Test Eq true)")
|
169
|
+
assert !sample("Test Eq true Not (Not Test Eq false)")
|
170
|
+
assert !sample("Test Eq false Not (Not Test Eq true)")
|
108
171
|
end
|
109
172
|
|
110
173
|
def test_examples
|
111
|
-
# This one is based on a real life example that had problems.
|
112
|
-
#
|
113
|
-
# CurrentPrice Bt 130000.00,180000.00 And PropertySubType Eq 'Single Family Residence' And
|
114
|
-
# SchoolDistrict Eq 'Byron Center','Grandville','Jenison' And MlsStatus Eq 'Active' And
|
115
|
-
# BathsTotal Bt 1.50,9999.00 And BedsTotal Bt 3,99 And PropertyType Eq 'A'
|
116
|
-
# Not "Garage"."Garage2" Eq 'No' And "Pool"."OutdoorAbove" Eq true
|
117
|
-
# And "Pool"."OutdoorInground" Eq true Not "Substructure"."Michigan Basement" Eq true
|
118
|
-
|
119
174
|
assert !sample("Test Eq false And Test Eq true And " \
|
120
175
|
"Test Eq false And Test Eq true And " \
|
121
176
|
"Test Eq true And Test Eq true And Test Eq true " \
|
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.3.
|
4
|
+
version: 1.3.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Wade McEwen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-08-
|
11
|
+
date: 2025-08-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: georuby
|