piglet 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +24 -4
- data/lib/piglet/field/binary_conditional.rb +15 -0
- data/lib/piglet/field/call_expression.rb +21 -0
- data/lib/piglet/field/infix_expression.rb +19 -0
- data/lib/piglet/field/literal.rb +20 -0
- data/lib/piglet/field/operators.rb +80 -0
- data/lib/piglet/field/prefix_expression.rb +23 -0
- data/lib/piglet/field/reference.rb +41 -0
- data/lib/piglet/field/rename.rb +13 -0
- data/lib/piglet/field/suffix_expression.rb +19 -0
- data/lib/piglet/inout/describe.rb +7 -0
- data/lib/piglet/inout/dump.rb +7 -0
- data/lib/piglet/inout/explain.rb +15 -0
- data/lib/piglet/inout/illustrate.rb +7 -0
- data/lib/piglet/inout/load.rb +31 -0
- data/lib/piglet/inout/output.rb +15 -0
- data/lib/piglet/inout/storage_types.rb +18 -0
- data/lib/piglet/inout/store.rb +19 -0
- data/lib/piglet/interpreter.rb +39 -7
- data/lib/piglet/relation/cogroup.rb +33 -0
- data/lib/piglet/relation/cross.rb +24 -0
- data/lib/piglet/relation/distinct.rb +18 -0
- data/lib/piglet/relation/filter.rb +15 -0
- data/lib/piglet/relation/foreach.rb +21 -0
- data/lib/piglet/relation/group.rb +23 -0
- data/lib/piglet/relation/join.rb +22 -0
- data/lib/piglet/relation/limit.rb +15 -0
- data/lib/piglet/relation/order.rb +31 -0
- data/lib/piglet/relation/relation.rb +179 -0
- data/lib/piglet/relation/sample.rb +15 -0
- data/lib/piglet/relation/split.rb +45 -0
- data/lib/piglet/relation/stream.rb +7 -0
- data/lib/piglet/relation/union.rb +21 -0
- data/lib/piglet.rb +40 -38
- data/spec/piglet/{field_spec.rb → field/reference_spec.rb} +22 -6
- data/spec/piglet/interpreter_spec.rb +51 -5
- data/spec/piglet/{relation_spec.rb → relation/relation_spec.rb} +6 -6
- data/spec/piglet/{split_spec.rb → relation/split_spec.rb} +8 -8
- data/spec/spec_helper.rb +0 -2
- metadata +39 -40
- data/examples/spike1.rb +0 -43
- data/examples/spike2.rb +0 -40
- data/lib/piglet/assignment.rb +0 -13
- data/lib/piglet/cogroup.rb +0 -31
- data/lib/piglet/cross.rb +0 -22
- data/lib/piglet/describe.rb +0 -5
- data/lib/piglet/distinct.rb +0 -16
- data/lib/piglet/dump.rb +0 -5
- data/lib/piglet/explain.rb +0 -13
- data/lib/piglet/field.rb +0 -40
- data/lib/piglet/field_expression_functions.rb +0 -62
- data/lib/piglet/field_function_expression.rb +0 -19
- data/lib/piglet/field_infix_expression.rb +0 -17
- data/lib/piglet/field_prefix_expression.rb +0 -21
- data/lib/piglet/field_rename.rb +0 -11
- data/lib/piglet/field_suffix_expression.rb +0 -17
- data/lib/piglet/filter.rb +0 -13
- data/lib/piglet/foreach.rb +0 -19
- data/lib/piglet/group.rb +0 -21
- data/lib/piglet/illustrate.rb +0 -5
- data/lib/piglet/join.rb +0 -20
- data/lib/piglet/limit.rb +0 -13
- data/lib/piglet/load.rb +0 -31
- data/lib/piglet/load_and_store.rb +0 -16
- data/lib/piglet/order.rb +0 -29
- data/lib/piglet/relation.rb +0 -177
- data/lib/piglet/sample.rb +0 -13
- data/lib/piglet/split.rb +0 -41
- data/lib/piglet/store.rb +0 -17
- data/lib/piglet/storing.rb +0 -13
- data/lib/piglet/stream.rb +0 -5
- data/lib/piglet/union.rb +0 -19
data/README.rdoc
CHANGED
@@ -256,14 +256,34 @@ All the aggregate functions are supported:
|
|
256
256
|
* +SUM+
|
257
257
|
* +TOKENIZE+
|
258
258
|
|
259
|
-
Piglet only supports
|
259
|
+
Piglet only supports most arithmetic and logic operators (see below) on fields -- but check the output and make sure that it's doing what you expect because some it's tricky to see where Piglet hijacks the operators and when it's Ruby that is running the show. I'm doing the best I can, but there are many things that can't be done, at least not in Ruby 1.8.
|
260
|
+
|
261
|
+
Piglet does support these field operators:
|
262
|
+
|
263
|
+
* <code>==</code> (equality)
|
264
|
+
* <code>></code> (greater than)
|
265
|
+
* <code><</code> (less than)
|
266
|
+
* <code>>=</code> (greater or equal to)
|
267
|
+
* <code><=</code> (less than or equal to)
|
268
|
+
* <code>%</code> (modulo)
|
269
|
+
* <code>+</code> (addition)
|
270
|
+
* <code>-</code> (subtraction)
|
271
|
+
* <code>*</code> (multiplication)
|
272
|
+
* <code>/</code> (division)
|
273
|
+
|
274
|
+
It also has these operators, see below for explanations:
|
275
|
+
|
276
|
+
* <code>#not</code> (logical negation)
|
277
|
+
* <code>#neg</code> (numerical negation)
|
278
|
+
* <code>#ne</code> (not equals)
|
279
|
+
* <code>#test</code> (binary conditionals)
|
260
280
|
|
261
281
|
Piglet does not support:
|
262
282
|
|
263
|
-
* <code>!=</code> (not equals, you have to use <code>==</code> and a <code>NOT</code>, e.g. <code>(a == b).not</code>, which will be translated as <code>NOT (a == b)</code> or you can use <code>#ne</code>, which will translate to !=, e.g. <code>a.ne(b)</code> will become <code>a != b</code
|
283
|
+
* <code>!=</code> (not equals, you have to use <code>==</code> and a <code>NOT</code>, e.g. <code>(a == b).not</code>, which will be translated as <code>NOT (a == b)</code> or you can use <code>#ne</code>, which will translate to !=, e.g. <code>a.ne(b)</code> will become <code>a != b</code>. May be supported in the future, but only in Ruby 1.9)
|
264
284
|
* <code>? :</code> (the ternary operator)
|
265
|
-
* <code>-</code> (negation, but you can use <code>#neg</code> on a field expression to get the same result, e.g. <code>a.neg</code> will be translated as <code>-a</code
|
266
|
-
* <code>key#value</code> (map dereferencing)
|
285
|
+
* <code>-</code> (negation, but you can use <code>#neg</code> on a field expression to get the same result, e.g. <code>a.neg</code> will be translated as <code>-a</code>. May be supported in the future, but only in Ruby 1.9)
|
286
|
+
* <code>key#value</code> (map dereferencing, may be supported in the future)
|
267
287
|
|
268
288
|
=== Why aren't the aliases in the Pig Latin the same as the variable names in the Piglet script?
|
269
289
|
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Piglet
|
2
|
+
module Field
|
3
|
+
include Operators
|
4
|
+
|
5
|
+
class BinaryConditional
|
6
|
+
def initialize(test, if_true, if_false)
|
7
|
+
@test, @if_true, @if_false = test, if_true, if_false
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_s
|
11
|
+
"(#{@test} ? #{@if_true} : #{@if_false})"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Piglet
|
2
|
+
module Field
|
3
|
+
class CallExpression # :nodoc:
|
4
|
+
include Operators
|
5
|
+
|
6
|
+
def initialize(name, inner_expression, options=nil)
|
7
|
+
options ||= {}
|
8
|
+
@name, @inner_expression = name, inner_expression
|
9
|
+
@new_name = options[:as]
|
10
|
+
end
|
11
|
+
|
12
|
+
def simple?
|
13
|
+
false
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_s
|
17
|
+
"#{@name}(#{@inner_expression})"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Piglet
|
2
|
+
module Field
|
3
|
+
class InfixExpression # :nodoc:
|
4
|
+
include Operators
|
5
|
+
|
6
|
+
def initialize(operator, left_expression, right_expression)
|
7
|
+
@operator, @left_expression, @right_expression = operator, left_expression, right_expression
|
8
|
+
end
|
9
|
+
|
10
|
+
def simple?
|
11
|
+
false
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_s
|
15
|
+
"#{parenthesise(@left_expression)} #{@operator} #{parenthesise(@right_expression)}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module Piglet
|
2
|
+
module Field
|
3
|
+
module Operators # :nodoc:
|
4
|
+
SYMBOLIC_OPERATORS = [:==, :>, :<, :>=, :<=, :%, :+, :-, :*, :/]
|
5
|
+
FUNCTIONS = [:avg, :count, :diff, :max, :min, :size, :sum, :tokenize]
|
6
|
+
|
7
|
+
FUNCTIONS.each do |fun|
|
8
|
+
define_method(fun) do
|
9
|
+
CallExpression.new(fun.to_s.upcase, self)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def empty?
|
14
|
+
CallExpression.new('IsEmpty', self)
|
15
|
+
end
|
16
|
+
|
17
|
+
def as(new_name)
|
18
|
+
Rename.new(new_name, self)
|
19
|
+
end
|
20
|
+
|
21
|
+
def not
|
22
|
+
PrefixExpression.new('NOT', self)
|
23
|
+
end
|
24
|
+
|
25
|
+
def null?
|
26
|
+
SuffixExpression.new('is null', self)
|
27
|
+
end
|
28
|
+
|
29
|
+
def not_null?
|
30
|
+
SuffixExpression.new('is not null', self)
|
31
|
+
end
|
32
|
+
|
33
|
+
def cast(type)
|
34
|
+
PrefixExpression.new("(#{type.to_s})", self)
|
35
|
+
end
|
36
|
+
|
37
|
+
def matches(pattern)
|
38
|
+
regex_options_pattern = /^\(\?.+?:(.*)\)$/
|
39
|
+
pattern = pattern.to_s.sub(regex_options_pattern, '\1') if pattern.is_a?(Regexp) && pattern.to_s =~ regex_options_pattern
|
40
|
+
InfixExpression.new('matches', self, "'#{pattern.to_s}'")
|
41
|
+
end
|
42
|
+
|
43
|
+
def neg
|
44
|
+
PrefixExpression.new('-', self, false)
|
45
|
+
end
|
46
|
+
|
47
|
+
def ne(other)
|
48
|
+
InfixExpression.new('!=', self, other)
|
49
|
+
end
|
50
|
+
|
51
|
+
def and(other)
|
52
|
+
InfixExpression.new('AND', self, other)
|
53
|
+
end
|
54
|
+
|
55
|
+
def or(other)
|
56
|
+
InfixExpression.new('OR', self, other)
|
57
|
+
end
|
58
|
+
|
59
|
+
SYMBOLIC_OPERATORS.each do |op|
|
60
|
+
define_method(op) do |other|
|
61
|
+
InfixExpression.new(op.to_s, self, other)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
protected
|
66
|
+
|
67
|
+
def parenthesise(expr)
|
68
|
+
if expr.respond_to?(:simple?) && ! expr.simple?
|
69
|
+
"(#{expr})"
|
70
|
+
else
|
71
|
+
expr.to_s
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def escape(str)
|
76
|
+
str.gsub(/("|'|\\)/) { |m| "\\#{$1}" }
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Piglet
|
2
|
+
module Field
|
3
|
+
class PrefixExpression # :nodoc:
|
4
|
+
include Operators
|
5
|
+
|
6
|
+
def initialize(operator, expression, space_between=true)
|
7
|
+
@operator, @expression, @space_between = operator, expression, space_between
|
8
|
+
end
|
9
|
+
|
10
|
+
def simple?
|
11
|
+
true
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_s
|
15
|
+
if @space_between
|
16
|
+
"#{@operator} #{parenthesise(@expression)}"
|
17
|
+
else
|
18
|
+
"#{@operator}#{parenthesise(@expression)}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Piglet
|
2
|
+
module Field
|
3
|
+
class Reference # :nodoc:
|
4
|
+
include Operators
|
5
|
+
|
6
|
+
def initialize(name, relation=nil, options=nil)
|
7
|
+
options ||= {}
|
8
|
+
@name, @parent = name, relation
|
9
|
+
@explicit_ancestry = options[:explicit_ancestry] || false
|
10
|
+
end
|
11
|
+
|
12
|
+
def simple?
|
13
|
+
true
|
14
|
+
end
|
15
|
+
|
16
|
+
def method_missing(name, *args)
|
17
|
+
if name.to_s =~ /^\w+$/ && args.empty?
|
18
|
+
Reference.new(name, self, :explicit_ancestry => true)
|
19
|
+
else
|
20
|
+
super
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def [](n)
|
25
|
+
Reference.new("\$#{n}", self, :explicit_ancestry => true)
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_s
|
29
|
+
if @explicit_ancestry
|
30
|
+
if @parent.respond_to?(:alias)
|
31
|
+
"#{@parent.alias}.#{@name.to_s}"
|
32
|
+
else
|
33
|
+
"#{@parent}.#{@name.to_s}"
|
34
|
+
end
|
35
|
+
else
|
36
|
+
@name.to_s
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Piglet
|
2
|
+
module Field
|
3
|
+
class SuffixExpression # :nodoc:
|
4
|
+
include Operators
|
5
|
+
|
6
|
+
def initialize(operator, expression)
|
7
|
+
@operator, @expression = operator, expression
|
8
|
+
end
|
9
|
+
|
10
|
+
def simple?
|
11
|
+
false
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_s
|
15
|
+
"#{parenthesise(@expression)} #{@operator}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Piglet
|
2
|
+
module Inout
|
3
|
+
class Load # :nodoc:
|
4
|
+
include StorageTypes
|
5
|
+
|
6
|
+
def initialize(path, options={})
|
7
|
+
options ||= {}
|
8
|
+
@path, @using, @schema = path, options[:using], options[:schema]
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_s
|
12
|
+
str = "LOAD '#{@path}'"
|
13
|
+
str << " USING #{resolve_load_store_function(@using)}" if @using
|
14
|
+
str << " AS (#{schema_string})" if @schema
|
15
|
+
str
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def schema_string
|
21
|
+
@schema.map do |field|
|
22
|
+
if field.is_a?(Enumerable)
|
23
|
+
field.map { |f| f.to_s }.join(':')
|
24
|
+
else
|
25
|
+
field.to_s
|
26
|
+
end
|
27
|
+
end.join(', ')
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Piglet
|
2
|
+
module Inout
|
3
|
+
module StorageTypes # :nodoc:
|
4
|
+
LOAD_STORE_FUNCTIONS = {
|
5
|
+
:binary_serializer => 'BinarySerializer',
|
6
|
+
:binary_deserializer => 'BinaryDeserializer',
|
7
|
+
:bin_storage => 'BinStorage',
|
8
|
+
:pig_storage => 'PigStorage',
|
9
|
+
:pig_dump => 'PigDump',
|
10
|
+
:text_loader => 'TextLoader'
|
11
|
+
}
|
12
|
+
|
13
|
+
def resolve_load_store_function(name)
|
14
|
+
LOAD_STORE_FUNCTIONS[name] || name.to_s
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Piglet
|
2
|
+
module Inout
|
3
|
+
class Store # :nodoc:
|
4
|
+
include StorageTypes
|
5
|
+
include Output
|
6
|
+
|
7
|
+
def initialize(relation, path, options={})
|
8
|
+
@relation, @path, @using = relation, path, options[:using]
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_s
|
12
|
+
str = super
|
13
|
+
str << " INTO '#{@path}'"
|
14
|
+
str << " USING #{resolve_load_store_function(@using)}" if @using
|
15
|
+
str
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/piglet/interpreter.rb
CHANGED
@@ -52,7 +52,9 @@ module Piglet
|
|
52
52
|
# NOTE: the syntax load('path', :schema => {:a => :chararray, :b => :int})
|
53
53
|
# would be nice, but the order of the keys can't be guaranteed in Ruby 1.8.
|
54
54
|
def load(path, options={})
|
55
|
-
Load.new(path, options)
|
55
|
+
load = Inout::Load.new(path, options)
|
56
|
+
load.extend Piglet::Relation::Relation
|
57
|
+
load
|
56
58
|
end
|
57
59
|
|
58
60
|
# STORE
|
@@ -61,28 +63,28 @@ module Piglet
|
|
61
63
|
# store(x, 'some/path', :using => 'Xyz') # => STORE x INTO 'some/path' USING Xyz
|
62
64
|
# store(x, 'some/path', :using => :pig_storage) # => STORE x INTO 'some/path' USING PigStorage
|
63
65
|
def store(relation, path, options={})
|
64
|
-
@stores << Store.new(relation, path, options)
|
66
|
+
@stores << Inout::Store.new(relation, path, options)
|
65
67
|
end
|
66
68
|
|
67
69
|
# DUMP
|
68
70
|
#
|
69
71
|
# dump(x) # => DUMP x
|
70
72
|
def dump(relation)
|
71
|
-
@stores << Dump.new(relation)
|
73
|
+
@stores << Inout::Dump.new(relation)
|
72
74
|
end
|
73
75
|
|
74
76
|
# ILLUSTRATE
|
75
77
|
#
|
76
78
|
# illustrate(x) # => ILLUSTRATE x
|
77
79
|
def illustrate(relation)
|
78
|
-
@stores << Illustrate.new(relation)
|
80
|
+
@stores << Inout::Illustrate.new(relation)
|
79
81
|
end
|
80
82
|
|
81
83
|
# DESCRIBE
|
82
84
|
#
|
83
85
|
# describe(x) # => DESCRIBE x
|
84
86
|
def describe(relation)
|
85
|
-
@stores << Describe.new(relation)
|
87
|
+
@stores << Inout::Describe.new(relation)
|
86
88
|
end
|
87
89
|
|
88
90
|
# EXPLAIN
|
@@ -90,7 +92,23 @@ module Piglet
|
|
90
92
|
# explain # => EXPLAIN
|
91
93
|
# explain(x) # => EXPLAIN(x)
|
92
94
|
def explain(relation=nil)
|
93
|
-
@stores << Explain.new(relation)
|
95
|
+
@stores << Inout::Explain.new(relation)
|
96
|
+
end
|
97
|
+
|
98
|
+
# Support for binary conditions, a.k.a. the ternary operator.
|
99
|
+
#
|
100
|
+
# x.test(x.a > x.b, x.a, x.b) # => (a > b ? a : b)
|
101
|
+
#
|
102
|
+
# Should only be used in the block given to #filter and #foreach
|
103
|
+
def test(test, if_true, if_false)
|
104
|
+
Field::BinaryConditional.new(test, if_true, if_false)
|
105
|
+
end
|
106
|
+
|
107
|
+
# Support for literals in FOREACH … GENERATE blocks.
|
108
|
+
#
|
109
|
+
# x.foreach { |r| [literal("hello").as(:hello)] } # => FOREACH x GENERATE 'hello' AS hello
|
110
|
+
def literal(obj)
|
111
|
+
Field::Literal.new(obj)
|
94
112
|
end
|
95
113
|
|
96
114
|
private
|
@@ -104,5 +122,19 @@ module Piglet
|
|
104
122
|
[assignment]
|
105
123
|
end
|
106
124
|
end
|
107
|
-
end
|
125
|
+
end
|
126
|
+
|
127
|
+
private
|
128
|
+
|
129
|
+
class Assignment # :nodoc:
|
130
|
+
attr_reader :target
|
131
|
+
|
132
|
+
def initialize(relation)
|
133
|
+
@target = relation
|
134
|
+
end
|
135
|
+
|
136
|
+
def to_s
|
137
|
+
"#{@target.alias} = #{@target.to_s}"
|
138
|
+
end
|
139
|
+
end
|
108
140
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Piglet
|
2
|
+
module Relation
|
3
|
+
class Cogroup # :nodoc:
|
4
|
+
include Relation
|
5
|
+
|
6
|
+
def initialize(relation, description)
|
7
|
+
@join_fields = description.reject { |k, v| ! (k.is_a?(Relation)) }
|
8
|
+
@sources = @join_fields.keys
|
9
|
+
@parallel = description[:parallel]
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_s
|
13
|
+
joins = @sources.map do |s|
|
14
|
+
fields = @join_fields[s]
|
15
|
+
if fields.is_a?(Enumerable) && fields.size > 1 && (fields.last == :inner || fields.last == :outer)
|
16
|
+
inout = fields.last.to_s.upcase
|
17
|
+
fields = fields[0..-2]
|
18
|
+
end
|
19
|
+
if fields.is_a?(Enumerable) && fields.size > 1
|
20
|
+
str = "#{s.alias} BY (#{fields.join(', ')})"
|
21
|
+
else
|
22
|
+
str = "#{s.alias} BY #{fields}"
|
23
|
+
end
|
24
|
+
str << " #{inout}" if inout
|
25
|
+
str
|
26
|
+
end
|
27
|
+
str = "COGROUP #{joins.join(', ')}"
|
28
|
+
str << " PARALLEL #{@parallel}" if @parallel
|
29
|
+
str
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Piglet
|
2
|
+
module Relation
|
3
|
+
class Cross # :nodoc:
|
4
|
+
include Relation
|
5
|
+
|
6
|
+
def initialize(relations, options={})
|
7
|
+
options ||= {}
|
8
|
+
@sources, @parallel = relations, options[:parallel]
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_s
|
12
|
+
str = "CROSS #{source_aliases.join(', ')}"
|
13
|
+
str << " PARALLEL #{@parallel}" if @parallel
|
14
|
+
str
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def source_aliases
|
20
|
+
@sources.map { |s| s.alias }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Piglet
|
2
|
+
module Relation
|
3
|
+
class Distinct # :nodoc:
|
4
|
+
include Relation
|
5
|
+
|
6
|
+
def initialize(relation, options={})
|
7
|
+
options ||= {}
|
8
|
+
@sources, @parallel = [relation], options[:parallel]
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_s
|
12
|
+
str = "DISTINCT #{@sources.first.alias}"
|
13
|
+
str << " PARALLEL #{@parallel}" if @parallel
|
14
|
+
str
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Piglet
|
2
|
+
module Relation
|
3
|
+
class Filter # :nodoc:
|
4
|
+
include Relation
|
5
|
+
|
6
|
+
def initialize(relation, expression)
|
7
|
+
@sources, @expression = [relation], expression
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_s
|
11
|
+
"FILTER #{@sources.first.alias} BY #{@expression}"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Piglet
|
2
|
+
module Relation
|
3
|
+
class Foreach # :nodoc:
|
4
|
+
include Relation
|
5
|
+
|
6
|
+
def initialize(relation, field_expressions)
|
7
|
+
@sources, @field_expressions = [relation], [field_expressions].flatten
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_s
|
11
|
+
"FOREACH #{@sources.first.alias} GENERATE #{field_expressions_string}"
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def field_expressions_string
|
17
|
+
@field_expressions.map { |fe| fe.to_s }.join(', ')
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Piglet
|
2
|
+
module Relation
|
3
|
+
class Group # :nodoc:
|
4
|
+
include Relation
|
5
|
+
|
6
|
+
def initialize(relation, grouping, options={})
|
7
|
+
options ||= {}
|
8
|
+
@sources, @grouping, @parallel = [relation], grouping, options[:parallel]
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_s
|
12
|
+
str = "GROUP #{@sources.first.alias} BY "
|
13
|
+
if @grouping.size > 1
|
14
|
+
str << "(#{@grouping.join(', ')})"
|
15
|
+
else
|
16
|
+
str << @grouping.first.to_s
|
17
|
+
end
|
18
|
+
str << " PARALLEL #{@parallel}" if @parallel
|
19
|
+
str
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|