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 +8 -8
- data/CHANGELOG.md +4 -0
- data/VERSION +1 -1
- data/lib/sparkql/evaluator.rb +23 -24
- data/lib/sparkql/expression_resolver.rb +6 -0
- data/test/unit/evaluator_test.rb +58 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
ODFlYzJkZDViM2E4OWI2YmRmZDc0MTIyYTVmZDFlNzdhMzlkYzJkMg==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
YmYzYWIyZDJiZTRkZTYyZjg4NzQyYzYxZGI4OTQ1OWQzNDIzN2M4ZQ==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
MGRjNTRhZDU5NjQ1NmFkOGUzMjQ4M2JhOTNiY2Q1NTdiOWM2ODQ0NGVmYzY0
|
10
|
+
Yjk0OTQyZWEwY2I3ZGUyMTA3YWI4YmJkOTFjOGUzMDMyN2QwNWYzNjg1MTRl
|
11
|
+
OTNiOTkyNDY2OTYxYWI0ZDQ2YjUyMjZkYWRkZDgzNDI4NGViYTU=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
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.
|
1
|
+
1.1.1
|
data/lib/sparkql/evaluator.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
111
|
-
|
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
|
data/test/unit/evaluator_test.rb
CHANGED
@@ -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.
|
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-
|
11
|
+
date: 2016-09-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: georuby
|