msfl_visitors 1.1.0.dev2 → 1.1.0.dev3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 45becd6e90f826ce123e6f4c5ee389e37c4f3337
4
- data.tar.gz: 87baa18d96cba72ef39deecf22447ecb7a7c30f3
3
+ metadata.gz: 8ec3bfda1d028fdcd4c4fb02eaf3e6a26c792e4b
4
+ data.tar.gz: f1e1324598941df2d59dc3da9b80ef2f0a4e3710
5
5
  SHA512:
6
- metadata.gz: cf1b9343cdf9a64ad46c916b36c37d5eb06c8cb43cb224329194722ad7af915e5e0069ce126004c68c4e46cdcd3e437dd0bcaf100986e5855ffe74a874a083dd
7
- data.tar.gz: c034ae9ca6db09fdc7ebc67387a61903e08de3cc8b84e1de735bd0607ac20a3b2688a029203bbd2ce16202ef48b05d51b4be0332e114f6b00c34e11045a0aa49
6
+ metadata.gz: 8f910b468fefd6e905119c76e9fd7e9939490b37789ba76f9d1cc5a32d42d30a565f44aa32c0c72e8cd149ac10e4b564972f66536d9a59ba95aeca592744d408
7
+ data.tar.gz: 9e621c0a5175d457c0dc0c63324ac229719442d01c9008a043ced18a41767c37597a5c14da68f710ed0cdf811c0820c07c17178a793b7820c2afc5a2ae67f0d3
@@ -12,19 +12,30 @@ module MSFLVisitors
12
12
  end
13
13
 
14
14
  def visit(node)
15
- case node
16
- when Nodes::Partial
17
- in_aggregation_mode do
18
- clauses.concat get_visitor.visit(node)
19
- ""
20
- end
21
- else
22
- get_visitor.visit(node)
15
+ if mode == :es_term
16
+ get_visitor.visit(node)
17
+ else
18
+ case node
19
+ when Nodes::Partial
20
+ in_aggregation_mode do
21
+ clauses.concat get_visitor.visit(node)
22
+ ""
23
+ end
24
+ else
25
+ get_visitor.visit(node)
26
+ end
23
27
  end
24
28
  end
25
29
 
26
30
  def get_visitor
27
- (mode == :term ? TermFilterVisitor : AggregationsVisitor).new(self)
31
+ case mode
32
+ when :term
33
+ TermFilterVisitor.new(self)
34
+ when :es_term
35
+ ESTermFilterVisitor.new(self)
36
+ else
37
+ AggregationsVisitor.new(self)
38
+ end
28
39
  end
29
40
 
30
41
  def in_aggregation_mode
@@ -202,6 +213,72 @@ module MSFLVisitors
202
213
 
203
214
  attr_reader :visitor
204
215
  end
216
+
217
+ class ESTermFilterVisitor
218
+ def initialize(visitor)
219
+ @visitor = visitor
220
+ end
221
+
222
+ RANGE_OPERATORS = {
223
+ Nodes::GreaterThan => :gt,
224
+ Nodes::GreaterThanEqual => :gte,
225
+ Nodes::LessThan => :lt,
226
+ Nodes::LessThanEqual => :lte,
227
+ }
228
+
229
+ def visit(node)
230
+ case node
231
+ when Nodes::Partial
232
+ { given: Hash[[node.left.accept(visitor), node.right.accept(visitor)]] }
233
+
234
+ when Nodes::Equal
235
+ { term: { node.left.accept(visitor) => node.right.accept(visitor) } }
236
+ # [{ clause: }]
237
+ when Nodes::Field
238
+ node.value.to_sym
239
+ when Nodes::Date, Nodes::Time
240
+ node.value.iso8601
241
+ when Nodes::Word,
242
+ Nodes::Number,
243
+ Nodes::Boolean,
244
+ Nodes::Dataset
245
+ node.value
246
+ when Nodes::GreaterThan,
247
+ Nodes::GreaterThanEqual,
248
+ Nodes::LessThan,
249
+ Nodes::LessThanEqual
250
+ { range: { node.left.accept(visitor) => { RANGE_OPERATORS[node.class] => node.right.accept(visitor) } } }
251
+ when Nodes::Given
252
+ [:filter, node.contents.first.accept(visitor)]
253
+ when Nodes::ExplicitFilter
254
+ [:filter, node.contents.map { |n| n.accept(visitor) }.reduce({}) { |hsh, x| hsh.merge!(x); hsh } ]
255
+ when Nodes::NamedValue
256
+ [:aggs, {node.name.accept(visitor).to_sym => Hash[[node.value.accept(visitor)]]}]
257
+ when Nodes::Containment
258
+ { terms: {node.left.accept(visitor).to_sym => node.right.accept(visitor)} }
259
+ when Nodes::Set
260
+ node.contents.map { |n| n.accept(visitor) }
261
+ when Nodes::Filter
262
+ if node.contents.count == 1
263
+ node.contents.first.accept visitor
264
+ else
265
+ { and: node.contents.map { |n| n.accept(visitor) } }
266
+ end
267
+ when Nodes::And
268
+ { and: node.set.accept(visitor) }
269
+
270
+ when Nodes::Foreign
271
+ { has_child: Hash[[[:type, node.left.accept(visitor)], node.right.accept(visitor)]] }
272
+
273
+ else
274
+ fail ArgumentError, "ES TermFilter Visitor cannot visit: #{node.class.name}"
275
+ end
276
+ end
277
+
278
+ private
279
+
280
+ attr_reader :visitor
281
+ end
205
282
  end
206
283
  end
207
284
 
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'msfl_visitors'
3
- s.version = '1.1.0.dev2'
3
+ s.version = '1.1.0.dev3'
4
4
  s.date = '2015-05-28'
5
5
  s.summary = "Convert MSFL to other forms"
6
6
  s.description = "Visitor pattern approach to converting MSFL to other forms."
@@ -0,0 +1,434 @@
1
+ require 'spec_helper'
2
+
3
+ describe MSFLVisitors::Visitor do
4
+
5
+ let(:node) { fail ArgumentError, "You must define the node variable in each scope." }
6
+
7
+ let(:visitor) { described_class.new }
8
+
9
+ let(:left) { MSFLVisitors::Nodes::Field.new "lhs" }
10
+
11
+ let(:right) { MSFLVisitors::Nodes::Word.new "rhs" }
12
+
13
+ subject(:result) { node.accept visitor }
14
+
15
+ context "when using the ESTermFilter visitor" do
16
+
17
+ before { visitor.mode = :es_term }
18
+
19
+ context "when visiting" do
20
+
21
+ describe "an unsupported node" do
22
+
23
+ class UnsupportedNode
24
+
25
+ def accept(visitor)
26
+ visitor.visit self
27
+ end
28
+ end
29
+
30
+ let(:node) { UnsupportedNode.new }
31
+
32
+ it "raises an ArgumentError" do
33
+ expect { subject }.to raise_error ArgumentError
34
+ end
35
+ end
36
+
37
+ describe "a Partial node" do
38
+
39
+ let(:node) { MSFLVisitors::Nodes::Partial.new given_node, named_value }
40
+
41
+ let(:given_node) { MSFLVisitors::Nodes::Given.new [given_equal_node] }
42
+
43
+ let(:given_equal_node) { MSFLVisitors::Nodes::Equal.new given_field_node, given_value_node }
44
+
45
+ let(:given_field_node) { MSFLVisitors::Nodes::Field.new :make }
46
+
47
+ let(:given_value_node) { MSFLVisitors::Nodes::Word.new "Toyota" }
48
+
49
+
50
+ let(:named_value) { MSFLVisitors::Nodes::NamedValue.new MSFLVisitors::Nodes::Word.new("partial"), explicit_filter_node }
51
+
52
+ let(:explicit_filter_node) { MSFLVisitors::Nodes::ExplicitFilter.new [greater_than_node] }
53
+
54
+ let(:greater_than_node) { MSFLVisitors::Nodes::GreaterThan.new field_node, value_node }
55
+
56
+ let(:field_node) { MSFLVisitors::Nodes::Field.new :age }
57
+
58
+ let(:value_node) { MSFLVisitors::Nodes::Number.new 10 }
59
+
60
+
61
+ subject { visitor.visit_tree node }
62
+
63
+ it "results in the appropriate clause" do
64
+ exp = [{
65
+ clause: {
66
+ given: {
67
+ filter: {
68
+ term: { make: "Toyota" }
69
+ },
70
+ aggs: {
71
+ partial: {
72
+ filter: { range: { age: { gt: 10 }}}
73
+ }
74
+ }
75
+ }
76
+ }
77
+ }]
78
+ expect(subject).to eq exp
79
+ end
80
+ end
81
+
82
+ describe "a Given node" do
83
+
84
+ let(:node) { MSFLVisitors::Nodes::Given.new [given_equal_node] }
85
+
86
+ let(:given_equal_node) { MSFLVisitors::Nodes::Equal.new given_field_node, given_value_node }
87
+
88
+ let(:given_field_node) { MSFLVisitors::Nodes::Field.new :make }
89
+
90
+ let(:given_value_node) { MSFLVisitors::Nodes::Word.new "Toyota" }
91
+
92
+
93
+ it "results in: [:filter, { term: { make: \"Toyota\" } }]" do
94
+ expect(subject).to eq([:filter, { term: { make: "Toyota" } }])
95
+ end
96
+ end
97
+
98
+ describe "a Foreign node" do
99
+
100
+ let(:node) { MSFLVisitors::Nodes::Foreign.new dataset_node, filter_node }
101
+
102
+ let(:dataset_node) { MSFLVisitors::Nodes::Dataset.new "person" }
103
+
104
+ let(:filter_node) { MSFLVisitors::Nodes::ExplicitFilter.new [equal_node] }
105
+
106
+ let(:equal_node) { MSFLVisitors::Nodes::Equal.new left, right }
107
+
108
+ let(:left) { MSFLVisitors::Nodes::Field.new :age }
109
+
110
+ let(:right) { MSFLVisitors::Nodes::Number.new 25 }
111
+
112
+ it "results in: { has_child: { type: \"person\", filter: { term: { age: 25 } } } }" do
113
+ expect(subject).to eq({ has_child: { type: "person", filter: { term: { age: 25 } } } })
114
+ end
115
+ end
116
+
117
+ describe "a Containment node" do
118
+
119
+ let(:node) { MSFLVisitors::Nodes::Containment.new field, values }
120
+
121
+ let(:values) { MSFLVisitors::Nodes::Set.new(MSFL::Types::Set.new([item_one, item_two, item_three])) }
122
+
123
+ let(:item_one) { MSFLVisitors::Nodes::Word.new "item_one" }
124
+
125
+ let(:item_two) { MSFLVisitors::Nodes::Word.new "item_two" }
126
+
127
+ let(:item_three) { MSFLVisitors::Nodes::Word.new "item_three" }
128
+
129
+ let(:field) { left }
130
+
131
+ it "results in: { terms: { lhs: [\"item_one\", \"item_two\", \"item_three\"] } }" do
132
+ expect(subject).to eq({ terms: { lhs: ["item_one", "item_two", "item_three"] } })
133
+ end
134
+ end
135
+
136
+ describe "a Set node" do
137
+
138
+ let(:node) { MSFLVisitors::Nodes::Set.new values }
139
+
140
+ let(:values) { MSFL::Types::Set.new([item_one, item_two]) }
141
+
142
+ let(:item_one) { MSFLVisitors::Nodes::Word.new "item_one" }
143
+
144
+ let(:item_two) { MSFLVisitors::Nodes::Word.new "item_two" }
145
+
146
+ it "results in: [\"item_one\", \"item_two\"]" do
147
+ expect(result).to eq ["item_one", "item_two"]
148
+ end
149
+ end
150
+
151
+ describe "an Equal node" do
152
+
153
+ let(:node) { MSFLVisitors::Nodes::Equal.new left, right }
154
+
155
+ it "results in: { term: { lhs: \"rhs\" } }" do
156
+ expect(result).to eq({ term: { lhs: "rhs" } })
157
+ end
158
+ end
159
+
160
+ describe "a GreaterThan node" do
161
+
162
+ let(:node) { MSFLVisitors::Nodes::GreaterThan.new left, right }
163
+
164
+ let(:right) { MSFLVisitors::Nodes::Number.new 1000 }
165
+
166
+ it "results in: { range: { lhs: { gt: 1000 } } }" do
167
+ expect(result).to eq({ range: { lhs: { gt: 1000 } } })
168
+ end
169
+ end
170
+
171
+ describe "a GreaterThanEqual node" do
172
+
173
+ let(:node) { MSFLVisitors::Nodes::GreaterThanEqual.new left, right }
174
+
175
+ let(:right) { MSFLVisitors::Nodes::Number.new 10.52 }
176
+
177
+ it "results in: { range: { lhs: { gte: 10.52 } } }" do
178
+ expect(result).to eq({ range: { lhs: { gte: 10.52 } } })
179
+ end
180
+ end
181
+
182
+ describe "a LessThan node" do
183
+
184
+ let(:node) { MSFLVisitors::Nodes::LessThan.new left, right }
185
+
186
+ let(:right) { MSFLVisitors::Nodes::Number.new 133.7 }
187
+
188
+ it "returns: { range: { lhs: { lt: 133.7 } } }" do
189
+ expect(result).to eq({ range: { lhs: { lt: 133.7 } } })
190
+ end
191
+ end
192
+
193
+ describe "a LessThanEqual node" do
194
+
195
+ let(:node) { MSFLVisitors::Nodes::LessThanEqual.new left, right }
196
+
197
+ let(:right) { MSFLVisitors::Nodes::Date.new Date.today }
198
+
199
+ it "returns: { range: { lhs: { lte: \"#{Date.today}\" } } }" do
200
+ expect(result).to eq({ range: { lhs: { lte: "#{Date.today}" } } })
201
+ end
202
+ end
203
+
204
+ describe "a Filter node" do
205
+
206
+ let(:node) { MSFLVisitors::Nodes::Filter.new filtered_nodes }
207
+
208
+ let(:filtered_nodes) do
209
+ [
210
+ MSFLVisitors::Nodes::GreaterThanEqual.new(
211
+ MSFLVisitors::Nodes::Field.new(:value),
212
+ MSFLVisitors::Nodes::Number.new(1000))
213
+ ]
214
+ end
215
+
216
+ it "returns: { range: { value: { gte: 1000 } } }" do
217
+ expect(result).to eq({ range: { value: { gte: 1000 } } })
218
+ end
219
+
220
+ context "when the filter has multiple children" do
221
+
222
+ let(:filtered_nodes) do
223
+ [
224
+ MSFLVisitors::Nodes::Equal.new(
225
+ MSFLVisitors::Nodes::Field.new(:make),
226
+ MSFLVisitors::Nodes::Word.new("Chevy")
227
+ ),
228
+ MSFLVisitors::Nodes::GreaterThanEqual.new(
229
+ MSFLVisitors::Nodes::Field.new(:value),
230
+ MSFLVisitors::Nodes::Number.new(1000))
231
+ ]
232
+ end
233
+
234
+ it "returns: { and: [{ term: { make: \"Chevy\" } },{ range: { value: { gte: 1000 } } }] }" do
235
+ expect(result).to eq({ and: [{ term: { make: "Chevy" } },{ range: { value: { gte: 1000 } } }] })
236
+ end
237
+ end
238
+ end
239
+
240
+ describe "an And node" do
241
+
242
+ let(:first_field) { MSFLVisitors::Nodes::Field.new "first_field" }
243
+
244
+ let(:first_value) { MSFLVisitors::Nodes::Word.new "first_word" }
245
+
246
+ let(:first) { MSFLVisitors::Nodes::Equal.new(first_field, first_value) }
247
+
248
+ let(:second_field) { MSFLVisitors::Nodes::Field.new "second_field" }
249
+
250
+ let(:second_value) { MSFLVisitors::Nodes::Word.new "second_word" }
251
+
252
+ let(:second) { MSFLVisitors::Nodes::Equal.new(second_field, second_value) }
253
+
254
+ let(:third_field) { MSFLVisitors::Nodes::Field.new "third_field" }
255
+
256
+ let(:third_value) { MSFLVisitors::Nodes::Word.new "third_word" }
257
+
258
+ let(:third) { MSFLVisitors::Nodes::Equal.new(third_field, third_value) }
259
+
260
+ let(:node) { MSFLVisitors::Nodes::And.new(set_node) }
261
+
262
+ context "when the And node has zero items" do
263
+
264
+ let(:set_node) { MSFLVisitors::Nodes::Set.new [] }
265
+
266
+ it "returns: { and: [] }" do
267
+ expect(result).to eq({ and: [] })
268
+ end
269
+ end
270
+
271
+ context "when the node has one item" do
272
+
273
+ let(:set_node) { MSFLVisitors::Nodes::Set.new [first] }
274
+
275
+ it "returns: { and: [{ term: { first_field: \"first_word\" }] }" do
276
+ expect(result).to eq({ and: [{ term: { first_field: "first_word" } }] })
277
+ end
278
+ end
279
+
280
+ context "when the node has two items" do
281
+
282
+ let(:set_node) { MSFLVisitors::Nodes::Set.new [first, second] }
283
+
284
+ it "returns: { and: [{ term: { first_field: \"first_word\" } },{ term: { second_field: \"second_word\" } }] }" do
285
+ expect(result).to eq({ and: [{ term: { first_field: "first_word" }}, { term: { second_field: "second_word" } }] })
286
+ end
287
+ end
288
+
289
+ context "when the node has three items" do
290
+
291
+ let(:set_node) { MSFLVisitors::Nodes::Set.new [first, second, third] }
292
+
293
+ it "returns: { and: [{ term: { first_field: \"first_word\" } },{ term: { second_field: \"second_word\" } },{ term: { third_field: \"third_word\" } }] }" do
294
+ expect(result).to eq({ and: [{ term: { first_field: "first_word" } },{ term: { second_field: "second_word" } },{ term: { third_field: "third_word" } }] })
295
+ end
296
+ end
297
+
298
+ context "when one of the node's items is a containment node" do
299
+
300
+ let(:node) do
301
+ MSFLVisitors::Nodes::And.new(
302
+ MSFLVisitors::Nodes::Set.new(
303
+ [
304
+ MSFLVisitors::Nodes::Filter.new(
305
+ [
306
+ MSFLVisitors::Nodes::Containment.new(
307
+ MSFLVisitors::Nodes::Field.new(:make),
308
+ MSFLVisitors::Nodes::Set.new(
309
+ [
310
+ MSFLVisitors::Nodes::Word.new("Honda"),
311
+ MSFLVisitors::Nodes::Word.new("Chevy"),
312
+ MSFLVisitors::Nodes::Word.new("Volvo")
313
+ ]
314
+ )
315
+ )
316
+ ]
317
+ ),
318
+ MSFLVisitors::Nodes::Filter.new(
319
+ [
320
+ MSFLVisitors::Nodes::GreaterThanEqual.new(
321
+ MSFLVisitors::Nodes::Field.new(:value),
322
+ MSFLVisitors::Nodes::Number.new(1000)
323
+ )
324
+ ]
325
+ )
326
+ ]
327
+ )
328
+ )
329
+ end
330
+ it "returns: { and: [{ terms: { make: [\"Honda\",\"Chevy\",\"Volvo\"]} }, { range: { value: { gte: 1000 } } }] }" do
331
+ expected = { and: [{ terms: { make: ["Honda", "Chevy", "Volvo"]} }, { range: { value: { gte: 1000 } } }] }
332
+ expect(result).to eq expected
333
+ end
334
+ end
335
+ end
336
+
337
+ describe "value nodes" do
338
+ describe "a Boolean node" do
339
+
340
+ let(:node) { MSFLVisitors::Nodes::Boolean.new value }
341
+
342
+ subject(:result) { node.accept visitor }
343
+
344
+ context "with a value of true" do
345
+
346
+ let(:value) { true }
347
+
348
+ it "returns: true" do
349
+ expect(result).to eq true
350
+ end
351
+ end
352
+
353
+ context "with a value of false" do
354
+
355
+ let(:value) { false }
356
+
357
+ it "returns: false" do
358
+ expect(result).to eq false
359
+ end
360
+ end
361
+ end
362
+
363
+ describe "a Word node" do
364
+
365
+ let(:word) { "node_content" }
366
+
367
+ let(:node) { MSFLVisitors::Nodes::Word.new word }
368
+
369
+ it "returns: the literal string" do
370
+ expect(result).to eq "#{word}"
371
+ end
372
+ end
373
+ end
374
+
375
+ describe "range value nodes" do
376
+
377
+ subject(:result) { node.accept visitor }
378
+
379
+ describe "a Date node" do
380
+
381
+ let(:today) { Date.today }
382
+
383
+ let(:node) { MSFLVisitors::Nodes::Date.new today }
384
+
385
+ it "returns: the date using iso8601 formatting" do
386
+ expect(result).to eq "#{today.iso8601}"
387
+ end
388
+ end
389
+
390
+ describe "a Time node" do
391
+
392
+ let(:now) { Time.now }
393
+
394
+ let(:node) { MSFLVisitors::Nodes::Time.new now }
395
+
396
+ it "returns: the date using iso8601 formatting" do
397
+ expect(result).to eq "#{now.iso8601}"
398
+ end
399
+ end
400
+
401
+ describe "a DateTime node" do
402
+
403
+ let(:now) { DateTime.now }
404
+
405
+ let(:node) { MSFLVisitors::Nodes::DateTime.new now }
406
+
407
+ it "returns: the date and time using iso8601 formatting" do
408
+ expect(result).to eq "#{now.iso8601}"
409
+ end
410
+ end
411
+
412
+ describe "a Number node" do
413
+
414
+ let(:number) { 123 }
415
+
416
+ let(:node) { MSFLVisitors::Nodes::Number.new number }
417
+
418
+ it "returns: 123" do
419
+ expect(result).to eq number
420
+ end
421
+
422
+ context "when the number is a float" do
423
+
424
+ let(:number) { 123.456 }
425
+
426
+ it "returns: the number with the same precision" do
427
+ expect(result).to eq number
428
+ end
429
+ end
430
+ end
431
+ end
432
+ end
433
+ end
434
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: msfl_visitors
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0.dev2
4
+ version: 1.1.0.dev3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Courtland Caldwell
@@ -154,6 +154,7 @@ files:
154
154
  - spec/msfl_visitors/parsers/msfl_parser_spec.rb
155
155
  - spec/msfl_visitors/visitor_spec.rb
156
156
  - spec/msfl_visitors/visitors/chewy_term_filter_spec.rb
157
+ - spec/msfl_visitors/visitors/es_term_filter_spec.rb
157
158
  - spec/msfl_visitors_spec.rb
158
159
  - spec/spec_helper.rb
159
160
  homepage: https://github.com/Referly/msfl_visitors
@@ -186,6 +187,7 @@ test_files:
186
187
  - spec/msfl_visitors/parsers/msfl_parser_spec.rb
187
188
  - spec/msfl_visitors/visitor_spec.rb
188
189
  - spec/msfl_visitors/visitors/chewy_term_filter_spec.rb
190
+ - spec/msfl_visitors/visitors/es_term_filter_spec.rb
189
191
  - spec/msfl_visitors_spec.rb
190
192
  - spec/spec_helper.rb
191
193
  has_rdoc: