norikra 0.0.5-java → 0.0.6-java
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.
- data/lib/norikra/engine.rb +48 -37
- data/lib/norikra/query.rb +140 -108
- data/lib/norikra/query/ast.rb +237 -0
- data/lib/norikra/typedef_manager.rb +27 -0
- data/lib/norikra/version.rb +1 -1
- data/spec/query_spec.rb +100 -17
- data/spec/typedef_manager_spec.rb +57 -1
- metadata +3 -2
data/lib/norikra/engine.rb
CHANGED
@@ -29,7 +29,7 @@ module Norikra
|
|
29
29
|
@targets = []
|
30
30
|
@queries = []
|
31
31
|
|
32
|
-
@waiting_queries =
|
32
|
+
@waiting_queries = []
|
33
33
|
end
|
34
34
|
|
35
35
|
def start
|
@@ -58,8 +58,8 @@ module Norikra
|
|
58
58
|
|
59
59
|
def register(query)
|
60
60
|
info "registering query", :name => query.name, :targets => query.targets, :expression => query.expression
|
61
|
-
|
62
|
-
open(
|
61
|
+
query.targets.each do |target|
|
62
|
+
open(target) unless @targets.include?(target)
|
63
63
|
end
|
64
64
|
register_query(query)
|
65
65
|
end
|
@@ -120,8 +120,7 @@ module Norikra
|
|
120
120
|
end
|
121
121
|
|
122
122
|
def update(new_events, old_events)
|
123
|
-
|
124
|
-
#p "updated event query:#{@query_name}, event:#{new_events.inspect}"
|
123
|
+
trace "updated event", :query => @query_name, :event => new_events
|
125
124
|
@output_pool.push(@query_name, new_events)
|
126
125
|
end
|
127
126
|
end
|
@@ -161,25 +160,27 @@ module Norikra
|
|
161
160
|
return false unless @typedef_manager.lazy?(target)
|
162
161
|
|
163
162
|
@typedef_manager.activate(target, fieldset)
|
164
|
-
# @typedef_manager.bind_fieldset(target, :base, fieldset)
|
165
163
|
register_fieldset_actually(target, fieldset, :base)
|
166
164
|
end
|
167
165
|
nil
|
168
166
|
end
|
169
167
|
|
170
168
|
def register_query(query)
|
171
|
-
#TODO: support JOINs
|
172
|
-
target = query.targets.first
|
173
169
|
@mutex.synchronize do
|
174
|
-
|
175
|
-
@waiting_queries
|
176
|
-
@
|
177
|
-
|
178
|
-
|
170
|
+
unless @typedef_manager.ready?(query)
|
171
|
+
@waiting_queries.push(query)
|
172
|
+
@queries.push(query)
|
173
|
+
return
|
174
|
+
end
|
175
|
+
|
176
|
+
mapping = @typedef_manager.generate_fieldset_mapping(query)
|
177
|
+
mapping.each do |target, query_fieldset|
|
179
178
|
@typedef_manager.bind_fieldset(target, :query, query_fieldset)
|
180
179
|
register_fieldset_actually(target, query_fieldset, :query)
|
181
|
-
|
180
|
+
end
|
181
|
+
register_query_actually(query, mapping)
|
182
182
|
|
183
|
+
mapping.each do |target, query_fieldset|
|
183
184
|
# replace registered data fieldsets with new fieldset inherits this query fieldset
|
184
185
|
@typedef_manager.supersets(target, query_fieldset).each do |set|
|
185
186
|
rebound = set.rebind(true) # update event_type_name with new inheritations
|
@@ -189,33 +190,38 @@ module Norikra
|
|
189
190
|
remove_fieldset_actually(target, set, :data)
|
190
191
|
end
|
191
192
|
end
|
193
|
+
|
192
194
|
@queries.push(query)
|
193
195
|
end
|
194
196
|
end
|
195
197
|
|
196
198
|
def register_waiting_queries(target)
|
197
|
-
|
198
|
-
|
199
|
+
ready = []
|
200
|
+
not_ready = []
|
201
|
+
@waiting_queries.each do |q|
|
202
|
+
if @typedef_manager.ready?(q)
|
203
|
+
ready.push(q)
|
204
|
+
else
|
205
|
+
not_ready.push(q)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
@waiting_queries = not_ready
|
199
209
|
|
200
|
-
|
201
|
-
|
202
|
-
|
210
|
+
ready.each do |query|
|
211
|
+
mapping = @typedef_manager.generate_fieldset_mapping(query)
|
212
|
+
mapping.each do |target, query_fieldset|
|
203
213
|
@typedef_manager.bind_fieldset(target, :query, query_fieldset)
|
204
214
|
register_fieldset_actually(target, query_fieldset, :query)
|
205
|
-
register_query_actually(target, query_fieldset.event_type_name, query)
|
206
|
-
else
|
207
|
-
not_registered.push(query)
|
208
215
|
end
|
216
|
+
register_query_actually(query, mapping)
|
209
217
|
end
|
210
|
-
|
211
|
-
@waiting_queries[target] = not_registered if not_registered.size > 0
|
212
218
|
end
|
213
219
|
|
214
220
|
def register_fieldset(target, fieldset)
|
215
221
|
@mutex.synchronize do
|
216
222
|
@typedef_manager.bind_fieldset(target, :data, fieldset)
|
217
223
|
|
218
|
-
if @waiting_queries
|
224
|
+
if @waiting_queries.size > 0
|
219
225
|
register_waiting_queries(target)
|
220
226
|
end
|
221
227
|
|
@@ -224,12 +230,20 @@ module Norikra
|
|
224
230
|
end
|
225
231
|
|
226
232
|
# this method should be protected with @mutex lock
|
227
|
-
def register_query_actually(
|
228
|
-
|
229
|
-
|
233
|
+
def register_query_actually(query, mapping)
|
234
|
+
# 'mapping' argument is {target => fieldset}
|
235
|
+
event_type_name_map = {}
|
236
|
+
mapping.keys.each do |key|
|
237
|
+
event_type_name_map[key] = mapping[key].event_type_name
|
238
|
+
end
|
239
|
+
|
240
|
+
administrator = @service.getEPAdministrator
|
241
|
+
|
242
|
+
statement_model = administrator.compileEPL(query.expression)
|
243
|
+
Norikra::Query.rewrite_event_type_name(statement_model, event_type_name_map)
|
244
|
+
|
245
|
+
epl = administrator.create(statement_model)
|
230
246
|
epl.java_send :addListener, [com.espertech.esper.client.UpdateListener.java_class], Listener.new(query.name, @output_pool)
|
231
|
-
#TODO: debug log
|
232
|
-
#p "addListener target:#{target}, query_name:#{query.name}, query:#{query.expression}"
|
233
247
|
end
|
234
248
|
|
235
249
|
# this method should be protected with @mutex lock
|
@@ -242,19 +256,16 @@ module Norikra
|
|
242
256
|
# .addEventType("AccountUpdate", accountUpdateDef, new String[] {"BaseUpdate"});
|
243
257
|
case level
|
244
258
|
when :base
|
259
|
+
debug "add event type", :target => target, :level => 'base', :event_type => fieldset.event_type_name
|
245
260
|
@config.addEventType(fieldset.event_type_name, fieldset.definition)
|
246
|
-
#TODO: debug log
|
247
|
-
#p "addEventType target:#{target}, level:base, eventType:#{fieldset.event_type_name}"
|
248
261
|
when :query
|
249
262
|
base_name = @typedef_manager.base_fieldset(target).event_type_name
|
263
|
+
debug "add event type", :target => target, :level => 'query', :event_type => fieldset.event_type_name, :base => base_name
|
250
264
|
@config.addEventType(fieldset.event_type_name, fieldset.definition, [base_name].to_java(:string))
|
251
|
-
#TODO: debug log
|
252
|
-
#p "addEventType target:#{target}, level:query, eventType:#{fieldset.event_type_name}, base:#{base_name}"
|
253
265
|
else
|
254
266
|
subset_names = @typedef_manager.subsets(target, fieldset).map(&:event_type_name)
|
267
|
+
debug "add event type", :target => target, :level => 'data', :event_type => fieldset.event_type_name, :inherit => subset_names
|
255
268
|
@config.addEventType(fieldset.event_type_name, fieldset.definition, subset_names.to_java(:string))
|
256
|
-
#TODO: debug log
|
257
|
-
#p "addEventType target:#{target}, level:data, eventType:#{fieldset.event_type_name}, inherit:#{subset_names.join(',')}"
|
258
269
|
|
259
270
|
@registered_fieldsets[target][level][fieldset.summary] = fieldset
|
260
271
|
end
|
@@ -267,7 +278,7 @@ module Norikra
|
|
267
278
|
|
268
279
|
# DON'T check @registered_fieldsets[target][level][fieldset.summary]
|
269
280
|
# removed fieldset should be already replaced with register_fieldset_actually w/ replace flag
|
270
|
-
|
281
|
+
debug "remove event type", :target => target, :event_type => fieldset.event_type_name
|
271
282
|
@config.removeEventType(fieldset.event_type_name, true)
|
272
283
|
end
|
273
284
|
end
|
data/lib/norikra/query.rb
CHANGED
@@ -4,6 +4,8 @@ require 'esper/lib/commons-logging-1.1.1.jar'
|
|
4
4
|
require 'esper/lib/antlr-runtime-3.2.jar'
|
5
5
|
require 'esper/lib/cglib-nodep-2.2.jar'
|
6
6
|
|
7
|
+
require 'norikra/query/ast'
|
8
|
+
|
7
9
|
module Norikra
|
8
10
|
class Query
|
9
11
|
attr_accessor :name, :expression
|
@@ -13,6 +15,7 @@ module Norikra
|
|
13
15
|
@expression = param[:expression]
|
14
16
|
@ast = nil
|
15
17
|
@targets = nil
|
18
|
+
@subqueries = nil
|
16
19
|
@fields = nil
|
17
20
|
end
|
18
21
|
|
@@ -36,139 +39,168 @@ module Norikra
|
|
36
39
|
|
37
40
|
def targets
|
38
41
|
return @targets if @targets
|
39
|
-
|
40
|
-
@targets = self.ast.find('STREAM_EXPR').find('EVENT_FILTER_EXPR').children.map(&:name)
|
42
|
+
@targets = (self.ast.listup(:stream).map(&:target) + self.subqueries.map(&:targets).flatten).sort.uniq
|
41
43
|
@targets
|
42
44
|
end
|
43
45
|
|
44
|
-
def
|
45
|
-
return @
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
# Norikra::Query.new(
|
50
|
-
# :name => 'hoge',
|
51
|
-
# :expression => 'select count(*) AS cnt from www.win:time_batch(10 seconds) where path="/" AND search.length() > 0').ast.to_a
|
52
|
-
# ["EPL_EXPR",
|
53
|
-
# ["SELECTION_EXPR", ["SELECTION_ELEMENT_EXPR", ["count"], ["cnt"]]],
|
54
|
-
# ["STREAM_EXPR",
|
55
|
-
# ["EVENT_FILTER_EXPR", ["www"]],
|
56
|
-
# ["VIEW_EXPR",
|
57
|
-
# ["win"],
|
58
|
-
# ["time_batch"],
|
59
|
-
# ["TIME_PERIOD", ["SECOND_PART", ["10"]]]]],
|
60
|
-
# ["WHERE_EXPR",
|
61
|
-
# ["EVAL_AND_EXPR",
|
62
|
-
# ["EVAL_EQUALS_EXPR",
|
63
|
-
# ["EVENT_PROP_EXPR", ["EVENT_PROP_SIMPLE", ["path"]]],
|
64
|
-
# ["\"/\""]],
|
65
|
-
# [">",
|
66
|
-
# ["LIB_FUNC_CHAIN", ["LIB_FUNCTION", ["search"], ["length"], ["("]]],
|
67
|
-
# ["0"]]]]]
|
68
|
-
|
69
|
-
ast = self.ast
|
70
|
-
names_simple = ast.listup('EVENT_PROP_SIMPLE').map{|p| p.child.name}
|
71
|
-
names_chain_root = ast.listup('LIB_FUNC_CHAIN').map{|c| c.child.child.name}.select{|n| not self.class.imported_java_class?(n)}
|
72
|
-
@fields = (names_simple + names_chain_root).uniq.sort
|
73
|
-
@fields
|
46
|
+
def subqueries
|
47
|
+
return @subqueries if @subqueries
|
48
|
+
@subqueries = self.ast.listup(:subquery).map{|n| Norikra::SubQuery.new(n)}
|
49
|
+
@subqueries
|
74
50
|
end
|
75
51
|
|
76
|
-
def
|
77
|
-
|
78
|
-
|
79
|
-
# java.lang.* -> Java::JavaLang::*
|
80
|
-
# java.math.* -> Java::JavaMath::*
|
81
|
-
# java.text.* -> Java::JavaText::*
|
82
|
-
# java.util.* -> Java::JavaUtil::*
|
83
|
-
java_class('Java::JavaLang::'+name) || java_class('Java::JavaMath::'+name) ||
|
84
|
-
java_class('Java::JavaText::'+name) || java_class('Java::JavaUtil::'+name) || false
|
85
|
-
end
|
86
|
-
def self.java_class(const_name)
|
87
|
-
begin
|
88
|
-
c = eval(const_name)
|
89
|
-
c.class == Kernel ? nil : c
|
90
|
-
rescue NameError
|
91
|
-
return nil
|
92
|
-
end
|
93
|
-
end
|
52
|
+
def explore(outer_targets=[], alias_overridden={})
|
53
|
+
fields = {}
|
54
|
+
alias_map = {}.merge(alias_overridden)
|
94
55
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
56
|
+
all = []
|
57
|
+
unknowns = []
|
58
|
+
self.ast.listup(:stream).each do |node|
|
59
|
+
#TODO: raise error for same name of target/alias
|
60
|
+
if node.alias
|
61
|
+
alias_map[node.alias] = node.target
|
62
|
+
end
|
63
|
+
fields[node.target] = []
|
99
64
|
end
|
100
|
-
end
|
101
65
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
@children = children
|
107
|
-
end
|
108
|
-
def to_a
|
109
|
-
[@name] + @children.map(&:to_a)
|
66
|
+
default_target = fields.keys.size == 1 ? fields.keys.first : nil
|
67
|
+
|
68
|
+
outer_targets.each do |t|
|
69
|
+
fields[t] ||= []
|
110
70
|
end
|
111
|
-
|
112
|
-
|
71
|
+
|
72
|
+
field_bag = []
|
73
|
+
self.subqueries.each do |subquery|
|
74
|
+
field_bag.push(subquery.explore(fields.keys, alias_map))
|
113
75
|
end
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
76
|
+
|
77
|
+
self.ast.fields(default_target).each do |field_def|
|
78
|
+
f = field_def[:f]
|
79
|
+
all.push(f)
|
80
|
+
|
81
|
+
if field_def[:t]
|
82
|
+
t = alias_map[field_def[:t]] || field_def[:t]
|
83
|
+
unless fields[t]
|
84
|
+
raise "unknown target alias name for: #{field_def[:t]}.#{field_def[:f]}"
|
85
|
+
end
|
86
|
+
fields[t].push(f)
|
87
|
+
|
88
|
+
else
|
89
|
+
unknowns.push(f)
|
119
90
|
end
|
120
|
-
nil
|
121
91
|
end
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
92
|
+
|
93
|
+
field_bag.each do |bag|
|
94
|
+
all += bag['']
|
95
|
+
unknowns += bag[nil]
|
96
|
+
bag.keys.each do |t|
|
97
|
+
fields[t] ||= []
|
98
|
+
fields[t] += bag[t]
|
127
99
|
end
|
128
|
-
|
100
|
+
end
|
101
|
+
|
102
|
+
fields.keys.each do |target|
|
103
|
+
fields[target] = fields[target].sort.uniq
|
104
|
+
end
|
105
|
+
fields[''] = all.sort.uniq
|
106
|
+
fields[nil] = unknowns.sort.uniq
|
107
|
+
|
108
|
+
fields
|
109
|
+
end
|
110
|
+
|
111
|
+
def fields(target='')
|
112
|
+
# target '': fields for all targets (without target name)
|
113
|
+
# target nil: fields for unknown targets
|
114
|
+
return @fields[target] if @fields
|
115
|
+
|
116
|
+
@fields = explore()
|
117
|
+
@fields[target]
|
118
|
+
end
|
119
|
+
|
120
|
+
class ParseRuleSelectorImpl
|
121
|
+
include com.espertech.esper.epl.parse.ParseRuleSelector
|
122
|
+
def invokeParseRule(parser)
|
123
|
+
parser.startEPLExpressionRule().getTree()
|
129
124
|
end
|
130
125
|
end
|
131
126
|
|
132
127
|
def ast
|
133
|
-
#TODO:
|
134
|
-
#TODO: take care for parse error
|
128
|
+
#TODO: take care for parse error(com.espertech.esper.client.EPStatementSyntaxException)
|
135
129
|
return @ast if @ast
|
136
130
|
rule = ParseRuleSelectorImpl.new
|
137
131
|
target = @expression.dup
|
138
132
|
forerrmsg = @expression.dup
|
139
133
|
result = com.espertech.esper.epl.parse.ParseHelper.parse(target, forerrmsg, true, rule, false)
|
140
134
|
|
141
|
-
|
142
|
-
ASTNode.new(tree.text, (tree.children ? tree.children.map{|c| convSubTree(c)} : []))
|
143
|
-
end
|
144
|
-
@ast = convSubTree(result.getTree)
|
135
|
+
@ast = astnode(result.getTree)
|
145
136
|
@ast
|
146
137
|
end
|
147
138
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
139
|
+
def self.rewrite_event_type_name(statement_model, mapping)
|
140
|
+
# mapping: {target_name => query_event_type_name}
|
141
|
+
|
142
|
+
### esper-4.9.0/esper/doc/reference/html/epl_clauses.html#epl-subqueries
|
143
|
+
# Subqueries can only consist of a select clause, a from clause and a where clause.
|
144
|
+
# The group by and having clauses, as well as joins, outer-joins and output rate limiting are not permitted within subqueries.
|
145
|
+
|
146
|
+
# model.getFromClause.getStreams[0].getFilter.setEventTypeName("hoge")
|
147
|
+
|
148
|
+
# model.getSelectClause.getSelectList[1].getExpression => #<Java::ComEspertechEsperClientSoda::SubqueryExpression:0x3344c133>
|
149
|
+
# model.getSelectClause.getSelectList[1].getExpression.getModel.getFromClause.getStreams[0].getFilter.getEventTypeName
|
150
|
+
# model.getWhereClause.getChildren[1] .getModel.getFromClause.getStreams[0].getFilter.getEventTypeName
|
151
|
+
|
152
|
+
statement_model.getFromClause.getStreams.each do |stream|
|
153
|
+
target_name = stream.getFilter.getEventTypeName
|
154
|
+
unless mapping[target_name]
|
155
|
+
raise RuntimeError, "target missing in mapping, maybe BUG"
|
156
|
+
end
|
157
|
+
stream.getFilter.setEventTypeName(mapping[target_name])
|
158
|
+
end
|
159
|
+
|
160
|
+
dig = lambda {|node|
|
161
|
+
if node.is_a?(Java::ComEspertechEsperClientSoda::SubqueryExpression)
|
162
|
+
Norikra::Query.rewrite_event_type_name(node.getModel, mapping)
|
163
|
+
elsif node.getChildren.size > 0
|
164
|
+
node.getChildren.each do |c|
|
165
|
+
dig.call(c)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
}
|
169
|
+
|
170
|
+
if statement_model.getSelectClause
|
171
|
+
statement_model.getSelectClause.getSelectList.each do |item|
|
172
|
+
dig.call(item.getExpression)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
if statement_model.getWhereClause
|
177
|
+
statement_model.getWhereClause.getChildren.each do |child|
|
178
|
+
dig.call(child)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
statement_model
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
class SubQuery < Query
|
187
|
+
def initialize(ast_nodetree)
|
188
|
+
@ast = ast_nodetree
|
189
|
+
@targets = nil
|
190
|
+
@subqueries = nil
|
191
|
+
end
|
192
|
+
|
193
|
+
def ast; @ast; end
|
194
|
+
|
195
|
+
def subqueries
|
196
|
+
return @subqueries if @subqueries
|
197
|
+
@subqueries = @ast.children.map{|c| c.listup(:subquery)}.reduce(&:+).map{|n| Norikra::SubQuery.new(n)}
|
198
|
+
@subqueries
|
199
|
+
end
|
200
|
+
|
201
|
+
def name; ''; end
|
202
|
+
def expression; ''; end
|
203
|
+
def dup; self; end
|
204
|
+
def dup_with_stream_name(actual_name); self; end
|
173
205
|
end
|
174
206
|
end
|
@@ -0,0 +1,237 @@
|
|
1
|
+
module Norikra
|
2
|
+
class Query
|
3
|
+
### SELECT MAX(size) AS maxsize, fraud.aaa,bbb
|
4
|
+
### FROM FraudWarningEvent.win:keepall() AS fraud,
|
5
|
+
### PINChangeEvent(size > 10).win:time(20 sec)
|
6
|
+
### WHERE fraud.accountNumber.substr(0,8) = substr(PINChangeEvent.accountNumber, 0, 8)
|
7
|
+
### AND cast(PINChangeEvent.size,double) > 10.5
|
8
|
+
#
|
9
|
+
# ["EPL_EXPR",
|
10
|
+
# ["SELECTION_EXPR",
|
11
|
+
# ["SELECTION_ELEMENT_EXPR",
|
12
|
+
# ["LIB_FUNC_CHAIN",
|
13
|
+
# ["LIB_FUNCTION",
|
14
|
+
# "max",
|
15
|
+
# ["EVENT_PROP_EXPR", ["EVENT_PROP_SIMPLE", "size"]],
|
16
|
+
# "("]],
|
17
|
+
# "maxsize"],
|
18
|
+
# ["SELECTION_ELEMENT_EXPR",
|
19
|
+
# ["EVENT_PROP_EXPR",
|
20
|
+
# ["EVENT_PROP_SIMPLE", "fraud"],
|
21
|
+
# ["EVENT_PROP_SIMPLE", "aaa"]]],
|
22
|
+
# ["SELECTION_ELEMENT_EXPR",
|
23
|
+
# ["EVENT_PROP_EXPR", ["EVENT_PROP_SIMPLE", "bbb"]]]],
|
24
|
+
# ["STREAM_EXPR",
|
25
|
+
# ["EVENT_FILTER_EXPR", "FraudWarningEvent"],
|
26
|
+
# ["VIEW_EXPR", "win", "keepall"],
|
27
|
+
# "fraud"],
|
28
|
+
# ["STREAM_EXPR",
|
29
|
+
# ["EVENT_FILTER_EXPR",
|
30
|
+
# "PINChangeEvent",
|
31
|
+
# [">", ["EVENT_PROP_EXPR", ["EVENT_PROP_SIMPLE", "size"]], "10"]],
|
32
|
+
# ["VIEW_EXPR", "win", "time", ["TIME_PERIOD", ["SECOND_PART", "20"]]]],
|
33
|
+
# ["WHERE_EXPR",
|
34
|
+
# ["EVAL_AND_EXPR",
|
35
|
+
# ["EVAL_EQUALS_EXPR",
|
36
|
+
# ["LIB_FUNC_CHAIN",
|
37
|
+
# ["LIB_FUNCTION", "fraud.accountNumber", "substr", "0", "8", "("]],
|
38
|
+
# ["LIB_FUNC_CHAIN",
|
39
|
+
# ["LIB_FUNCTION",
|
40
|
+
# "substr",
|
41
|
+
# ["EVENT_PROP_EXPR",
|
42
|
+
# ["EVENT_PROP_SIMPLE", "PINChangeEvent"],
|
43
|
+
# ["EVENT_PROP_SIMPLE", "accountNumber"]],
|
44
|
+
# "0",
|
45
|
+
# "8",
|
46
|
+
# "("]]],
|
47
|
+
# [">",
|
48
|
+
# ["cast",
|
49
|
+
# ["EVENT_PROP_EXPR",
|
50
|
+
# ["EVENT_PROP_SIMPLE", "PINChangeEvent"],
|
51
|
+
# ["EVENT_PROP_SIMPLE", "size"]],
|
52
|
+
# "double"],
|
53
|
+
# "10.5"]]]]
|
54
|
+
|
55
|
+
def astnode(tree)
|
56
|
+
children = if tree.children
|
57
|
+
tree.children.map{|c| astnode(c)}
|
58
|
+
else
|
59
|
+
[]
|
60
|
+
end
|
61
|
+
case tree.text
|
62
|
+
when 'EVENT_PROP_EXPR'
|
63
|
+
ASTEventPropNode.new(tree.text, children)
|
64
|
+
when 'LIB_FUNCTION'
|
65
|
+
ASTLibFunctionNode.new(tree.text, children)
|
66
|
+
when 'STREAM_EXPR'
|
67
|
+
ASTStreamNode.new(tree.text, children)
|
68
|
+
when 'SUBSELECT_EXPR'
|
69
|
+
ASTSubSelectNode.new(tree.text, children)
|
70
|
+
else
|
71
|
+
ASTNode.new(tree.text, children)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class ASTNode
|
76
|
+
attr_accessor :name, :children
|
77
|
+
|
78
|
+
def initialize(name, children)
|
79
|
+
@name = name
|
80
|
+
@children = children
|
81
|
+
end
|
82
|
+
|
83
|
+
def nodetype?(*sym)
|
84
|
+
false
|
85
|
+
end
|
86
|
+
|
87
|
+
def to_a
|
88
|
+
[@name] + @children.map{|c| c.children.size > 0 ? c.to_a : c.name}
|
89
|
+
end
|
90
|
+
|
91
|
+
def child
|
92
|
+
@children.first
|
93
|
+
end
|
94
|
+
|
95
|
+
def find(type) # only one, depth-first search
|
96
|
+
return self if type.is_a?(String) && @name == type || nodetype?(type)
|
97
|
+
|
98
|
+
@children.each do |c|
|
99
|
+
next if type != :subquery && c.nodetype?(:subquery)
|
100
|
+
r = c.find(type)
|
101
|
+
return r if r
|
102
|
+
end
|
103
|
+
nil
|
104
|
+
end
|
105
|
+
|
106
|
+
def listup(type) # search all nodes that has 'type'
|
107
|
+
result = []
|
108
|
+
result.push(self) if type.is_a?(String) && @name == type || nodetype?(type)
|
109
|
+
|
110
|
+
@children.each do |c|
|
111
|
+
next if type != :subquery && c.nodetype?(:subquery)
|
112
|
+
result.push(*c.listup(type))
|
113
|
+
end
|
114
|
+
result
|
115
|
+
end
|
116
|
+
|
117
|
+
def fields(default_target=nil)
|
118
|
+
@children.map{|c| c.nodetype?(:subquery) ? [] : c.fields(default_target)}.reduce(&:+) || []
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
class ASTEventPropNode < ASTNode # EVENT_PROP_EXPR
|
123
|
+
# ["EVENT_PROP_EXPR", ["EVENT_PROP_SIMPLE", "bbb"]]
|
124
|
+
# ["EVENT_PROP_EXPR", ["EVENT_PROP_SIMPLE", "fraud"], ["EVENT_PROP_SIMPLE", "aaa"]]
|
125
|
+
|
126
|
+
def nodetype?(*sym)
|
127
|
+
sym.include?(:prop) || sym.include?(:property)
|
128
|
+
end
|
129
|
+
|
130
|
+
def fields(default_target=nil)
|
131
|
+
props = self.listup('EVENT_PROP_SIMPLE')
|
132
|
+
if props.size > 1 # alias.fieldname
|
133
|
+
[ {:f => props[1].child.name, :t => props[0].child.name} ]
|
134
|
+
else # fieldname (default target)
|
135
|
+
[ {:f => props[0].child.name, :t => default_target } ]
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
class ASTLibFunctionNode < ASTNode # LIB_FUNCTION
|
141
|
+
# ["LIB_FUNCTION", "now", "("] #### now()
|
142
|
+
# ["LIB_FUNCTION", "hoge", "length", "("] # #### hoge.length()
|
143
|
+
# ["LIB_FUNCTION", "hoge", "substr", "0", "("] #### hoge.substr(0)
|
144
|
+
# ["LIB_FUNCTION", "substr", "10", "0", "("] #### substr(10,0)
|
145
|
+
# ["LIB_FUNCTION", "hoge", "substr", "0", "8", "("] #### hoge.substr(0,8)
|
146
|
+
# ["LIB_FUNCTION", "max", ["EVENT_PROP_EXPR", ["EVENT_PROP_SIMPLE", "size"]], "("] #### max(size)
|
147
|
+
|
148
|
+
def nodetype?(*sym)
|
149
|
+
sym.include?(:lib) || sym.include?(:libfunc)
|
150
|
+
end
|
151
|
+
|
152
|
+
def fields(default_target=nil)
|
153
|
+
if @children.size <= 2
|
154
|
+
# single function like 'now()', function-name and "("
|
155
|
+
[]
|
156
|
+
|
157
|
+
elsif @children[1].nodetype?(:prop, :lib, :subquery)
|
158
|
+
# first element should be func name if second element is property, library call or subqueries
|
159
|
+
self.listup('EVENT_PROP_EXPR').map{|c| c.fields(default_target)}.reduce(&:+) || []
|
160
|
+
|
161
|
+
elsif @children[1].name =~ /^(-)?\d+(\.\d+)$/ || @children[1].name =~ /^'[^']*'$/ || @children[1].name =~ /^"[^"]*"$/
|
162
|
+
# first element should be func name if secod element is number/string literal
|
163
|
+
self.listup('EVENT_PROP_EXPR').map{|c| c.fields(default_target)}.reduce(&:+) || []
|
164
|
+
|
165
|
+
elsif Norikra::Query.imported_java_class?(@children[0].name)
|
166
|
+
# Java imported class name (ex: 'Math.abs(-1)')
|
167
|
+
self.listup('EVENT_PROP_EXPR').map{|c| c.fields(default_target)}.reduce(&:+) || []
|
168
|
+
|
169
|
+
else
|
170
|
+
# first element may be property (simple 'fieldname.funcname()' or fully qualified 'target.fieldname.funcname()')
|
171
|
+
target,fieldname = if @children[0].name.include?('.')
|
172
|
+
@children[0].name.split('.', 2)
|
173
|
+
else
|
174
|
+
[default_target,@children[0].name]
|
175
|
+
end
|
176
|
+
children_list = self.listup('EVENT_PROP_EXPR').map{|c| c.fields(default_target)}.reduce(&:+) || []
|
177
|
+
[{:f => fieldname, :t => target}] + children_list
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
class ASTStreamNode < ASTNode # STREAM_EXPR
|
183
|
+
# ["STREAM_EXPR",
|
184
|
+
# ["EVENT_FILTER_EXPR", "FraudWarningEvent"],
|
185
|
+
# ["VIEW_EXPR", "win", "keepall"],
|
186
|
+
# "fraud"],
|
187
|
+
# ["STREAM_EXPR",
|
188
|
+
# ["EVENT_FILTER_EXPR",
|
189
|
+
# "PINChangeEvent",
|
190
|
+
# [">", ["EVENT_PROP_EXPR", ["EVENT_PROP_SIMPLE", "size"]], "10"]],
|
191
|
+
# ["VIEW_EXPR", "win", "time", ["TIME_PERIOD", ["SECOND_PART", "20"]]]],
|
192
|
+
|
193
|
+
def nodetype?(*sym)
|
194
|
+
sym.include?(:stream)
|
195
|
+
end
|
196
|
+
|
197
|
+
def target
|
198
|
+
self.find('EVENT_FILTER_EXPR').child.name
|
199
|
+
end
|
200
|
+
|
201
|
+
def alias
|
202
|
+
@children.last.children.size < 1 ? @children.last.name : nil
|
203
|
+
end
|
204
|
+
|
205
|
+
def fields(default_target=nil)
|
206
|
+
this_target = self.target
|
207
|
+
self.listup('EVENT_PROP_EXPR').map{|p| p.fields(this_target)}.reduce(&:+) || []
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
class ASTSubSelectNode < ASTNode # SUBSELECT_EXPR
|
212
|
+
def nodetype?(*sym)
|
213
|
+
sym.include?(:subquery)
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def self.imported_java_class?(name)
|
218
|
+
return false unless name =~ /^[A-Z]/
|
219
|
+
# Esper auto-imports the following Java library packages:
|
220
|
+
# java.lang.* -> Java::JavaLang::*
|
221
|
+
# java.math.* -> Java::JavaMath::*
|
222
|
+
# java.text.* -> Java::JavaText::*
|
223
|
+
# java.util.* -> Java::JavaUtil::*
|
224
|
+
java_class('Java::JavaLang::'+name) || java_class('Java::JavaMath::'+name) ||
|
225
|
+
java_class('Java::JavaText::'+name) || java_class('Java::JavaUtil::'+name) || false
|
226
|
+
end
|
227
|
+
|
228
|
+
def self.java_class(const_name)
|
229
|
+
begin
|
230
|
+
c = eval(const_name)
|
231
|
+
c.class == Kernel ? nil : c
|
232
|
+
rescue NameError
|
233
|
+
return nil
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
@@ -41,6 +41,33 @@ module Norikra
|
|
41
41
|
@typedefs[target].field_defined?(field_name_list)
|
42
42
|
end
|
43
43
|
|
44
|
+
def ready?(query)
|
45
|
+
query.targets.all?{|t| @typedefs[t] && ! @typedefs[t].lazy? && @typedefs[t].field_defined?(query.fields(t))} &&
|
46
|
+
query.fields(nil).all?{|f| query.targets.any?{|t| @typedefs[t].field_defined?([f])}}
|
47
|
+
end
|
48
|
+
|
49
|
+
def generate_fieldset_mapping(query)
|
50
|
+
fields_set = {}
|
51
|
+
query.targets.each do |target|
|
52
|
+
fields_set[target] = query.fields(target)
|
53
|
+
end
|
54
|
+
query.fields(nil).each do |field|
|
55
|
+
assumed = query.targets.select{|t| @typedefs[t].field_defined?([field])}
|
56
|
+
if assumed.size != 1
|
57
|
+
#TODO exception class
|
58
|
+
raise "cannot determine target for field #{field}"
|
59
|
+
end
|
60
|
+
fields_set[assumed.first].push(field)
|
61
|
+
end
|
62
|
+
|
63
|
+
mapping = {}
|
64
|
+
fields_set.each do |target,fields|
|
65
|
+
mapping[target] = generate_query_fieldset(target, fields.sort.uniq)
|
66
|
+
end
|
67
|
+
|
68
|
+
mapping
|
69
|
+
end
|
70
|
+
|
44
71
|
def bind_fieldset(target, level, fieldset)
|
45
72
|
fieldset.bind(target, level)
|
46
73
|
@typedefs[target].push(level, fieldset)
|
data/lib/norikra/version.rb
CHANGED
data/spec/query_spec.rb
CHANGED
@@ -6,26 +6,107 @@ describe Norikra::Query do
|
|
6
6
|
context 'when instanciate' do
|
7
7
|
describe '#initialize' do
|
8
8
|
context 'with simple query' do
|
9
|
-
|
10
|
-
|
9
|
+
it 'returns query instances collectly parsed' do
|
10
|
+
expression = 'SELECT count(*) AS cnt FROM TestTable.win:time_batch(10 sec) WHERE path="/" AND size > 100 and param.length() > 0'
|
11
|
+
q = Norikra::Query.new(
|
11
12
|
:name => 'TestTable query1', :expression => expression
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
13
|
+
)
|
14
|
+
expect(q.name).to eql('TestTable query1')
|
15
|
+
expect(q.expression).to eql(expression)
|
16
|
+
expect(q.targets).to eql(['TestTable'])
|
17
|
+
|
18
|
+
expect(q.fields).to eql(['param', 'path', 'size'].sort)
|
19
|
+
expect(q.fields('TestTable')).to eql(['param','path','size'].sort)
|
20
|
+
expect(q.fields(nil)).to eql([])
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'with query including Static lib call' do
|
25
|
+
it 'returns query instances collectly parsed' do
|
26
|
+
expression = 'SELECT count(*) AS cnt FROM TestTable.win:time_batch(10 sec) AS source WHERE source.path="/" AND Math.abs(-1 * source.size) > 3'
|
27
|
+
q = Norikra::Query.new(
|
28
|
+
:name => 'TestTable query2', :expression => expression
|
29
|
+
)
|
30
|
+
expect(q.name).to eql('TestTable query2')
|
31
|
+
expect(q.expression).to eql(expression)
|
32
|
+
expect(q.targets).to eql(['TestTable'])
|
33
|
+
|
34
|
+
expect(q.fields).to eql(['path', 'size'].sort)
|
35
|
+
expect(q.fields('TestTable')).to eql(['path', 'size'].sort)
|
36
|
+
expect(q.fields(nil)).to eql([])
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context 'with query with join' do
|
41
|
+
it 'returns query instances collectly parsed' do
|
42
|
+
expression = 'select product, max(sta.size) as maxsize from StreamA.win:keepall() as sta, StreamB(size > 10).win:time(20 sec) as stb where sta.data.substr(0,8) = stb.header AND Math.abs(sta.size) > 3'
|
43
|
+
q = Norikra::Query.new(
|
44
|
+
:name => 'TestTable query3', :expression => expression
|
45
|
+
)
|
46
|
+
expect(q.name).to eql('TestTable query3')
|
47
|
+
expect(q.expression).to eql(expression)
|
48
|
+
expect(q.targets).to eql(['StreamA', 'StreamB'])
|
49
|
+
|
50
|
+
expect(q.fields).to eql(['product', 'size', 'data', 'header'].sort)
|
51
|
+
expect(q.fields('StreamA')).to eql(['size','data'].sort)
|
52
|
+
expect(q.fields('StreamB')).to eql(['size','header'].sort)
|
53
|
+
expect(q.fields(nil)).to eql(['product'])
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context 'with query with subquery (where clause)' do
|
58
|
+
it 'returns query instances collectly parsed' do
|
59
|
+
expression = 'select * from RfidEvent as RFID where "Dock 1" = (select name from Zones.std:unique(zoneName) where zoneId = RFID.zoneId)'
|
60
|
+
q = Norikra::Query.new(
|
61
|
+
:name => 'TestTable query4', :expression => expression
|
62
|
+
)
|
63
|
+
expect(q.name).to eql('TestTable query4')
|
64
|
+
expect(q.expression).to eql(expression)
|
65
|
+
expect(q.targets).to eql(['RfidEvent', 'Zones'])
|
66
|
+
|
67
|
+
expect(q.fields).to eql(['name','zoneName','zoneId'].sort)
|
68
|
+
expect(q.fields('RfidEvent')).to eql(['zoneId'])
|
69
|
+
expect(q.fields('Zones')).to eql(['name','zoneName','zoneId'].sort)
|
70
|
+
expect(q.fields(nil)).to eql([])
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
context 'with query with subquery (select clause)' do
|
75
|
+
it 'returns query instances collectly parsed' do
|
76
|
+
expression = 'select zoneId, (select name from Zones.std:unique(zoneName) where zoneId = RfidEvent.zoneId) as name from RfidEvent'
|
77
|
+
q = Norikra::Query.new(
|
78
|
+
:name => 'TestTable query5', :expression => expression
|
79
|
+
)
|
80
|
+
expect(q.name).to eql('TestTable query5')
|
81
|
+
expect(q.expression).to eql(expression)
|
82
|
+
expect(q.targets).to eql(['RfidEvent', 'Zones'].sort)
|
83
|
+
|
84
|
+
expect(q.fields).to eql(['name','zoneName','zoneId'].sort)
|
85
|
+
expect(q.fields('RfidEvent')).to eql(['zoneId'])
|
86
|
+
expect(q.fields('Zones')).to eql(['name','zoneName','zoneId'].sort)
|
87
|
+
expect(q.fields(nil)).to eql([])
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context 'with query with subquery (from clause)' do
|
92
|
+
it 'returns query instances collectly parsed' do
|
93
|
+
expression = "select * from BarData(ticker='MSFT', sub(closePrice, (select movAgv from SMA20Stream(ticker='MSFT').std:lastevent())) > 0)"
|
94
|
+
q = Norikra::Query.new(
|
95
|
+
:name => 'TestTable query6', :expression => expression
|
96
|
+
)
|
97
|
+
expect(q.name).to eql('TestTable query6')
|
98
|
+
expect(q.expression).to eql(expression)
|
99
|
+
expect(q.targets).to eql(['BarData', 'SMA20Stream'].sort)
|
100
|
+
|
101
|
+
expect(q.fields).to eql(['ticker','closePrice','movAgv'].sort)
|
102
|
+
expect(q.fields('BarData')).to eql(['ticker','closePrice'].sort)
|
103
|
+
expect(q.fields('SMA20Stream')).to eql(['movAgv','ticker'].sort)
|
104
|
+
expect(q.fields(nil)).to eql([])
|
105
|
+
end
|
17
106
|
end
|
18
|
-
context 'with query including Static lib call'
|
19
|
-
expression = 'SELECT count(*) AS cnt FROM TestTable.win:time_batch(10 sec) WHERE path="/" AND Math.abs(-1 * size) > 3'
|
20
|
-
subject { Norikra::Query.new(
|
21
|
-
:name => 'TestTable query1', :expression => expression
|
22
|
-
) }
|
23
|
-
its(:name){ should == 'TestTable query1' }
|
24
|
-
its(:expression){ should == expression }
|
25
|
-
its(:targets){ should == ['TestTable'] }
|
26
|
-
its(:fields){ should == ['path', 'size'] }
|
27
107
|
end
|
28
|
-
|
108
|
+
|
109
|
+
describe '#dup_with_stream_name' do
|
29
110
|
context 'with simple query' do
|
30
111
|
expression = 'SELECT count(*) AS cnt FROM TestTable.win:time_batch(10 sec) WHERE path="/" AND size > 100 and param.length() > 0'
|
31
112
|
it 'returns duplicated object, with replaced ' do
|
@@ -37,6 +118,7 @@ describe Norikra::Query do
|
|
37
118
|
)
|
38
119
|
end
|
39
120
|
end
|
121
|
+
|
40
122
|
context 'with query with newlines' do
|
41
123
|
expression = <<EOQ
|
42
124
|
SELECT
|
@@ -61,6 +143,7 @@ EOQ
|
|
61
143
|
end
|
62
144
|
end
|
63
145
|
end
|
146
|
+
|
64
147
|
describe '.imported_java_class?' do
|
65
148
|
it 'can do judge passed name exists under java package tree or not' do
|
66
149
|
expect(Norikra::Query.imported_java_class?('String')).to be_true
|
@@ -41,6 +41,8 @@ describe Norikra::TypedefManager do
|
|
41
41
|
context 'when instanciated with a target with fields definition' do
|
42
42
|
manager = Norikra::TypedefManager.new
|
43
43
|
manager.add_target('sample', {'a'=>'string','b'=>'string','c'=>'double'})
|
44
|
+
manager.reserve('sample', 'z', 'boolean')
|
45
|
+
manager.add_target('sample_next', {'a'=>'string','b'=>'string','c'=>'double','d'=>'double'})
|
44
46
|
|
45
47
|
set_query_base = Norikra::FieldSet.new({'a'=>'string','b'=>'string','c'=>'double'})
|
46
48
|
|
@@ -68,9 +70,63 @@ describe Norikra::TypedefManager do
|
|
68
70
|
describe '#fields_defined?' do
|
69
71
|
it 'does not fail' do
|
70
72
|
expect(manager.fields_defined?('sample', ['a','b','x'])).to be_true
|
71
|
-
expect(manager.fields_defined?('sample', ['a','b','
|
73
|
+
expect(manager.fields_defined?('sample', ['a','b','y'])).to be_false
|
72
74
|
end
|
73
75
|
end
|
76
|
+
|
77
|
+
describe '#ready?' do
|
78
|
+
context 'with query with single target' do
|
79
|
+
it 'returns boolean which matches target or not' do
|
80
|
+
q1 = Norikra::Query.new(:name => 'test', :expression => 'select a from sample.win:time(5 sec) where c > 1.0 and z')
|
81
|
+
expect(manager.ready?(q1)).to be_true
|
82
|
+
q2 = Norikra::Query.new(:name => 'test', :expression => 'select a from sample.win:time(5 sec) where c > 1.0 and d > 2.0')
|
83
|
+
expect(manager.ready?(q2)).to be_false
|
84
|
+
q3 = Norikra::Query.new(:name => 'test', :expression => 'select a from sample2.win:time(5 sec) where c > 1.0 and d > 2.0')
|
85
|
+
expect(manager.ready?(q3)).to be_false
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
context 'with query with multi targets, including unexisting target' do
|
90
|
+
it 'returns false' do
|
91
|
+
q = Norikra::Query.new(:name => 'test', :expression => 'select x.a,y.a from sample.win:time(5 sec) as x, sample2.win:time(5 sec) as y where x.c > 1.0 and y.d > 1.0')
|
92
|
+
expect(manager.ready?(q)).to be_false
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
context 'with query with multi targets, all of them are exisitng' do
|
97
|
+
it 'returns true' do
|
98
|
+
q = Norikra::Query.new(:name => 'test', :expression => 'select x.a,d from sample.win:time(5 sec) as x, sample_next.win:time(5 sec) as y where x.c > 1.0 and y.d > 1.0')
|
99
|
+
expect(manager.ready?(q)).to be_true
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
describe '#generate_fieldset_mapping' do
|
105
|
+
it 'retuns collect mapping for fieldsets' do
|
106
|
+
q1 = Norikra::Query.new(:name => 'test', :expression => 'select a from sample.win:time(5 sec) where c > 1.0 and z')
|
107
|
+
map1 = manager.generate_fieldset_mapping(q1)
|
108
|
+
expect(map1.keys).to eql(['sample'])
|
109
|
+
expect(map1.values.size).to eql(1)
|
110
|
+
expect(map1.values.first).to be_a(Norikra::FieldSet)
|
111
|
+
expect(map1.values.first.fields.size).to eql(4)
|
112
|
+
expect(map1.values.first.fields.keys).to eql(['a','b','c','z']) # a,b,c is non-optional fields
|
113
|
+
|
114
|
+
q2 = Norikra::Query.new(:name => 'test', :expression => 'select a from sample.win:time(5 sec) where c > 1.0')
|
115
|
+
map2 = manager.generate_fieldset_mapping(q2)
|
116
|
+
expect(map2.keys).to eql(['sample'])
|
117
|
+
expect(map2.values.size).to eql(1)
|
118
|
+
expect(map2.values.first).to be_a(Norikra::FieldSet)
|
119
|
+
expect(map2.values.first.fields.size).to eql(3)
|
120
|
+
expect(map2.values.first.fields.keys).to eql(['a','b','c']) # a,b,c is non-optional fields
|
121
|
+
|
122
|
+
q3 = Norikra::Query.new(:name => 'test', :expression => 'select x.a, z from sample.win:time(5 sec) as x, sample_next.win:time(5 sec) as y where x.c > 1.0 and y.d > 1.0')
|
123
|
+
map3 = manager.generate_fieldset_mapping(q3)
|
124
|
+
expect(map3.keys).to eql(['sample', 'sample_next'])
|
125
|
+
expect(map3['sample'].fields.keys).to eql(['a','b','c','z'])
|
126
|
+
expect(map3['sample_next'].fields.keys).to eql(['a','b','c','d'])
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
74
130
|
describe '#bind_fieldset' do
|
75
131
|
it 'does not fail' do
|
76
132
|
manager.bind_fieldset('sample', :query, set_query_base)
|
metadata
CHANGED
@@ -2,14 +2,14 @@
|
|
2
2
|
name: norikra
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.0.
|
5
|
+
version: 0.0.6
|
6
6
|
platform: java
|
7
7
|
authors:
|
8
8
|
- TAGOMORI Satoshi
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-07-
|
12
|
+
date: 2013-07-05 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: mizuno
|
@@ -266,6 +266,7 @@ files:
|
|
266
266
|
- lib/norikra/logger_mizuno_patch.rb
|
267
267
|
- lib/norikra/output_pool.rb
|
268
268
|
- lib/norikra/query.rb
|
269
|
+
- lib/norikra/query/ast.rb
|
269
270
|
- lib/norikra/rpc.rb
|
270
271
|
- lib/norikra/rpc/handler.rb
|
271
272
|
- lib/norikra/rpc/http.rb
|