skydb 0.2.1 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/sky +85 -0
- data/lib/ext/hash.rb +11 -0
- data/lib/ext/treetop.rb +19 -0
- data/lib/skydb.rb +10 -3
- data/lib/skydb/client.rb +92 -28
- data/lib/skydb/import.rb +7 -0
- data/lib/skydb/import/importer.rb +258 -0
- data/lib/skydb/import/transforms/sky.yml +20 -0
- data/lib/skydb/import/transforms/snowplow.yml +1 -0
- data/lib/skydb/import/translator.rb +119 -0
- data/lib/skydb/message.rb +17 -12
- data/lib/skydb/message/create_table.rb +64 -0
- data/lib/skydb/message/delete_table.rb +66 -0
- data/lib/skydb/message/get_table.rb +74 -0
- data/lib/skydb/message/lookup.rb +79 -0
- data/lib/skydb/property.rb +5 -5
- data/lib/skydb/query.rb +198 -0
- data/lib/skydb/query/after.rb +103 -0
- data/lib/skydb/query/ast/selection_field_syntax_node.rb +26 -0
- data/lib/skydb/query/ast/selection_fields_syntax_node.rb +16 -0
- data/lib/skydb/query/ast/selection_group_syntax_node.rb +16 -0
- data/lib/skydb/query/ast/selection_groups_syntax_node.rb +16 -0
- data/lib/skydb/query/selection.rb +268 -0
- data/lib/skydb/query/selection_field.rb +74 -0
- data/lib/skydb/query/selection_fields_grammar.treetop +46 -0
- data/lib/skydb/query/selection_fields_parse_error.rb +30 -0
- data/lib/skydb/query/selection_group.rb +57 -0
- data/lib/skydb/query/selection_groups_grammar.treetop +31 -0
- data/lib/skydb/query/selection_groups_parse_error.rb +30 -0
- data/lib/skydb/query/validation_error.rb +8 -0
- data/lib/skydb/table.rb +69 -0
- data/lib/skydb/version.rb +1 -1
- data/test/import/importer_test.rb +42 -0
- data/test/import/translator_test.rb +88 -0
- data/test/message/add_event_message_test.rb +1 -1
- data/test/message/add_property_message_test.rb +2 -2
- data/test/message/create_table_message_test.rb +34 -0
- data/test/message/delete_table_message_test.rb +34 -0
- data/test/message/get_table_message_test.rb +19 -0
- data/test/message/lookup_message_test.rb +27 -0
- data/test/message_test.rb +1 -1
- data/test/query/after_test.rb +71 -0
- data/test/query/selection_test.rb +273 -0
- data/test/query_test.rb +156 -0
- data/test/test_helper.rb +3 -0
- metadata +129 -3
@@ -0,0 +1,74 @@
|
|
1
|
+
class SkyDB
|
2
|
+
# The selection field is a single field in the selection part of the query.
|
3
|
+
# This can include a simple property or it can be the aggregation of a
|
4
|
+
# property. Fields can also be aliased to a different name that is returned.
|
5
|
+
# This is typically useful when naming aggregated fields.
|
6
|
+
class Query
|
7
|
+
class SelectionField
|
8
|
+
##########################################################################
|
9
|
+
#
|
10
|
+
# Constructor
|
11
|
+
#
|
12
|
+
##########################################################################
|
13
|
+
|
14
|
+
def initialize(options={})
|
15
|
+
self.expression = options[:expression]
|
16
|
+
self.alias_name = options[:alias_name]
|
17
|
+
self.aggregation_type = options[:aggregation_type]
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
##########################################################################
|
22
|
+
#
|
23
|
+
# Attributes
|
24
|
+
#
|
25
|
+
##########################################################################
|
26
|
+
|
27
|
+
# The name of the property to select.
|
28
|
+
attr_accessor :expression
|
29
|
+
|
30
|
+
# The field name that is actually returned.
|
31
|
+
attr_accessor :alias_name
|
32
|
+
|
33
|
+
# The type of the aggregation used to process the property.
|
34
|
+
attr_accessor :aggregation_type
|
35
|
+
|
36
|
+
# The final computed name of the field. It is named after the alias name
|
37
|
+
# if provided, otherwise defaults to the expression. If neither the
|
38
|
+
# expression or alias are provided then the aggregation type is used.
|
39
|
+
def target_name
|
40
|
+
return alias_name || expression || aggregation_type.to_s
|
41
|
+
end
|
42
|
+
|
43
|
+
# The string used to access the expression.
|
44
|
+
def accessor(options={})
|
45
|
+
prefix = options.delete(:prefix) || 'cursor.event.'
|
46
|
+
|
47
|
+
if ["action_id", "timestamp"].index(expression)
|
48
|
+
return "#{prefix}#{expression}"
|
49
|
+
else
|
50
|
+
return "#{prefix}#{expression}()"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
##########################################################################
|
56
|
+
#
|
57
|
+
# Methods
|
58
|
+
#
|
59
|
+
##########################################################################
|
60
|
+
|
61
|
+
####################################
|
62
|
+
# Validation
|
63
|
+
####################################
|
64
|
+
|
65
|
+
# Validates that the field is valid.
|
66
|
+
def validate!
|
67
|
+
# Expression must be present unless this is a COUNT().
|
68
|
+
if expression.to_s.length == 0 && aggregation_type != :count
|
69
|
+
raise SkyDB::Query::ValidationError.new("Invalid expression for selection field: '#{expression.to_s}'")
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
grammar SelectionFieldsGrammar
|
2
|
+
rule root
|
3
|
+
fields <SkyDB::Query::Ast::SelectionFieldsSyntaxNode>
|
4
|
+
end
|
5
|
+
|
6
|
+
# Matches all selection fields.
|
7
|
+
rule fields
|
8
|
+
ws field (ws ',' ws field)* ws
|
9
|
+
end
|
10
|
+
|
11
|
+
# Matches a single field name as well as additional options such as an alias
|
12
|
+
# or an aggregation method.
|
13
|
+
rule field
|
14
|
+
aggregation_type '(' ws ')' ws alias_name <SkyDB::Query::Ast::SelectionFieldSyntaxNode>
|
15
|
+
/
|
16
|
+
aggregation_type '(' ws ')' <SkyDB::Query::Ast::SelectionFieldSyntaxNode>
|
17
|
+
/
|
18
|
+
aggregation_type '(' ws expression ws ')' ws alias_name <SkyDB::Query::Ast::SelectionFieldSyntaxNode>
|
19
|
+
/
|
20
|
+
aggregation_type '(' ws expression ws ')' <SkyDB::Query::Ast::SelectionFieldSyntaxNode>
|
21
|
+
/
|
22
|
+
expression ws alias_name <SkyDB::Query::Ast::SelectionFieldSyntaxNode>
|
23
|
+
/
|
24
|
+
expression <SkyDB::Query::Ast::SelectionFieldSyntaxNode>
|
25
|
+
end
|
26
|
+
|
27
|
+
rule expression
|
28
|
+
identifier
|
29
|
+
end
|
30
|
+
|
31
|
+
rule alias_name
|
32
|
+
identifier
|
33
|
+
end
|
34
|
+
|
35
|
+
rule aggregation_type
|
36
|
+
([a-zA-Z]+)
|
37
|
+
end
|
38
|
+
|
39
|
+
rule identifier
|
40
|
+
([a-zA-Z_] [a-zA-Z0-9_]*)
|
41
|
+
end
|
42
|
+
|
43
|
+
rule ws
|
44
|
+
(' ' / "\t" / "\n" / "\r")*
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class SkyDB
|
2
|
+
class Query
|
3
|
+
class SelectionFieldsParseError < StandardError
|
4
|
+
##########################################################################
|
5
|
+
#
|
6
|
+
# Constructor
|
7
|
+
#
|
8
|
+
##########################################################################
|
9
|
+
|
10
|
+
def initialize(message, options={})
|
11
|
+
super(message)
|
12
|
+
@line = options[:line].to_i
|
13
|
+
@column = options[:column].to_i
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
##########################################################################
|
18
|
+
#
|
19
|
+
# Attributes
|
20
|
+
#
|
21
|
+
##########################################################################
|
22
|
+
|
23
|
+
# The line number that the error occurred on.
|
24
|
+
attr_reader :line
|
25
|
+
|
26
|
+
# The column number that the error occurred on.
|
27
|
+
attr_reader :column
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
class SkyDB
|
2
|
+
# The selection group contains an expression by which to group selected
|
3
|
+
# data.
|
4
|
+
class Query
|
5
|
+
class SelectionGroup
|
6
|
+
##########################################################################
|
7
|
+
#
|
8
|
+
# Constructor
|
9
|
+
#
|
10
|
+
##########################################################################
|
11
|
+
|
12
|
+
def initialize(options={})
|
13
|
+
self.expression = options[:expression]
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
##########################################################################
|
18
|
+
#
|
19
|
+
# Attributes
|
20
|
+
#
|
21
|
+
##########################################################################
|
22
|
+
|
23
|
+
# The name of the expression to group by.
|
24
|
+
attr_accessor :expression
|
25
|
+
|
26
|
+
# The string used to access the expression.
|
27
|
+
def accessor(options={})
|
28
|
+
prefix = options.delete(:prefix) || 'cursor.event.'
|
29
|
+
|
30
|
+
if ["action_id", "timestamp"].index(expression)
|
31
|
+
return "#{prefix}#{expression}"
|
32
|
+
else
|
33
|
+
return "#{prefix}#{expression}()"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
##########################################################################
|
39
|
+
#
|
40
|
+
# Methods
|
41
|
+
#
|
42
|
+
##########################################################################
|
43
|
+
|
44
|
+
####################################
|
45
|
+
# Validation
|
46
|
+
####################################
|
47
|
+
|
48
|
+
# Validates that the field is valid.
|
49
|
+
def validate!
|
50
|
+
# Expression must be present.
|
51
|
+
if expression.to_s.length == 0
|
52
|
+
raise SkyDB::Query::ValidationError.new("Invalid expression for selection group: '#{expression.to_s}'")
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
grammar SelectionGroupsGrammar
|
2
|
+
rule root
|
3
|
+
groups <SkyDB::Query::Ast::SelectionGroupsSyntaxNode>
|
4
|
+
end
|
5
|
+
|
6
|
+
# Matches all selection groups.
|
7
|
+
rule groups
|
8
|
+
ws group (ws ',' ws group)* ws
|
9
|
+
end
|
10
|
+
|
11
|
+
# Matches a single group name and optional alias.
|
12
|
+
rule group
|
13
|
+
expression <SkyDB::Query::Ast::SelectionGroupSyntaxNode>
|
14
|
+
end
|
15
|
+
|
16
|
+
rule expression
|
17
|
+
identifier
|
18
|
+
end
|
19
|
+
|
20
|
+
rule alias_name
|
21
|
+
identifier
|
22
|
+
end
|
23
|
+
|
24
|
+
rule identifier
|
25
|
+
([a-zA-Z_] [a-zA-Z0-9_]*)
|
26
|
+
end
|
27
|
+
|
28
|
+
rule ws
|
29
|
+
(' ' / "\t" / "\n" / "\r")*
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class SkyDB
|
2
|
+
class Query
|
3
|
+
class SelectionGroupsParseError < StandardError
|
4
|
+
##########################################################################
|
5
|
+
#
|
6
|
+
# Constructor
|
7
|
+
#
|
8
|
+
##########################################################################
|
9
|
+
|
10
|
+
def initialize(message, options={})
|
11
|
+
super(message)
|
12
|
+
@line = options[:line].to_i
|
13
|
+
@column = options[:column].to_i
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
##########################################################################
|
18
|
+
#
|
19
|
+
# Attributes
|
20
|
+
#
|
21
|
+
##########################################################################
|
22
|
+
|
23
|
+
# The line number that the error occurred on.
|
24
|
+
attr_reader :line
|
25
|
+
|
26
|
+
# The column number that the error occurred on.
|
27
|
+
attr_reader :column
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/skydb/table.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
class SkyDB
|
2
|
+
class Table
|
3
|
+
##########################################################################
|
4
|
+
#
|
5
|
+
# Constants
|
6
|
+
#
|
7
|
+
##########################################################################
|
8
|
+
|
9
|
+
DEFAULT_TABLET_COUNT = 4
|
10
|
+
|
11
|
+
|
12
|
+
##########################################################################
|
13
|
+
#
|
14
|
+
# Constructor
|
15
|
+
#
|
16
|
+
##########################################################################
|
17
|
+
|
18
|
+
# Initializes the table.
|
19
|
+
def initialize(name='', options={})
|
20
|
+
self.name = name
|
21
|
+
self.tablet_count = options[:tablet_count].to_i
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
##########################################################################
|
26
|
+
#
|
27
|
+
# Attributes
|
28
|
+
#
|
29
|
+
##########################################################################
|
30
|
+
|
31
|
+
##################################
|
32
|
+
# Name
|
33
|
+
##################################
|
34
|
+
|
35
|
+
# The name of the table.
|
36
|
+
attr_reader :name
|
37
|
+
|
38
|
+
def name=(value)
|
39
|
+
@name = value.to_s
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
##################################
|
44
|
+
# Tablet count
|
45
|
+
##################################
|
46
|
+
|
47
|
+
# The number of tablets the table has
|
48
|
+
attr_reader :tablet_count
|
49
|
+
|
50
|
+
def tablet_count=(value)
|
51
|
+
@tablet_count = value.to_i || DEFAULT_TABLET_COUNT
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
##########################################################################
|
56
|
+
#
|
57
|
+
# Methods
|
58
|
+
#
|
59
|
+
##########################################################################
|
60
|
+
|
61
|
+
# Encodes the table into MsgPack format.
|
62
|
+
def to_msgpack
|
63
|
+
return {
|
64
|
+
name: name,
|
65
|
+
tablet_count: tablet_count
|
66
|
+
}.to_msgpack
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
data/lib/skydb/version.rb
CHANGED
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class TestImporter < MiniTest::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
@importer = SkyDB::Import::Importer.new()
|
6
|
+
@input = {
|
7
|
+
'myString' => 'Hello',
|
8
|
+
'foo' => 100
|
9
|
+
}
|
10
|
+
@output = {}
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
######################################
|
15
|
+
# Transform File
|
16
|
+
######################################
|
17
|
+
|
18
|
+
def test_load_simple_transform
|
19
|
+
@importer.load_transform(
|
20
|
+
<<-BLOCK.unindent
|
21
|
+
fields:
|
22
|
+
name: myString
|
23
|
+
BLOCK
|
24
|
+
)
|
25
|
+
|
26
|
+
assert_equal "myString", @importer.translators.first.input_field
|
27
|
+
assert_equal "name", @importer.translators.first.output_field
|
28
|
+
assert_equal "string", @importer.translators.first.format
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_load_simple_proc
|
32
|
+
@importer.load_transform(
|
33
|
+
<<-BLOCK.unindent
|
34
|
+
fields:
|
35
|
+
name: "{ output['foo'] * 10 }"
|
36
|
+
BLOCK
|
37
|
+
)
|
38
|
+
|
39
|
+
assert_equal "name", @importer.translators.first.output_field
|
40
|
+
assert !@importer.translators.first.translate_function.nil?
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class TestTranslator < MiniTest::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
@translator = SkyDB::Import::Translator.new()
|
6
|
+
|
7
|
+
@input = {
|
8
|
+
'myString' => 'hello world',
|
9
|
+
'myInt' => '100',
|
10
|
+
'myFloat' => '20.21',
|
11
|
+
'myTrue' => 'true',
|
12
|
+
'myFalse' => 'false',
|
13
|
+
'myDate' => '10/26/1982 12:00 AM',
|
14
|
+
'foo' => '1000',
|
15
|
+
}
|
16
|
+
@output = {}
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
######################################
|
21
|
+
# Simple Translation
|
22
|
+
######################################
|
23
|
+
|
24
|
+
def test_string_translation
|
25
|
+
SkyDB::Import::Translator.new(:input_field => 'myString', :output_field => 'data')
|
26
|
+
.translate(@input, @output)
|
27
|
+
assert_equal 'hello world', @output['data']
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_int_translation
|
31
|
+
SkyDB::Import::Translator.new(:input_field => 'myInt', :output_field => 'data', :format => 'Int')
|
32
|
+
.translate(@input, @output)
|
33
|
+
assert_equal 100, @output['data']
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_float_translation
|
37
|
+
SkyDB::Import::Translator.new(:input_field => 'myFloat', :output_field => 'data', :format => 'Float')
|
38
|
+
.translate(@input, @output)
|
39
|
+
assert_equal 20.21, @output['data']
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_boolean_true_translation
|
43
|
+
SkyDB::Import::Translator.new(:input_field => 'myTrue', :output_field => 'data', :format => 'Boolean')
|
44
|
+
.translate(@input, @output)
|
45
|
+
assert_equal true, @output['data']
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_boolean_false_translation
|
49
|
+
SkyDB::Import::Translator.new(:input_field => 'myFalse', :output_field => 'data', :format => 'Boolean')
|
50
|
+
.translate(@input, @output)
|
51
|
+
assert_equal false, @output['data']
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_date_translation
|
55
|
+
SkyDB::Import::Translator.new(:input_field => 'myDate', :output_field => 'data', :format => 'Date')
|
56
|
+
.translate(@input, @output)
|
57
|
+
assert_equal Time.parse('1982-10-26'), @output['data']
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
######################################
|
62
|
+
# Nested Translation
|
63
|
+
######################################
|
64
|
+
|
65
|
+
def test_nested_translation
|
66
|
+
SkyDB::Import::Translator.new(:input_field => 'myString', :output_field => ['action', 'data'])
|
67
|
+
.translate(@input, @output)
|
68
|
+
assert_equal 'hello world', @output['action']['data']
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
######################################
|
73
|
+
# Dynamic Translation
|
74
|
+
######################################
|
75
|
+
|
76
|
+
def test_translate_function_proc
|
77
|
+
SkyDB::Import::Translator.new(:translate_function => lambda {|input, output| output['data'] = input['foo'].to_i + 10})
|
78
|
+
.translate(@input, @output)
|
79
|
+
assert_equal 1010, @output['data']
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
def test_translate_function_string
|
84
|
+
SkyDB::Import::Translator.new(:output_field => 'data', :translate_function => "input['foo'].to_i * 2")
|
85
|
+
.translate(@input, @output)
|
86
|
+
assert_equal 2000, @output['data']
|
87
|
+
end
|
88
|
+
end
|