sparkql 1.2.5 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -13
- data/.rubocop.yml +111 -0
- data/.ruby-version +1 -1
- data/CHANGELOG.md +16 -0
- data/Gemfile +1 -2
- data/Rakefile +2 -3
- data/VERSION +1 -1
- data/lib/sparkql/errors.rb +68 -71
- data/lib/sparkql/evaluator.rb +13 -9
- data/lib/sparkql/expression_resolver.rb +2 -3
- data/lib/sparkql/expression_state.rb +7 -9
- data/lib/sparkql/function_resolver.rb +777 -676
- data/lib/sparkql/geo/record_circle.rb +1 -1
- data/lib/sparkql/lexer.rb +54 -56
- data/lib/sparkql/parser.rb +35 -35
- data/lib/sparkql/parser_compatibility.rb +98 -77
- data/lib/sparkql/parser_tools.rb +159 -139
- data/lib/sparkql/token.rb +25 -25
- data/lib/sparkql/version.rb +1 -1
- data/sparkql.gemspec +20 -18
- data/test/unit/errors_test.rb +4 -5
- data/test/unit/evaluator_test.rb +15 -16
- data/test/unit/expression_state_test.rb +14 -15
- data/test/unit/function_resolver_test.rb +445 -203
- data/test/unit/geo/record_circle_test.rb +2 -2
- data/test/unit/lexer_test.rb +15 -16
- data/test/unit/parser_compatability_test.rb +177 -151
- data/test/unit/parser_test.rb +133 -99
- metadata +36 -35
data/test/unit/parser_test.rb
CHANGED
@@ -5,10 +5,10 @@ class ParserTest < Test::Unit::TestCase
|
|
5
5
|
|
6
6
|
def test_simple
|
7
7
|
@parser = Parser.new
|
8
|
-
parse 'Test Eq 10',10.to_s
|
9
|
-
parse 'Test Eq 10.0',10.0.to_s
|
10
|
-
parse 'Test Eq true',true.to_s
|
11
|
-
parse "Test Eq 'false'","'false'"
|
8
|
+
parse 'Test Eq 10', 10.to_s
|
9
|
+
parse 'Test Eq 10.0', 10.0.to_s
|
10
|
+
parse 'Test Eq true', true.to_s
|
11
|
+
parse "Test Eq 'false'", "'false'"
|
12
12
|
end
|
13
13
|
|
14
14
|
def test_conjunction
|
@@ -26,7 +26,7 @@ class ParserTest < Test::Unit::TestCase
|
|
26
26
|
assert_equal 11.to_s, expression.last[:value]
|
27
27
|
assert_equal 'Not', expression.last[:conjunction]
|
28
28
|
end
|
29
|
-
|
29
|
+
|
30
30
|
def test_tough_conjunction
|
31
31
|
@parser = Parser.new
|
32
32
|
expression = @parser.parse('Test Eq 10 Or Test Ne 11 And Test Ne 9')
|
@@ -52,50 +52,50 @@ class ParserTest < Test::Unit::TestCase
|
|
52
52
|
def test_multiples
|
53
53
|
@parser = Parser.new
|
54
54
|
expression = @parser.parse('(Test Eq 10,11,12)').first
|
55
|
-
assert_equal [10.to_s,11.to_s,12.to_s], expression[:value]
|
55
|
+
assert_equal [10.to_s, 11.to_s, 12.to_s], expression[:value]
|
56
56
|
assert_equal '10,11,12', expression[:condition]
|
57
57
|
end
|
58
|
-
|
58
|
+
|
59
59
|
def test_invalid_syntax
|
60
60
|
@parser = Parser.new
|
61
61
|
expression = @parser.parse('Test Eq DERP')
|
62
62
|
assert @parser.errors?, "Should be nil: #{expression}"
|
63
63
|
end
|
64
|
-
|
64
|
+
|
65
65
|
def test_nesting
|
66
66
|
assert_nesting(
|
67
67
|
"City Eq 'Fargo' Or (BathsFull Eq 1 Or BathsFull Eq 2) Or City Eq 'Moorhead' Or City Eq 'Dilworth'",
|
68
|
-
[0,1,1,0,0]
|
68
|
+
[0, 1, 1, 0, 0]
|
69
69
|
)
|
70
70
|
end
|
71
|
-
|
71
|
+
|
72
72
|
def test_nesting_and_functions
|
73
73
|
# Nesting with a function thrown in. Yes, this was a problem.
|
74
74
|
assert_nesting(
|
75
75
|
"City Eq 'Fargo' Or (BathsFull Eq 1 And Location Eq rectangle('35.12 -68.33, 35.13 -68.32')) Or Location Eq radius('35.12 -68.33',10.0) Or City Eq 'Dilworth'",
|
76
|
-
[0,1,1,0,0]
|
76
|
+
[0, 1, 1, 0, 0]
|
77
77
|
)
|
78
78
|
end
|
79
79
|
|
80
80
|
def test_multilevel_nesting
|
81
81
|
assert_nesting(
|
82
82
|
"(City Eq 'Fargo' And (BathsFull Eq 1 Or BathsFull Eq 2)) Or City Eq 'Moorhead' Or City Eq 'Dilworth'",
|
83
|
-
[1,2,2,0,0]
|
83
|
+
[1, 2, 2, 0, 0]
|
84
84
|
)
|
85
|
-
|
85
|
+
|
86
86
|
# API-629
|
87
87
|
assert_nesting(
|
88
88
|
"((MlsStatus Eq 'A') Or (MlsStatus Eq 'D' And CloseDate Ge 2011-05-17)) And ListPrice Ge 150000.0 And PropertyType Eq 'A'",
|
89
|
-
[2,2,2,0,0],
|
90
|
-
[2,3,3,0,0]
|
89
|
+
[2, 2, 2, 0, 0],
|
90
|
+
[2, 3, 3, 0, 0]
|
91
91
|
)
|
92
92
|
assert_nesting(
|
93
93
|
"ListPrice Ge 150000.0 And PropertyType Eq 'A' And ((MlsStatus Eq 'A') Or (MlsStatus Eq 'D' And CloseDate Ge 2011-05-17))",
|
94
|
-
[0,0,2,2,2],
|
95
|
-
[0,0,2,3,3]
|
94
|
+
[0, 0, 2, 2, 2],
|
95
|
+
[0, 0, 2, 3, 3]
|
96
96
|
)
|
97
97
|
end
|
98
|
-
|
98
|
+
|
99
99
|
def test_bad_queries
|
100
100
|
filter = "City IsLikeA 'Town'"
|
101
101
|
@parser = Parser.new
|
@@ -105,8 +105,8 @@ class ParserTest < Test::Unit::TestCase
|
|
105
105
|
end
|
106
106
|
|
107
107
|
def test_function_months
|
108
|
-
|
109
|
-
|
108
|
+
t = Time.new(2014, 1, 5, 0, 0, 0, 0)
|
109
|
+
Time.expects(:now).returns(t)
|
110
110
|
@parser = Parser.new
|
111
111
|
expressions = @parser.parse "ExpirationDate Gt months(-3)"
|
112
112
|
assert !@parser.errors?, "errors :( #{@parser.errors.inspect}"
|
@@ -115,8 +115,8 @@ class ParserTest < Test::Unit::TestCase
|
|
115
115
|
end
|
116
116
|
|
117
117
|
def test_function_years
|
118
|
-
|
119
|
-
|
118
|
+
t = Time.new(2014, 1, 5, 0, 0, 0, 0)
|
119
|
+
Time.expects(:now).returns(t)
|
120
120
|
@parser = Parser.new
|
121
121
|
expressions = @parser.parse "SoldDate Lt years(2)"
|
122
122
|
assert !@parser.errors?, "errors :( #{@parser.errors.inspect}"
|
@@ -125,19 +125,23 @@ class ParserTest < Test::Unit::TestCase
|
|
125
125
|
end
|
126
126
|
|
127
127
|
def test_function_days
|
128
|
-
|
129
|
-
|
130
|
-
filter = "OriginalEntryTimestamp Ge days(-7)"
|
128
|
+
t = Time.new(2021, 2, 22, 0, 0, 0, 0)
|
129
|
+
Time.expects(:now).returns(t)
|
131
130
|
@parser = Parser.new
|
132
|
-
expressions = @parser.parse(
|
133
|
-
assert !@parser.errors
|
134
|
-
assert_equal
|
135
|
-
|
136
|
-
|
131
|
+
expressions = @parser.parse "ExpirationDate Gt days(10)"
|
132
|
+
assert !@parser.errors?
|
133
|
+
assert_equal "2021-03-04", expressions.first[:value]
|
134
|
+
assert_equal 'days(10)', expressions.first[:condition]
|
135
|
+
end
|
137
136
|
|
138
|
-
|
139
|
-
|
140
|
-
|
137
|
+
def test_function_weekdays
|
138
|
+
t = Time.new(2021, 2, 22, 0, 0, 0, 0)
|
139
|
+
Time.expects(:now).returns(t)
|
140
|
+
@parser = Parser.new
|
141
|
+
expressions = @parser.parse "ExpirationDate Gt weekdays(10)"
|
142
|
+
assert !@parser.errors?
|
143
|
+
assert_equal "2021-03-08", expressions.first[:value]
|
144
|
+
assert_equal 'weekdays(10)', expressions.first[:condition]
|
141
145
|
end
|
142
146
|
|
143
147
|
def test_function_now
|
@@ -148,8 +152,8 @@ class ParserTest < Test::Unit::TestCase
|
|
148
152
|
assert !@parser.errors?, "errors #{@parser.errors.inspect}"
|
149
153
|
assert_equal 'now()', expressions.first[:condition]
|
150
154
|
test_time = Time.parse(expressions.first[:value])
|
151
|
-
assert
|
152
|
-
assert -
|
155
|
+
assert test_time - start < 5, "Time range off by more than five seconds #{test_time - start}"
|
156
|
+
assert(test_time - start > -5, "Time range off by more than five seconds #{test_time - start}")
|
153
157
|
end
|
154
158
|
|
155
159
|
def test_function_range
|
@@ -174,7 +178,8 @@ class ParserTest < Test::Unit::TestCase
|
|
174
178
|
assert_equal 'indexof', expression[:field_manipulations][:function_name]
|
175
179
|
assert_equal :function, expression[:field_manipulations][:type]
|
176
180
|
assert_equal :integer, expression[:field_manipulations][:return_type]
|
177
|
-
assert_equal
|
181
|
+
assert_equal(%w[City 4131800000000],
|
182
|
+
expression[:field_manipulations][:args].map { |v| v[:value] })
|
178
183
|
end
|
179
184
|
|
180
185
|
test 'add' do
|
@@ -307,7 +312,7 @@ class ParserTest < Test::Unit::TestCase
|
|
307
312
|
assert_equal 'days(-7)', expressions.first[:condition]
|
308
313
|
assert_equal([-7], expressions.first[:function_parameters])
|
309
314
|
end
|
310
|
-
|
315
|
+
|
311
316
|
test "function rangeable " do
|
312
317
|
filter = "OriginalEntryTimestamp Bt days(-7),days(-1)"
|
313
318
|
@parser = Parser.new
|
@@ -388,10 +393,40 @@ class ParserTest < Test::Unit::TestCase
|
|
388
393
|
test "invalid regex" do
|
389
394
|
filter = "ParcelNumber Eq regex('[1234', '')"
|
390
395
|
@parser = Parser.new
|
391
|
-
|
396
|
+
@parser.parse(filter)
|
392
397
|
assert @parser.errors?, "Parser error expected due to invalid regex"
|
393
398
|
end
|
394
399
|
|
400
|
+
test "dayofyear function parses" do
|
401
|
+
filter = "dayofyear(DatetimeField) Eq 2012"
|
402
|
+
@parser = Parser.new
|
403
|
+
expressions = @parser.parse(filter)
|
404
|
+
assert !@parser.errors?, @parser.errors.inspect
|
405
|
+
function = expressions.first[:field_manipulations]
|
406
|
+
assert_equal 'dayofyear', function[:function_name]
|
407
|
+
assert_equal 'DatetimeField', function[:function_parameters].first
|
408
|
+
end
|
409
|
+
|
410
|
+
test "dayofweek function parses" do
|
411
|
+
filter = "dayofweek(DatetimeField) Eq 7"
|
412
|
+
@parser = Parser.new
|
413
|
+
expressions = @parser.parse(filter)
|
414
|
+
assert !@parser.errors?, @parser.errors.inspect
|
415
|
+
function = expressions.first[:field_manipulations]
|
416
|
+
assert_equal 'dayofweek', function[:function_name]
|
417
|
+
assert_equal 'DatetimeField', function[:function_parameters].first
|
418
|
+
end
|
419
|
+
|
420
|
+
test "weekdays function resolves" do
|
421
|
+
filter = "DatetimeField Eq weekdays(10)"
|
422
|
+
@parser = Parser.new
|
423
|
+
expressions = @parser.parse(filter)
|
424
|
+
assert !@parser.errors?, @parser.errors.inspect
|
425
|
+
function = expressions.first
|
426
|
+
assert_equal 'weekdays', function[:function_name]
|
427
|
+
assert_equal 10, function[:function_parameters].first
|
428
|
+
end
|
429
|
+
|
395
430
|
test "allow timezone offsets" do
|
396
431
|
values = [
|
397
432
|
"2013-07-26",
|
@@ -418,7 +453,7 @@ class ParserTest < Test::Unit::TestCase
|
|
418
453
|
assert_equal expressions.first[:value], value, "#{value} failed"
|
419
454
|
end
|
420
455
|
end
|
421
|
-
|
456
|
+
|
422
457
|
test "Location Eq polygon()" do
|
423
458
|
filter = "Location Eq polygon('35.12 -68.33, 35.13 -68.33, 35.13 -68.32, 35.12 -68.32')"
|
424
459
|
@parser = Parser.new
|
@@ -426,7 +461,7 @@ class ParserTest < Test::Unit::TestCase
|
|
426
461
|
assert !@parser.errors?, "errors #{@parser.errors.inspect}"
|
427
462
|
assert_equal "polygon('35.12 -68.33, 35.13 -68.33, 35.13 -68.32, 35.12 -68.32')", expressions.first[:condition]
|
428
463
|
assert_equal :shape, expressions.first[:type]
|
429
|
-
assert_equal [[-68.33, 35.12], [-68.33, 35.13], [-68.32,35.13], [-68.32,35.12],[-68.33, 35.12]], expressions.first[:value].to_coordinates.first, "#{expressions.first[:value].inspect} "
|
464
|
+
assert_equal [[-68.33, 35.12], [-68.33, 35.13], [-68.32, 35.13], [-68.32, 35.12], [-68.33, 35.12]], expressions.first[:value].to_coordinates.first, "#{expressions.first[:value].inspect} "
|
430
465
|
end
|
431
466
|
|
432
467
|
test "Location Eq linestring()" do
|
@@ -437,7 +472,6 @@ class ParserTest < Test::Unit::TestCase
|
|
437
472
|
assert_equal "linestring('35.12 -68.33, 35.13 -68.33')", expressions.first[:condition]
|
438
473
|
assert_equal :shape, expressions.first[:type]
|
439
474
|
assert_equal [[-68.33, 35.12], [-68.33, 35.13]], expressions.first[:value].to_coordinates, "#{expressions.first[:value].inspect} "
|
440
|
-
|
441
475
|
end
|
442
476
|
|
443
477
|
test "Location Eq rectangle()" do
|
@@ -446,7 +480,7 @@ class ParserTest < Test::Unit::TestCase
|
|
446
480
|
expressions = @parser.parse(filter)
|
447
481
|
assert !@parser.errors?, "errors #{@parser.errors.inspect}"
|
448
482
|
assert_equal :shape, expressions.first[:type]
|
449
|
-
assert_equal [[-68.33,35.12], [-68.32,35.12], [-68.32,35.13], [-68.33,35.13], [-68.33,35.12]], expressions.first[:value].to_coordinates.first, "#{expressions.first[:value].inspect} "
|
483
|
+
assert_equal [[-68.33, 35.12], [-68.32, 35.12], [-68.32, 35.13], [-68.33, 35.13], [-68.33, 35.12]], expressions.first[:value].to_coordinates.first, "#{expressions.first[:value].inspect} "
|
450
484
|
end
|
451
485
|
|
452
486
|
test "Location Eq radius()" do
|
@@ -473,26 +507,26 @@ class ParserTest < Test::Unit::TestCase
|
|
473
507
|
test "Location eq radius() error on invalid syntax" do
|
474
508
|
filter = "Location Eq radius('35.12,-68.33',1.0)"
|
475
509
|
@parser = Parser.new
|
476
|
-
|
510
|
+
@parser.parse(filter)
|
477
511
|
assert @parser.errors?, "Parser error expected due to comma between radius points"
|
478
512
|
end
|
479
|
-
|
513
|
+
|
480
514
|
test "Location ALL TOGETHER NOW" do
|
481
515
|
filter = "Location Eq linestring('35.12 -68.33, 35.13 -68.33') And Location Eq radius('35.12 -68.33',1.0) And Location Eq rectangle('35.12 -68.33, 35.13 -68.32') And Location Eq polygon('35.12 -68.33, 35.13 -68.33, 35.13 -68.32, 35.12 -68.32')"
|
482
516
|
@parser = Parser.new
|
483
517
|
expressions = @parser.parse(filter)
|
484
518
|
assert !@parser.errors?, "errors #{@parser.errors.inspect}"
|
485
|
-
assert_equal
|
519
|
+
assert_equal(%i[shape shape shape shape], expressions.map { |e| e[:type] })
|
486
520
|
end
|
487
|
-
|
521
|
+
|
488
522
|
def test_for_reserved_words_first_literals_second
|
489
523
|
["OrOrOr Eq true", "Equador Eq true", "Oregon Ge 10"].each do |filter|
|
490
524
|
@parser = Parser.new
|
491
|
-
|
525
|
+
@parser.parse(filter)
|
492
526
|
assert !@parser.errors?, "Filter '#{filter}' errors: #{@parser.errors.inspect}"
|
493
527
|
end
|
494
528
|
end
|
495
|
-
|
529
|
+
|
496
530
|
def test_custom_fields
|
497
531
|
filter = '"General Property Description"."Taxes" Lt 500.0'
|
498
532
|
@parser = Parser.new
|
@@ -502,48 +536,46 @@ class ParserTest < Test::Unit::TestCase
|
|
502
536
|
assert expressions.first[:custom_field], "Custom field expression #{expressions.inspect}"
|
503
537
|
assert_equal '500.0', expressions.first[:value], "Custom field expression #{expressions.inspect}"
|
504
538
|
end
|
505
|
-
|
539
|
+
|
506
540
|
def test_valid_custom_field_filters
|
507
541
|
['"General Property Description"."Taxes$" Lt 500.0',
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
].each do |filter|
|
542
|
+
'"General Property Desc\'"."Taxes" Lt 500.0',
|
543
|
+
'"General Property Description"."Taxes" Lt 500.0',
|
544
|
+
'"General \'Property\' Description"."Taxes" Lt 500.0',
|
545
|
+
'"General Property Description"."Taxes #" Lt 500.0',
|
546
|
+
'"General$Description"."Taxes" Lt 500.0',
|
547
|
+
'"Garage Type"."1" Eq true',
|
548
|
+
'" a "." b " Lt 500.0'].each do |filter|
|
516
549
|
@parser = Parser.new
|
517
|
-
|
550
|
+
@parser.parse(filter)
|
518
551
|
assert !@parser.errors?, "errors '#{filter}'\n#{@parser.errors.inspect}"
|
519
552
|
end
|
520
553
|
end
|
521
|
-
|
554
|
+
|
522
555
|
def test_invalid_custom_field_filters
|
523
556
|
['"$General Property Description"."Taxes" Lt 500.0',
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
].each do |filter|
|
557
|
+
'"General Property Description"."$Taxes" Lt 500.0',
|
558
|
+
'"General Property Description"."Tax.es" Lt 500.0',
|
559
|
+
'"General Property Description".".Taxes" Lt 500.0',
|
560
|
+
'"General Property Description".".Taxes"."SUB" Lt 500.0',
|
561
|
+
'"General.Description"."Taxes" Lt 500.0',
|
562
|
+
'""."" Lt 500.0'].each do |filter|
|
531
563
|
@parser = Parser.new
|
532
|
-
|
564
|
+
@parser.parse(filter)
|
533
565
|
assert @parser.errors?, "No errors? '#{filter}'\n#{@parser.inspect}"
|
534
566
|
end
|
535
567
|
end
|
536
568
|
|
537
569
|
def test_case_insensitve_ops_and_conjunctions
|
538
570
|
@parser = Parser.new
|
539
|
-
parse 'Test EQ 10',10.to_s
|
540
|
-
parse 'Test eq 10.0',10.0.to_s
|
541
|
-
parse 'Test eQ true',true.to_s
|
571
|
+
parse 'Test EQ 10', 10.to_s
|
572
|
+
parse 'Test eq 10.0', 10.0.to_s
|
573
|
+
parse 'Test eQ true', true.to_s
|
542
574
|
parse 'Test EQ 10 AND Test NE 11', 10.to_s
|
543
575
|
parse 'Test eq 10 or Test ne 11', 10.to_s
|
544
576
|
parse 'Test eq 10 NOT Test ne 11', 10.to_s
|
545
577
|
end
|
546
|
-
|
578
|
+
|
547
579
|
def test_null
|
548
580
|
@parser = Parser.new
|
549
581
|
parse 'Test Eq NULL', "NULL"
|
@@ -557,7 +589,7 @@ class ParserTest < Test::Unit::TestCase
|
|
557
589
|
end
|
558
590
|
end
|
559
591
|
end
|
560
|
-
|
592
|
+
|
561
593
|
def test_not_expression
|
562
594
|
@parser = Parser.new
|
563
595
|
expressions = @parser.parse('Test Lt 10 Not Test Eq 2')
|
@@ -578,7 +610,7 @@ class ParserTest < Test::Unit::TestCase
|
|
578
610
|
assert_equal "And", expression[:conjunction]
|
579
611
|
assert_equal expression[:level], expression[:unary_level]
|
580
612
|
end
|
581
|
-
|
613
|
+
|
582
614
|
def test_not_expression_group
|
583
615
|
@parser = Parser.new
|
584
616
|
expressions = @parser.parse('Not (Test Eq 10 Or Test Eq 11)')
|
@@ -603,7 +635,7 @@ class ParserTest < Test::Unit::TestCase
|
|
603
635
|
|
604
636
|
def test_not_not_expression
|
605
637
|
@parser = Parser.new
|
606
|
-
filter = "Not (Not ListPrice Eq 1) Not (Not BathsTotal Eq 2) And "
|
638
|
+
filter = "Not (Not ListPrice Eq 1) Not (Not BathsTotal Eq 2) And " \
|
607
639
|
"(Not TotalRooms Eq 3) Or (HasPool Eq true)"
|
608
640
|
|
609
641
|
expressions = @parser.parse(filter)
|
@@ -672,7 +704,6 @@ class ParserTest < Test::Unit::TestCase
|
|
672
704
|
assert_equal 1, e2[:level]
|
673
705
|
assert_equal "Not", e2[:conjunction]
|
674
706
|
assert_equal 1, e2[:conjunction_level]
|
675
|
-
|
676
707
|
end
|
677
708
|
|
678
709
|
def test_expression_conditions_attribute
|
@@ -704,7 +735,7 @@ class ParserTest < Test::Unit::TestCase
|
|
704
735
|
]
|
705
736
|
conditions.each do |condition|
|
706
737
|
@parser = Parser.new
|
707
|
-
|
738
|
+
@parser.parse("Test Eq #{condition}")
|
708
739
|
assert @parser.errors?, @parser.inspect
|
709
740
|
end
|
710
741
|
end
|
@@ -714,12 +745,12 @@ class ParserTest < Test::Unit::TestCase
|
|
714
745
|
"DateTimeField Bt 2013-07-26T10:22:15,2013-07-26T10:22:16",
|
715
746
|
"DateTimeField Bt 2013-07-26T10:22:15.422804-0300,2013-07-26T10:22:15.422805-0300",
|
716
747
|
"DateTimeField Bt 2013-07-26T10:22:15+0400,2013-07-26T10:22:16+0400"].each do |filter|
|
717
|
-
|
718
|
-
|
719
|
-
|
748
|
+
@parser = Parser.new
|
749
|
+
@parser.parse filter
|
750
|
+
assert !@parser.errors?, "Filter '#{filter}' failed: #{@parser.errors.first.inspect}"
|
720
751
|
end
|
721
752
|
end
|
722
|
-
|
753
|
+
|
723
754
|
def test_coercible_types
|
724
755
|
@parser = Parser.new
|
725
756
|
assert_equal :datetime, @parser.coercible_types(:date, :datetime)
|
@@ -780,7 +811,6 @@ class ParserTest < Test::Unit::TestCase
|
|
780
811
|
parser_errors("Field Eq -'Stringval'")
|
781
812
|
end
|
782
813
|
|
783
|
-
|
784
814
|
test "field negation" do
|
785
815
|
@parser = Parser.new
|
786
816
|
expressions = @parser.parse('-Test Eq 10')
|
@@ -827,7 +857,8 @@ class ParserTest < Test::Unit::TestCase
|
|
827
857
|
|
828
858
|
assert_equal 'round', expression[:field_manipulations][:function_name]
|
829
859
|
assert_equal :function, expression[:field_manipulations][:type]
|
830
|
-
assert_equal
|
860
|
+
assert_equal(['ListPrice'],
|
861
|
+
expression[:field_manipulations][:args].map { |v| v[:value] })
|
831
862
|
end
|
832
863
|
|
833
864
|
def test_ceiling_with_literal
|
@@ -887,7 +918,8 @@ class ParserTest < Test::Unit::TestCase
|
|
887
918
|
|
888
919
|
assert_equal 'floor', expression[:field_manipulations][:function_name]
|
889
920
|
assert_equal :function, expression[:field_manipulations][:type]
|
890
|
-
assert_equal
|
921
|
+
assert_equal(['ListPrice'],
|
922
|
+
expression[:field_manipulations][:args].map { |v| v[:value] })
|
891
923
|
end
|
892
924
|
|
893
925
|
def test_concat_with_field
|
@@ -898,12 +930,13 @@ class ParserTest < Test::Unit::TestCase
|
|
898
930
|
|
899
931
|
assert_equal :character, expression[:type]
|
900
932
|
assert_equal 'concat', expression[:field_function]
|
901
|
-
assert_equal([
|
933
|
+
assert_equal(%w[City b], expression[:args])
|
902
934
|
assert_equal("City", expression[:field])
|
903
935
|
|
904
936
|
assert_equal 'concat', expression[:field_manipulations][:function_name]
|
905
937
|
assert_equal :function, expression[:field_manipulations][:type]
|
906
|
-
assert_equal
|
938
|
+
assert_equal(%w[City b],
|
939
|
+
expression[:field_manipulations][:args].map { |v| v[:value] })
|
907
940
|
end
|
908
941
|
|
909
942
|
def test_concat_with_literal
|
@@ -915,7 +948,7 @@ class ParserTest < Test::Unit::TestCase
|
|
915
948
|
assert_equal 'concat', expression[:function_name]
|
916
949
|
assert_equal :character, expression[:type]
|
917
950
|
assert_equal "'ab'", expression[:value]
|
918
|
-
assert_equal [
|
951
|
+
assert_equal %w[a b], expression[:function_parameters]
|
919
952
|
end
|
920
953
|
|
921
954
|
def test_cast_with_field
|
@@ -930,7 +963,8 @@ class ParserTest < Test::Unit::TestCase
|
|
930
963
|
|
931
964
|
assert_equal 'cast', expression[:field_manipulations][:function_name]
|
932
965
|
assert_equal :function, expression[:field_manipulations][:type]
|
933
|
-
assert_equal
|
966
|
+
assert_equal(%w[ListPrice character],
|
967
|
+
expression[:field_manipulations][:args].map { |v| v[:value] })
|
934
968
|
end
|
935
969
|
|
936
970
|
def test_cast_with_invalid_type
|
@@ -1131,7 +1165,7 @@ class ParserTest < Test::Unit::TestCase
|
|
1131
1165
|
function2 = function1[:args].first
|
1132
1166
|
assert_equal :function, function2[:type]
|
1133
1167
|
assert_equal 'toupper', function2[:function_name]
|
1134
|
-
assert_equal({:
|
1168
|
+
assert_equal({ type: :field, value: "City" }, function2[:args].first)
|
1135
1169
|
end
|
1136
1170
|
|
1137
1171
|
test 'nested functions with multiple params' do
|
@@ -1142,12 +1176,12 @@ class ParserTest < Test::Unit::TestCase
|
|
1142
1176
|
function1 = expression[:field_manipulations]
|
1143
1177
|
assert_equal :function, function1[:type]
|
1144
1178
|
assert_equal 'concat', function1[:function_name]
|
1145
|
-
assert_equal({type: :character, value: 'b'}, function1[:args].last)
|
1179
|
+
assert_equal({ type: :character, value: 'b' }, function1[:args].last)
|
1146
1180
|
|
1147
1181
|
function2 = function1[:args].first
|
1148
1182
|
assert_equal :function, function2[:type]
|
1149
1183
|
assert_equal 'tolower', function2[:function_name]
|
1150
|
-
assert_equal({:
|
1184
|
+
assert_equal({ type: :field, value: "City" }, function2[:args].first)
|
1151
1185
|
end
|
1152
1186
|
|
1153
1187
|
test 'parse error with no field' do
|
@@ -1200,30 +1234,30 @@ class ParserTest < Test::Unit::TestCase
|
|
1200
1234
|
|
1201
1235
|
private
|
1202
1236
|
|
1203
|
-
def parser_errors(filter)
|
1237
|
+
def parser_errors(filter)
|
1204
1238
|
@parser = Parser.new
|
1205
1239
|
expression = @parser.parse(filter)
|
1206
1240
|
assert @parser.errors?, "Should find errors for '#{filter}': #{expression}"
|
1207
1241
|
end
|
1208
1242
|
|
1209
|
-
def parse(
|
1210
|
-
expressions = @parser.parse(
|
1211
|
-
assert !@parser.errors?, "Unexpected error parsing #{
|
1212
|
-
assert_equal
|
1243
|
+
def parse(query, value)
|
1244
|
+
expressions = @parser.parse(query)
|
1245
|
+
assert !@parser.errors?, "Unexpected error parsing #{query} #{@parser.errors.inspect}"
|
1246
|
+
assert_equal value, expressions.first[:value], "Expression #{expressions.inspect}"
|
1213
1247
|
assert !expressions.first[:custom_field], "Unexepected custom field #{expressions.inspect}"
|
1214
1248
|
end
|
1215
1249
|
|
1216
1250
|
# verify each expression in the query is at the right nesting level and group
|
1217
|
-
def assert_nesting(sparkql, levels=[], block_groups=nil)
|
1251
|
+
def assert_nesting(sparkql, levels = [], block_groups = nil)
|
1218
1252
|
block_groups = levels.clone if block_groups.nil?
|
1219
1253
|
parser = Parser.new
|
1220
1254
|
expressions = parser.parse(sparkql)
|
1221
1255
|
assert !parser.errors?, "Unexpected error parsing #{sparkql}: #{parser.errors.inspect}"
|
1222
1256
|
count = 0
|
1223
1257
|
expressions.each do |ex|
|
1224
|
-
assert_equal levels[count],
|
1225
|
-
assert_equal(block_groups[count],
|
1226
|
-
count +=1
|
1258
|
+
assert_equal levels[count], ex[:level], "Nesting level wrong for #{ex.inspect}"
|
1259
|
+
assert_equal(block_groups[count], ex[:block_group], "Nesting block group wrong for #{ex.inspect}")
|
1260
|
+
count += 1
|
1227
1261
|
end
|
1228
1262
|
end
|
1229
1263
|
end
|