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.
@@ -29,7 +29,7 @@ module Norikra
29
29
  @targets = []
30
30
  @queries = []
31
31
 
32
- @waiting_queries = {} # target => [query]
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
- unless @targets.include?(query.targets.first)
62
- open(query.targets.first) # open as lazy defined target
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
- #TODO: trace log
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
- if @typedef_manager.lazy?(target) || !@typedef_manager.fields_defined?(target, query.fields)
175
- @waiting_queries[target] ||= []
176
- @waiting_queries[target].push(query)
177
- else
178
- query_fieldset = @typedef_manager.generate_query_fieldset(target, query.fields)
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
- register_query_actually(target, query_fieldset.event_type_name, query)
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
- waitings = @waiting_queries.delete(target) || []
198
- not_registered = []
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
- waitings.each do |query|
201
- if @typedef_manager.fields_defined?(target, query.fields)
202
- query_fieldset = @typedef_manager.generate_query_fieldset(target, query.fields)
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[target]
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(target, stream_name, query)
228
- query = query.dup_with_stream_name(stream_name)
229
- epl = @service.getEPAdministrator.createEPL(query.expression)
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
- #TODO: debug log
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
@@ -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
- #TODO: test with JOINs.
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 fields
45
- return @fields if @fields
46
- #TODO: this code doesn't care JOINs.
47
- ### fields -> {'target_name' => fields} ?
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 self.imported_java_class?(name)
77
- return false unless name =~ /^[A-Z]/
78
- # Esper auto-imports the following Java library packages:
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
- class ParseRuleSelectorImpl
96
- include com.espertech.esper.epl.parse.ParseRuleSelector
97
- def invokeParseRule(parser)
98
- parser.startEPLExpressionRule().getTree()
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
- class ASTNode
103
- attr_accessor :name, :children
104
- def initialize(name, children)
105
- @name = name
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
- def child
112
- @children.first
71
+
72
+ field_bag = []
73
+ self.subqueries.each do |subquery|
74
+ field_bag.push(subquery.explore(fields.keys, alias_map))
113
75
  end
114
- def find(node_name) # only one, depth-first search
115
- return self if @name == node_name
116
- @children.each do |c|
117
- r = c.find(node_name)
118
- return r if r
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
- def listup(node_name)
123
- result = []
124
- result.push(self) if @name == node_name
125
- @children.each do |c|
126
- result.push(*c.listup(node_name))
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
- result
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: test
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
- def convSubTree(tree)
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
- ### select max(price) as maxprice from HogeTable.win:time_batch(10 sec) where cast(amount, double) > 2 and price > 50
149
- # query.ast.to_a
150
- # ["EPL_EXPR",
151
- # ["SELECTION_EXPR",
152
- # ["SELECTION_ELEMENT_EXPR",
153
- # ["LIB_FUNC_CHAIN",
154
- # ["LIB_FUNCTION",
155
- # ["max"],
156
- # ["EVENT_PROP_EXPR", ["EVENT_PROP_SIMPLE", ["price"]]],
157
- # ["("]]],
158
- # ["maxprice"]]],
159
- # ["STREAM_EXPR",
160
- # ["EVENT_FILTER_EXPR", ["HogeTable"]],
161
- # ["VIEW_EXPR",
162
- # ["win"],
163
- # ["time_batch"],
164
- # ["TIME_PERIOD", ["SECOND_PART", ["10"]]]]],
165
- # ["WHERE_EXPR",
166
- # ["EVAL_AND_EXPR",
167
- # [">",
168
- # ["cast",
169
- # ["EVENT_PROP_EXPR", ["EVENT_PROP_SIMPLE", ["amount"]]],
170
- # ["double"]],
171
- # ["2"]],
172
- # [">", ["EVENT_PROP_EXPR", ["EVENT_PROP_SIMPLE", ["price"]]], ["50"]]]]]
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)
@@ -1,3 +1,3 @@
1
1
  module Norikra
2
- VERSION = "0.0.5"
2
+ VERSION = "0.0.6"
3
3
  end
@@ -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
- expression = 'SELECT count(*) AS cnt FROM TestTable.win:time_batch(10 sec) WHERE path="/" AND size > 100 and param.length() > 0'
10
- subject { Norikra::Query.new(
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
- its(:name){ should == 'TestTable query1' }
14
- its(:expression){ should == expression }
15
- its(:targets){ should == ['TestTable'] }
16
- its(:fields){ should == ['param', 'path', 'size'] }
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
- describe '#dup_with_stream_name' do
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','z'])).to be_false
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
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-01 00:00:00.000000000 Z
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