skydb 0.2.1 → 0.2.2
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.
- 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
|