sparkql 1.3.0 → 1.3.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 +4 -4
- data/.github/workflows/ci.yml +51 -0
- data/.ruby-version +1 -1
- data/CHANGELOG.md +4 -0
- data/Gemfile +1 -1
- data/VERSION +1 -1
- data/lib/sparkql/evaluator.rb +117 -117
- data/lib/sparkql/parser.rb +499 -522
- data/script/bootstrap +3 -2
- data/sparkql.gemspec +1 -0
- data/test/unit/evaluator_test.rb +12 -5
- metadata +22 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0f8f460c07a1a833cd0a80040f686b12a9579db3bc0ae60d1cc7aa1c73fe644d
|
4
|
+
data.tar.gz: 66a6701ad74dc5454d9e4dd2a14c0ea133715e10ba5babb676c90aed62e917bd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 661e847ae1aa0f336706d94129c0afdc7d0ba040c0c8f62b7a6e9248dcc990d8f0179ef153355e050bae2b8a2f676c7e358081280418986c6c0bbf27393ac572
|
7
|
+
data.tar.gz: 4b64da6af2453236ec0b9c0e240238241609c79e0d4b8cca2c126ecc9b5e3b7e9c585836cc0dceb9a5136cb9decb993bffdef0145720de8dd734b2b2f291f340
|
@@ -0,0 +1,51 @@
|
|
1
|
+
name: CI
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches: [ master ]
|
6
|
+
pull_request:
|
7
|
+
release:
|
8
|
+
types: published
|
9
|
+
|
10
|
+
|
11
|
+
jobs:
|
12
|
+
build:
|
13
|
+
runs-on: ubuntu-latest
|
14
|
+
|
15
|
+
steps:
|
16
|
+
- uses: actions/checkout@v3
|
17
|
+
- uses: ruby/setup-ruby@v1
|
18
|
+
with:
|
19
|
+
ruby-version: '2.7'
|
20
|
+
bundler-cache: true
|
21
|
+
|
22
|
+
- run: |
|
23
|
+
gem install bundler -v 2.2.31
|
24
|
+
./script/ci_build
|
25
|
+
|
26
|
+
publish:
|
27
|
+
runs-on: ubuntu-latest
|
28
|
+
env:
|
29
|
+
RUBYGEMS_API_KEY: ${{secrets.RUBYGEMS_API_KEY}}
|
30
|
+
|
31
|
+
# only run if this is a release event
|
32
|
+
if: github.event_name == 'release'
|
33
|
+
|
34
|
+
# require that the build job passed
|
35
|
+
needs: build
|
36
|
+
|
37
|
+
steps:
|
38
|
+
- uses: actions/checkout@v3
|
39
|
+
- uses: ruby/setup-ruby@v1
|
40
|
+
with:
|
41
|
+
ruby-version: '2.7'
|
42
|
+
|
43
|
+
- run: |
|
44
|
+
./script/bootstrap
|
45
|
+
bundle exec rake build
|
46
|
+
|
47
|
+
mkdir -p $HOME/.gem
|
48
|
+
touch $HOME/.gem/credentials
|
49
|
+
printf -- "---\n:rubygems_api_key: ${RUBYGEMS_API_KEY}\n" > $HOME/.gem/credentials
|
50
|
+
chmod 0600 $HOME/.gem/credentials
|
51
|
+
gem push pkg/*.gem
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.
|
1
|
+
2.7.2
|
data/CHANGELOG.md
CHANGED
data/Gemfile
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.3.
|
1
|
+
1.3.1
|
data/lib/sparkql/evaluator.rb
CHANGED
@@ -4,20 +4,6 @@
|
|
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
|
-
# The struct here mimics some of the parser information about an expression,
|
8
|
-
# but should not be confused for an expression. Nodes reduce the expressions
|
9
|
-
# to a result based on conjunction logic, and only one exists per block group.
|
10
|
-
Node = Struct.new(
|
11
|
-
:level,
|
12
|
-
:block_group,
|
13
|
-
:conjunction,
|
14
|
-
:conjunction_level,
|
15
|
-
:match,
|
16
|
-
:good_ors,
|
17
|
-
:expressions,
|
18
|
-
:unary
|
19
|
-
)
|
20
|
-
|
21
7
|
attr_reader :processed_count
|
22
8
|
|
23
9
|
def initialize(expression_resolver)
|
@@ -25,131 +11,145 @@ class Sparkql::Evaluator
|
|
25
11
|
end
|
26
12
|
|
27
13
|
def evaluate(expressions)
|
28
|
-
@dropped_expression = nil
|
29
14
|
@processed_count = 0
|
30
|
-
|
31
|
-
|
32
|
-
expressions.each do |expression|
|
33
|
-
handle_group(expression)
|
34
|
-
adjust_expression_for_dropped_field(expression)
|
35
|
-
check_for_good_ors(expression)
|
36
|
-
next if skip?(expression)
|
15
|
+
levels = {}
|
16
|
+
block_groups = {}
|
37
17
|
|
38
|
-
|
18
|
+
build_structures(levels, block_groups, expressions)
|
19
|
+
|
20
|
+
final_result = process_structures(levels, block_groups)
|
21
|
+
# If we didn't process anything, we consider that a success
|
22
|
+
if final_result.nil?
|
23
|
+
final_result = true
|
39
24
|
end
|
40
|
-
|
41
|
-
|
25
|
+
|
26
|
+
final_result
|
42
27
|
end
|
43
28
|
|
44
29
|
private
|
45
30
|
|
46
|
-
#
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
end
|
31
|
+
# Take all the expressions and organize them into "chunks" appropriate for
|
32
|
+
# evaluation. Each block group should process it's expressions, and every
|
33
|
+
# block group injects itself as a placeholder expression in the block group a
|
34
|
+
# level above it.
|
35
|
+
#
|
36
|
+
# When no block groups exist above, we must stub one out for processing.
|
37
|
+
def build_structures(levels, block_groups, expressions)
|
38
|
+
expressions.each do |expression|
|
39
|
+
level = expression[:level]
|
40
|
+
block = expression[:block_group]
|
41
|
+
block_group = block_groups[block]
|
58
42
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
evaluate_node(expression, @resolver.resolve(expression))
|
65
|
-
end
|
43
|
+
unless block_group
|
44
|
+
block_groups[block] ||= block_builder(expression, level)
|
45
|
+
block_group = block_groups[block]
|
46
|
+
levels[level] ||= []
|
47
|
+
levels[level] << block
|
66
48
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
49
|
+
# When dealing with Not expression conjunctions at the block level,
|
50
|
+
# it's far simpler to convert it into the equivalent "And Not"
|
51
|
+
if block_group[:conjunction] == "Not"
|
52
|
+
block_group[:unary] = "Not"
|
53
|
+
block_group[:conjunction] = "And"
|
54
|
+
end
|
55
|
+
|
56
|
+
# Every block group _must_ be seen as an expression in another block
|
57
|
+
# group.This aids in final resolution order when processing the levels
|
58
|
+
#
|
59
|
+
# This is even true if there's only one block group. We always end up
|
60
|
+
# with a level -1 here to turn the top level expressions into a block
|
61
|
+
# group for processing.
|
62
|
+
current_level = level
|
63
|
+
while current_level >= 0
|
64
|
+
current_level -= 1
|
65
|
+
levels[current_level] ||= []
|
66
|
+
last_block_group_id = levels[current_level].last
|
67
|
+
if last_block_group_id
|
68
|
+
block_groups[last_block_group_id][:expressions] << block_group
|
69
|
+
break
|
70
|
+
else
|
71
|
+
block_id = "placeholder_for_#{block}_#{current_level}"
|
72
|
+
placeholder_block = block_builder(block_group, current_level)
|
73
|
+
placeholder_block[:expressions] << block_group
|
87
74
|
|
88
|
-
|
89
|
-
|
90
|
-
# level.
|
91
|
-
def check_for_good_ors(expression)
|
92
|
-
if expression[:conjunction] == 'Or'
|
93
|
-
good_index = @index
|
94
|
-
unless expression[:conjunction_level] == @index[:level]
|
95
|
-
good_index = nil
|
96
|
-
# Well crap, now we need to go back and find that level by hand
|
97
|
-
@groups.reverse_each do |i|
|
98
|
-
if i[:level] == expression[:conjunction_level]
|
99
|
-
good_index = i
|
75
|
+
levels[current_level] << block_id
|
76
|
+
block_groups[block_id] = placeholder_block
|
100
77
|
end
|
101
78
|
end
|
102
79
|
end
|
103
|
-
|
104
|
-
|
105
|
-
end
|
80
|
+
|
81
|
+
block_group[:expressions] << expression
|
106
82
|
end
|
107
83
|
end
|
108
84
|
|
109
|
-
#
|
110
|
-
#
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
end
|
85
|
+
# Starting from the deepest levels, we process block groups expressions and
|
86
|
+
# reduce the block group to a result. This result is used in our placeholder
|
87
|
+
# block groups at levels above, ending in a single final result.
|
88
|
+
def process_structures(levels, block_groups)
|
89
|
+
final_result = nil
|
115
90
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
91
|
+
# Now go through each level starting with the deepest and working back up.
|
92
|
+
levels.keys.sort.reverse.each do |level|
|
93
|
+
# Process each block group at this level and resolve the expressions in the group
|
94
|
+
levels[level].each do |block|
|
95
|
+
block_group = block_groups[block]
|
121
96
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
expression[:conjunction_level] = @dropped_expression[:conjunction_level]
|
130
|
-
end
|
97
|
+
block_result = nil
|
98
|
+
block_group[:expressions].each do |expression|
|
99
|
+
# If we encounter any or's in the same block group, we can cheat at
|
100
|
+
# resolving the rest, if we are at a true
|
101
|
+
if block_result && expression[:conjunction] == 'Or'
|
102
|
+
break
|
103
|
+
end
|
131
104
|
|
132
|
-
|
133
|
-
|
105
|
+
expression_result = if expression.key?(:result)
|
106
|
+
# This is a reduced block group, just pass on
|
107
|
+
# the result
|
108
|
+
expression[:result]
|
109
|
+
else
|
110
|
+
@processed_count += 1
|
111
|
+
@resolver.resolve(expression) # true, false, :drop
|
112
|
+
end
|
113
|
+
next if expression_result == :drop
|
114
|
+
|
115
|
+
if expression[:unary] == "Not"
|
116
|
+
expression_result = !expression_result
|
117
|
+
end
|
134
118
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
119
|
+
if block_result.nil?
|
120
|
+
block_result = expression_result
|
121
|
+
next
|
122
|
+
end
|
123
|
+
|
124
|
+
case expression[:conjunction]
|
125
|
+
when 'Not'
|
126
|
+
block_result &= !expression_result
|
127
|
+
when 'And'
|
128
|
+
block_result &= expression_result
|
129
|
+
when 'Or'
|
130
|
+
block_result |= expression_result
|
131
|
+
else
|
132
|
+
# Not a supported conjunction. We skip over this for backwards
|
133
|
+
# compatibility.
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# block_group.delete(:expressions)
|
138
|
+
block_group[:result] = block_result
|
139
|
+
final_result = block_result
|
140
|
+
end
|
143
141
|
end
|
142
|
+
|
143
|
+
final_result
|
144
144
|
end
|
145
145
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
146
|
+
def block_builder(expressionable, level)
|
147
|
+
{
|
148
|
+
conjunction: expressionable[:conjunction],
|
149
|
+
conjunction_level: expressionable[:conjunction_level],
|
150
|
+
level: level,
|
151
|
+
expressions: [],
|
152
|
+
result: nil
|
153
|
+
}
|
154
154
|
end
|
155
155
|
end
|