norikra 0.0.5-java → 0.0.6-java
Sign up to get free protection for your applications and to get access to all the features.
- 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
|