mohair 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/Gemfile +4 -1
- data/README.md +38 -3
- data/Rakefile +11 -1
- data/bin/mohair_dump +5 -0
- data/lib/mohair/inserter.rb +39 -0
- data/lib/mohair/selector.rb +139 -34
- data/lib/mohair/sql/parser.rb +88 -0
- data/lib/mohair/sql/select.rb +93 -0
- data/lib/mohair/sql/tree.rb +197 -0
- data/lib/mohair/version.rb +1 -1
- data/lib/mohair.rb +77 -7
- data/sample_data.json +40 -0
- data/test/plugin/selector.rb +10 -0
- data/test/plugin/sql.rb +73 -0
- data/test/test_helper.rb +5 -0
- metadata +28 -5
data/.gitignore
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
# Mohair
|
2
2
|
|
3
|
-
TODO: Write a gem description
|
4
|
-
|
5
3
|
## Installation
|
6
4
|
|
7
5
|
Add this line to your application's Gemfile:
|
@@ -18,7 +16,40 @@ Or install it yourself as:
|
|
18
16
|
|
19
17
|
## Usage
|
20
18
|
|
21
|
-
|
19
|
+
now:
|
20
|
+
|
21
|
+
```
|
22
|
+
$ bundle exec bin/mohair -q "select all from sometable" -i INDEX
|
23
|
+
$ bundle exec bin/mohair_dump < data.json
|
24
|
+
```
|
25
|
+
|
26
|
+
future:
|
27
|
+
|
28
|
+
```
|
29
|
+
$ bundle exec bin/mohair -i
|
30
|
+
mohair> select * from sometable
|
31
|
+
mohair> insert * into ...
|
32
|
+
mohair>
|
33
|
+
```
|
34
|
+
|
35
|
+
```
|
36
|
+
$ bundle exec bin/mohair -q "select * from sometable"
|
37
|
+
...
|
38
|
+
```
|
39
|
+
|
40
|
+
## Currently works
|
41
|
+
|
42
|
+
- basic SQL parsing
|
43
|
+
- select * from table
|
44
|
+
- select col,col,col from table [where col = "name" and col < 23] [group by col]
|
45
|
+
|
46
|
+
## TODO
|
47
|
+
|
48
|
+
- group by
|
49
|
+
- limit 10
|
50
|
+
- asc/desc
|
51
|
+
- 2i
|
52
|
+
- query optimization
|
22
53
|
|
23
54
|
## Contributing
|
24
55
|
|
@@ -27,3 +58,7 @@ TODO: Write usage instructions here
|
|
27
58
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
28
59
|
4. Push to the branch (`git push origin my-new-feature`)
|
29
60
|
5. Create new Pull Request
|
61
|
+
|
62
|
+
```
|
63
|
+
$ bundle exec rake release
|
64
|
+
```
|
data/Rakefile
CHANGED
@@ -1,3 +1,13 @@
|
|
1
1
|
require "bundler/gem_tasks"
|
2
2
|
require 'bundler'
|
3
|
-
Bundler::GemHelper.install_tasks
|
3
|
+
Bundler::GemHelper.install_tasks
|
4
|
+
|
5
|
+
require 'rake/testtask'
|
6
|
+
|
7
|
+
Rake::TestTask.new(:test) do |t|
|
8
|
+
t.libs << 'lib' << 'test'
|
9
|
+
t.test_files = FileList['test/plugin/*.rb']
|
10
|
+
t.verbose = true
|
11
|
+
end
|
12
|
+
|
13
|
+
task :default => :test
|
data/bin/mohair_dump
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'mohair'
|
2
|
+
require 'riak'
|
3
|
+
|
4
|
+
module Mohair
|
5
|
+
class Inserter
|
6
|
+
def initialize bucket_name
|
7
|
+
@bucket_name = bucket_name
|
8
|
+
end
|
9
|
+
def insert_all objs
|
10
|
+
@client = Riak::Client.new(:protocol => "http")
|
11
|
+
bucket = @client.bucket(@bucket_name)
|
12
|
+
if bucket.props["allow_mult"] then
|
13
|
+
$stderr.puts "allow_mult should be false (how to handle siblilngs?)"
|
14
|
+
return
|
15
|
+
end
|
16
|
+
|
17
|
+
objs.each do |obj|
|
18
|
+
r_o = Riak::RObject.new(bucket, obj["key"])
|
19
|
+
|
20
|
+
data = r_o.data = obj["data"]
|
21
|
+
obj["data"].each do |k,v|
|
22
|
+
begin
|
23
|
+
if integer? v then r_o.indexes[k + "_int"] << v end
|
24
|
+
rescue
|
25
|
+
r_o.indexes[k + "_bin"] << v
|
26
|
+
end
|
27
|
+
end
|
28
|
+
r_o.content_type = 'application/json'
|
29
|
+
r_o.store
|
30
|
+
print obj["key"] , "\t => ", data, "\n"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def exec!
|
35
|
+
end
|
36
|
+
def pr
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/mohair/selector.rb
CHANGED
@@ -1,43 +1,148 @@
|
|
1
1
|
require 'mohair'
|
2
|
+
require 'riak'
|
3
|
+
require 'erb'
|
2
4
|
|
3
5
|
module Mohair
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
end
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
6
|
+
|
7
|
+
MapperTemplate = <<EOMAP
|
8
|
+
function(v){
|
9
|
+
var f = function(key, obj){
|
10
|
+
var ret = {};
|
11
|
+
<% select.each do |c| %>
|
12
|
+
<%= c %>
|
13
|
+
<% end %>
|
14
|
+
ret.__key = key;
|
15
|
+
<%= where %>
|
16
|
+
};
|
17
|
+
var raw_obj = JSON.parse(v.values[0].data);
|
18
|
+
if(raw_obj instanceof Array){
|
19
|
+
var ret0 = [];
|
20
|
+
for(var i in raw_obj){
|
21
|
+
ret0 = ret0.concat(f(v.key, raw_obj[i]));
|
22
|
+
}
|
23
|
+
return ret0;
|
24
|
+
}else{
|
25
|
+
return f(v.key, raw_obj);
|
26
|
+
}
|
27
|
+
}
|
28
|
+
EOMAP
|
29
|
+
|
30
|
+
ReducerTemplate = <<EOREDUCE
|
31
|
+
function(values){
|
32
|
+
var ret = {};
|
33
|
+
// init lines
|
34
|
+
<%= agg_init %>
|
35
|
+
for(var i in values){
|
36
|
+
var v=values[i];
|
37
|
+
<%= agg_fun %>
|
38
|
+
}
|
39
|
+
return [ret];
|
40
|
+
}
|
41
|
+
EOREDUCE
|
42
|
+
# if(!!(v.sum_age)){ ret.sum_age += v.sum_age; }
|
43
|
+
|
44
|
+
GetAllMapper = <<GETALLMAPPER
|
45
|
+
function(v){
|
46
|
+
var f = function(key, obj){
|
47
|
+
var ret = obj;
|
48
|
+
ret.__key = key;
|
49
|
+
<%= where %>
|
50
|
+
};
|
51
|
+
var raw_obj = JSON.parse(v.values[0].data);
|
52
|
+
if(raw_obj instanceof Array){
|
53
|
+
var ret0 = [];
|
54
|
+
for(var i in raw_obj){
|
55
|
+
ret0 = ret0.concat(f(v.key, raw_obj[i]));
|
56
|
+
}
|
57
|
+
return ret0;
|
58
|
+
}else{
|
59
|
+
return f(v.key, raw_obj);
|
60
|
+
}
|
61
|
+
}
|
62
|
+
GETALLMAPPER
|
63
|
+
|
64
|
+
GroupByMapperTemplate = <<EOGROUPER
|
65
|
+
function(v){
|
66
|
+
ejsLog('/tmp/map_reduce.log', "startmapper>")
|
67
|
+
var f = function(key, obj){
|
68
|
+
var ret = {};
|
69
|
+
<% select.each do |c| %>
|
70
|
+
<%= c %>
|
71
|
+
<% end %>
|
72
|
+
ret.__key = key;
|
73
|
+
<%= where %>
|
74
|
+
};
|
75
|
+
var arr = [];
|
76
|
+
var raw_obj = JSON.parse(v.values[0].data);
|
77
|
+
if(raw_obj instanceof Array){
|
78
|
+
var ret0 = [];
|
79
|
+
for(var i in raw_obj){
|
80
|
+
ret0 = ret0.concat(f(v.key, raw_obj[i]));
|
81
|
+
}
|
82
|
+
arr = ret0;
|
83
|
+
}else{
|
84
|
+
arr = f(v.key, raw_obj);
|
85
|
+
}
|
86
|
+
var ret = {};
|
87
|
+
for(var i in arr){
|
88
|
+
//ejsLog('/tmp/map_reduce.log', JSON.stringify(arr[i].<%= col %>))
|
89
|
+
var col = arr[i].<%= col %>;
|
90
|
+
//ejsLog('/tmp/map_reduce.log', JSON.stringify(col))
|
91
|
+
if(ret[col]){
|
92
|
+
ret[col] = ret[col].push(arr[i]);
|
93
|
+
//ejsLog('/tmp/map_reduce.log', JSON.stringify(ret[col]))
|
94
|
+
}else{
|
95
|
+
ret[col] = [arr[i]];
|
96
|
+
//ejsLog('/tmp/map_reduce.log', JSON.stringify(ret[col]))
|
97
|
+
}
|
98
|
+
}
|
99
|
+
ejsLog('/tmp/map_reduce.log', "<eomapper")
|
100
|
+
return [ret];
|
101
|
+
}
|
102
|
+
EOGROUPER
|
103
|
+
|
104
|
+
|
105
|
+
## merge them all
|
106
|
+
## [{ k, v }, ....] -> {k, v}
|
107
|
+
GroupByReducerTemplate = <<EOREDUCEGROUPER
|
108
|
+
function(values){
|
109
|
+
ejsLog('/tmp/map_reduce.log', "start>");
|
110
|
+
var ret = {};
|
111
|
+
for(var i in values){
|
112
|
+
ejsLog('/tmp/map_reduce.log', JSON.stringify(i))
|
113
|
+
for(var a in values[i]){
|
114
|
+
if(ret[a]){
|
115
|
+
ret[a] = ret[a].concat(values[i][a]);
|
116
|
+
}else{
|
117
|
+
ret[a] = values[i][a];
|
118
|
+
}
|
119
|
+
}
|
120
|
+
}
|
121
|
+
ejsLog('/tmp/map_reduce.log', "<end");
|
122
|
+
// ejsLog('/tmp/map_reduce.log', JSON.stringify(ret))
|
123
|
+
return [ret];
|
124
|
+
}
|
125
|
+
EOREDUCEGROUPER
|
126
|
+
|
127
|
+
|
128
|
+
def Mohair.format_result results
|
129
|
+
columns = Set.new
|
130
|
+
results.each do |r|
|
131
|
+
r.each do |k,v|
|
132
|
+
columns.add(k)
|
22
133
|
end
|
23
134
|
end
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
135
|
+
columns.delete('__key')
|
136
|
+
cols = columns.to_a.join("\t| ")
|
137
|
+
print "| | #{cols}|\n"
|
138
|
+
print "+---------------+------------------------------+\n"
|
139
|
+
results.each do |r|
|
140
|
+
print "| #{r['__key']}\t| "
|
141
|
+
columns.to_a.each do |c|
|
142
|
+
print "#{r[c]} \t| "
|
32
143
|
end
|
33
|
-
|
34
|
-
def parse_where tokens
|
35
|
-
end
|
36
|
-
def exec!
|
37
|
-
pr
|
38
|
-
end
|
39
|
-
def pr
|
40
|
-
print "<select> #{@cols} <from> #{@from};\n"
|
144
|
+
print "\n"
|
41
145
|
end
|
42
146
|
end
|
147
|
+
|
43
148
|
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
|
2
|
+
require "rubygems"
|
3
|
+
require "parslet"
|
4
|
+
|
5
|
+
module Mohair
|
6
|
+
module Sql
|
7
|
+
class Parser < Parslet::Parser
|
8
|
+
rule(:integer) { match('[0-9]').repeat(1) }
|
9
|
+
#rule(:float) { integer.repeat >> str('.') >> integer.maybe }
|
10
|
+
|
11
|
+
rule(:space) { match('\s').repeat(1) }
|
12
|
+
rule(:space?) { space.maybe }
|
13
|
+
rule(:comma) { str(',') >> space? }
|
14
|
+
rule(:lparen) { str('(') >> space? }
|
15
|
+
rule(:rparen) { str(')') >> space? }
|
16
|
+
|
17
|
+
# logical operators
|
18
|
+
rule(:eq) { str('=') }
|
19
|
+
rule(:neq) { str('!=') | str('<>') }
|
20
|
+
rule(:gt) { str('>') }
|
21
|
+
rule(:lt) { str('<') }
|
22
|
+
rule(:geq) { str('>=') }
|
23
|
+
rule(:leq) { str('<=') }
|
24
|
+
rule(:btw) { str('between') }
|
25
|
+
#rule(:like) { str('like') >> space? }
|
26
|
+
rule(:binop) { eq | neq | gt | lt | geq | leq | btw}
|
27
|
+
|
28
|
+
rule(:string) { str('"') >> match('[a-zA-Z0-9]').repeat >> str('"') }
|
29
|
+
|
30
|
+
rule(:const) {
|
31
|
+
integer | string
|
32
|
+
}
|
33
|
+
rule(:term) { const | item }
|
34
|
+
|
35
|
+
rule(:bool_and) { str('and') }
|
36
|
+
rule(:bool_or) { str('or') }
|
37
|
+
|
38
|
+
|
39
|
+
rule(:identifier) { match('[a-z]').repeat(1) }
|
40
|
+
|
41
|
+
rule(:function) {
|
42
|
+
identifier.as(:function) >> space? >>
|
43
|
+
lparen >> arglist.as(:arguments) >> rparen
|
44
|
+
}
|
45
|
+
|
46
|
+
rule(:item) { function | identifier }
|
47
|
+
|
48
|
+
rule(:arglist) {
|
49
|
+
item.as(:item) >> (comma >> item.as(:item)).repeat
|
50
|
+
}
|
51
|
+
rule(:namelist) {
|
52
|
+
identifier.as(:name) >> (comma >> identifier.as(:name)).repeat
|
53
|
+
}
|
54
|
+
|
55
|
+
rule(:single_cond){
|
56
|
+
term.as(:lhs) >> space? >> binop.as(:op) >> space? >> term.as(:rhs)
|
57
|
+
}
|
58
|
+
|
59
|
+
rule(:condition) {
|
60
|
+
single_cond.as(:lhs) >> space? >>
|
61
|
+
((bool_and.as(:op) | bool_or.as(:op)) >>
|
62
|
+
space? >>
|
63
|
+
condition.as(:rhs)).maybe
|
64
|
+
}
|
65
|
+
|
66
|
+
rule(:select_s) {
|
67
|
+
str('select').as(:op) >> space? >> (arglist | str('*')).as(:select)
|
68
|
+
}
|
69
|
+
rule(:from_s) {
|
70
|
+
str('from') >> space? >> namelist.as(:from)
|
71
|
+
}
|
72
|
+
rule(:where_s) {
|
73
|
+
str('where') >> space? >> condition.as(:where)
|
74
|
+
}
|
75
|
+
rule(:group_s) {
|
76
|
+
str('group') >> space? >> str('by') >> space? >> item.as(:group_by)
|
77
|
+
}
|
78
|
+
rule(:select) {
|
79
|
+
select_s >> space? >> from_s >> space? >>
|
80
|
+
(where_s >> space? >> group_s.maybe | group_s).maybe
|
81
|
+
}
|
82
|
+
## limit 10 desc by 'col'
|
83
|
+
|
84
|
+
rule(:expression) { select } #| insert | create }
|
85
|
+
root :expression
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'mohair/selector'
|
2
|
+
|
3
|
+
module Mohair
|
4
|
+
class Select
|
5
|
+
def initialize tree
|
6
|
+
@select = build_columns tree[:select]
|
7
|
+
@from = From.new tree[:from]
|
8
|
+
@where = Where.new tree[:where]
|
9
|
+
@group_by = Group.new tree[:group_by]
|
10
|
+
@agg = false
|
11
|
+
end
|
12
|
+
|
13
|
+
def build_columns items
|
14
|
+
reqs = []
|
15
|
+
if items.class == Array then
|
16
|
+
items.each do |i|
|
17
|
+
reqs << maybe_column(i[:item])
|
18
|
+
end
|
19
|
+
elsif items.class == Hash then
|
20
|
+
reqs << maybe_column(items[:item])
|
21
|
+
elsif items.to_s == "*" then
|
22
|
+
reqs = :all
|
23
|
+
end
|
24
|
+
reqs
|
25
|
+
end
|
26
|
+
|
27
|
+
def maybe_column item
|
28
|
+
if item.class == Hash then
|
29
|
+
if item[:function] then
|
30
|
+
Function.new item
|
31
|
+
else
|
32
|
+
raise item
|
33
|
+
end
|
34
|
+
else
|
35
|
+
Column.new item
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def bucket
|
40
|
+
@from.bucket
|
41
|
+
end
|
42
|
+
|
43
|
+
def mapper
|
44
|
+
where = @where.to_js
|
45
|
+
|
46
|
+
## don't do 'select * from table group by col'
|
47
|
+
if @select == :all then
|
48
|
+
ERB.new(GetAllMapper).result(binding)
|
49
|
+
|
50
|
+
elsif @group_by and @group_by.col then
|
51
|
+
select = []
|
52
|
+
col = @group_by.col
|
53
|
+
@select.each do |c| select << c.to_map_js end
|
54
|
+
ERB.new(GroupByMapperTemplate).result(binding)
|
55
|
+
|
56
|
+
else
|
57
|
+
select = []
|
58
|
+
@select.each do |c| select << c.to_map_js end
|
59
|
+
ERB.new(MapperTemplate).result(binding)
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def reducer
|
65
|
+
if @select == :all then
|
66
|
+
return nil
|
67
|
+
end
|
68
|
+
if @group_by and @group_by.col then
|
69
|
+
col = @group_by.col
|
70
|
+
return ERB.new(GroupByReducerTemplate).result(binding)
|
71
|
+
end
|
72
|
+
|
73
|
+
@select.each do |c|
|
74
|
+
if c.is_agg? then
|
75
|
+
agg_init, agg_fun = c.to_reduce_js
|
76
|
+
return ERB.new(ReducerTemplate).result(binding)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
return nil
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
def any(arr, fun)
|
85
|
+
arr.each do |e|
|
86
|
+
if fun(e) then
|
87
|
+
return true
|
88
|
+
end
|
89
|
+
end
|
90
|
+
return false
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
@@ -0,0 +1,197 @@
|
|
1
|
+
## syntax tree
|
2
|
+
require 'mohair/sql/select'
|
3
|
+
|
4
|
+
module Mohair
|
5
|
+
|
6
|
+
def Mohair.build tree
|
7
|
+
case tree[:op]
|
8
|
+
when 'select'
|
9
|
+
Select.new tree
|
10
|
+
when 'insert'
|
11
|
+
Insert.new tree
|
12
|
+
when 'update'
|
13
|
+
Update.new tree
|
14
|
+
when 'delete'
|
15
|
+
Delete.new tree
|
16
|
+
else
|
17
|
+
LOG.error "bad :op", tree
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
class Column
|
23
|
+
def initialize item
|
24
|
+
@name = item.to_s
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_map_js
|
28
|
+
"ret.#{@name} = obj.#{@name};"
|
29
|
+
end
|
30
|
+
|
31
|
+
def is_agg?
|
32
|
+
false
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class Function
|
37
|
+
def initialize item
|
38
|
+
@name = item[:function].to_s
|
39
|
+
@argv = []
|
40
|
+
if item[:arguments] == Array then
|
41
|
+
item[:arguments].each do |i|
|
42
|
+
@argv << i[:item].to_s
|
43
|
+
end
|
44
|
+
else
|
45
|
+
@argv << item[:arguments][:item]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_map_js
|
50
|
+
s = @argv[0]
|
51
|
+
case @name
|
52
|
+
when 'sum' then
|
53
|
+
"ret.sum_#{s} = obj.#{s};"
|
54
|
+
when 'avg' then
|
55
|
+
"ret.sum_#{s} = obj.#{s};\n ret.count_#{s} = 1;"
|
56
|
+
when 'count' then
|
57
|
+
if s == 'key' then
|
58
|
+
"if(!!(v.#{s})){ ret.count_#{s} = 1; }"
|
59
|
+
else
|
60
|
+
"if(!!(obj.#{s})){ ret.count_#{s} = 1; }"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def to_reduce_js
|
66
|
+
|
67
|
+
s = @argv[0]
|
68
|
+
case @name
|
69
|
+
when 'sum' then
|
70
|
+
["ret.sum_#{s} = 0;", "if(!!(v.sum_#{s})){ ret.sum_#{s} += v.sum_#{s}; }"]
|
71
|
+
when 'avg' then
|
72
|
+
["ret.sum_#{s} = 0; ret.count_#{s} = 0;",
|
73
|
+
"if(!!(v.sum_#{s})){ ret.sum_#{s} += v.sum_#{s};\n ret.count_#{s} += v.count_#{s}; }"]
|
74
|
+
when 'count' then
|
75
|
+
["ret.count_#{s} = 0;",
|
76
|
+
"if(!!(v.count_#{s})){ ret.count_#{s} += v.count_#{s}; }"]
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
def is_agg?
|
82
|
+
true
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
class Insert
|
87
|
+
end
|
88
|
+
|
89
|
+
class Update
|
90
|
+
end
|
91
|
+
|
92
|
+
class Delete
|
93
|
+
end
|
94
|
+
|
95
|
+
class From
|
96
|
+
def initialize tree
|
97
|
+
@name = tree[:name]
|
98
|
+
end
|
99
|
+
def bucket
|
100
|
+
@name.to_s
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
class Condition
|
105
|
+
def initialize tree
|
106
|
+
if tree[:rhs].nil? && tree[:lhs].class == Hash then
|
107
|
+
tree = tree[:lhs]
|
108
|
+
end
|
109
|
+
lhs = tree[:lhs]
|
110
|
+
rhs = tree[:rhs]
|
111
|
+
@op = tree[:op].to_s
|
112
|
+
|
113
|
+
if lhs.class == Hash then
|
114
|
+
@lhs = Condition.new lhs
|
115
|
+
elsif (lhs.to_s =~ /^[0-9]+$/).nil? then
|
116
|
+
@lhs = lhs.to_s
|
117
|
+
else
|
118
|
+
@lhs = lhs.to_i
|
119
|
+
end
|
120
|
+
|
121
|
+
if rhs.class == Hash then
|
122
|
+
@rhs = Condition.new rhs
|
123
|
+
elsif (rhs.to_s =~ /^[0-9]+$/).nil? then
|
124
|
+
@rhs = rhs.to_s
|
125
|
+
else
|
126
|
+
@rhs = rhs.to_i
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def to_js
|
131
|
+
lhs = rhs = nil
|
132
|
+
if @rhs.class == Fixnum then
|
133
|
+
rhs = @rhs
|
134
|
+
elsif @rhs.class == Condition then
|
135
|
+
rhs = "( #{@rhs.to_js} )"
|
136
|
+
elsif (@rhs[0] == "\"" and @rhs[-1] == "\"") then
|
137
|
+
rhs = @rhs
|
138
|
+
else
|
139
|
+
rhs = "obj.#{@rhs}"
|
140
|
+
end
|
141
|
+
if @lhs.class == Fixnum then
|
142
|
+
lhs = @lhs
|
143
|
+
elsif @lhs.class == Condition then
|
144
|
+
lhs = "( #{@lhs.to_js} )"
|
145
|
+
elsif (@lhs[0] == "\"" and @lhs[-1] == "\"") then
|
146
|
+
lhs = @lhs
|
147
|
+
else
|
148
|
+
lhs = "obj.#{@lhs}"
|
149
|
+
end
|
150
|
+
" (#{lhs}) #{operator2str(@op)} (#{rhs}) "
|
151
|
+
end
|
152
|
+
|
153
|
+
def operator2str op
|
154
|
+
## SQL to JS operator
|
155
|
+
case op
|
156
|
+
when '=' then '=='
|
157
|
+
when '<>' then '!='
|
158
|
+
when "and" then '&&'
|
159
|
+
when 'or' then '||'
|
160
|
+
else op
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
end
|
165
|
+
|
166
|
+
class Where
|
167
|
+
def initialize tree
|
168
|
+
if tree then
|
169
|
+
@cond = Condition.new tree
|
170
|
+
else
|
171
|
+
@cond = nil
|
172
|
+
end
|
173
|
+
end
|
174
|
+
def to_js
|
175
|
+
if @cond.nil? then
|
176
|
+
"return [ret];"
|
177
|
+
else
|
178
|
+
s = @cond.to_js
|
179
|
+
"if(#{s}){ return [ret]; }else{ return[]; }"
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
class Order
|
185
|
+
end
|
186
|
+
|
187
|
+
class Group
|
188
|
+
def initialize col
|
189
|
+
@col = col.to_s
|
190
|
+
end
|
191
|
+
|
192
|
+
def col
|
193
|
+
@col
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
end
|
data/lib/mohair/version.rb
CHANGED
data/lib/mohair.rb
CHANGED
@@ -1,17 +1,87 @@
|
|
1
|
+
require "mohair/sql/parser"
|
2
|
+
require "mohair/sql/tree"
|
3
|
+
|
1
4
|
require "mohair/version"
|
2
5
|
require "mohair/selector"
|
6
|
+
require "mohair/inserter"
|
7
|
+
|
8
|
+
require "json"
|
9
|
+
require "optparse"
|
10
|
+
require "logger"
|
11
|
+
|
12
|
+
LOG = Logger.new(STDERR)
|
13
|
+
LOG.level = Logger::DEBUG # WARN, INFO, DEBUG, ...
|
3
14
|
|
4
15
|
module Mohair
|
5
|
-
|
16
|
+
|
17
|
+
def self.usage
|
18
|
+
print <<EOS
|
19
|
+
usage:
|
20
|
+
$ mohair -q "select foo, bar from bucket_name" [-i INDEX]
|
21
|
+
$ mohair_dump <bucket_name> < sample_data.json
|
22
|
+
|
23
|
+
insert, delete sentence is future work
|
24
|
+
mohair version #{Mohair::VERSION}
|
25
|
+
EOS
|
26
|
+
exit -1
|
27
|
+
end
|
28
|
+
|
6
29
|
def self.main
|
7
|
-
|
30
|
+
|
31
|
+
q = nil #query!!
|
32
|
+
index = nil
|
33
|
+
|
34
|
+
opt = OptionParser.new
|
35
|
+
opt.on('-q Q'){|v| q = v}
|
36
|
+
opt.on('-i INDEX'){|v| index = v}
|
37
|
+
opt.on('-h', '--help'){ usage }
|
38
|
+
|
39
|
+
opt.parse!(ARGV)
|
40
|
+
|
41
|
+
parser = Sql::Parser.new
|
42
|
+
sql_syntax_tree = parser.parse (q.strip)
|
43
|
+
LOG.debug sql_syntax_tree
|
44
|
+
|
45
|
+
case sql_syntax_tree[:op]
|
8
46
|
when 'select'
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
47
|
+
|
48
|
+
s = (Mohair.build sql_syntax_tree)
|
49
|
+
|
50
|
+
LOG.debug "mapper->\n"
|
51
|
+
LOG.debug s.mapper
|
52
|
+
LOG.debug "reducer->\n"
|
53
|
+
LOG.debug s.reducer
|
54
|
+
|
55
|
+
client = Riak::Client.new(:protocol => "http")
|
56
|
+
bucket = Riak::MapReduce.new(client)
|
57
|
+
.add(client.bucket(s.bucket))## keyfilsters and so on here
|
58
|
+
|
59
|
+
reducer = s.reducer
|
60
|
+
result = nil
|
61
|
+
if reducer.nil? then
|
62
|
+
result = bucket.map(s.mapper, :keep => true)
|
63
|
+
.run
|
64
|
+
else
|
65
|
+
result = bucket.map(s.mapper, :keep => false)
|
66
|
+
.reduce(reducer, :keep => true)
|
67
|
+
.run
|
68
|
+
end
|
69
|
+
|
70
|
+
#LOG.debug "raw query result> #{result}"
|
71
|
+
LOG.info "query result:"
|
72
|
+
format_result result
|
73
|
+
|
74
|
+
# when :insert
|
75
|
+
# when :show
|
76
|
+
when :create
|
77
|
+
LOG.error "CREATE sentence is unavailable at mohair"
|
13
78
|
else
|
14
|
-
|
79
|
+
LOG.error "bad query: ", result, "\n"
|
15
80
|
end
|
16
81
|
end
|
82
|
+
|
83
|
+
def self.do_dump
|
84
|
+
objs = JSON.load(STDIN)
|
85
|
+
Inserter.new(ARGV[0]).insert_all(objs)
|
86
|
+
end
|
17
87
|
end
|
data/sample_data.json
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
[{
|
2
|
+
"key" : "ham",
|
3
|
+
"data" : {
|
4
|
+
"name" : "john smith",
|
5
|
+
"age" : 42,
|
6
|
+
"gender" : "male"
|
7
|
+
}
|
8
|
+
},
|
9
|
+
{
|
10
|
+
"key" : "hamspam",
|
11
|
+
"data" : {
|
12
|
+
"name" : "john smith",
|
13
|
+
"age" : 42,
|
14
|
+
"gender" : "male"
|
15
|
+
}
|
16
|
+
},
|
17
|
+
{
|
18
|
+
"key" : "eggspam",
|
19
|
+
"data" : {
|
20
|
+
"name" : "joanna smith",
|
21
|
+
"age" : 42,
|
22
|
+
"gender" : "female"
|
23
|
+
}
|
24
|
+
},
|
25
|
+
{
|
26
|
+
"key" : "adam_smith2",
|
27
|
+
"data" : {
|
28
|
+
"name" : "john smith",
|
29
|
+
"age" : 42,
|
30
|
+
"gender" : "male"
|
31
|
+
}
|
32
|
+
},
|
33
|
+
{
|
34
|
+
"key" : "adam_smith",
|
35
|
+
"data" : {
|
36
|
+
"name" : "Adam smith",
|
37
|
+
"age" : 456,
|
38
|
+
"gender" : "male"
|
39
|
+
}
|
40
|
+
}]
|
data/test/plugin/sql.rb
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
# require 'parslet'
|
3
|
+
|
4
|
+
class ParserTest < MiniTest::Unit::TestCase
|
5
|
+
#include Mohair
|
6
|
+
|
7
|
+
def setup
|
8
|
+
@parser = Mohair::Sql::Parser.new
|
9
|
+
end
|
10
|
+
def teardown
|
11
|
+
end
|
12
|
+
def test_simples # check syntax parser with parslet
|
13
|
+
[
|
14
|
+
'select a from b',
|
15
|
+
'select a, b from comme',
|
16
|
+
'select b from far',
|
17
|
+
'select c from d',
|
18
|
+
'select a, c, d,e,f,f from b ',
|
19
|
+
'select count(a) from d',
|
20
|
+
].each do |sql|
|
21
|
+
s = @parser.parse sql
|
22
|
+
assert_equal(expected = "select", actual = s[:op])
|
23
|
+
assert(! s[:select].nil?)
|
24
|
+
assert(! s[:from].nil?)
|
25
|
+
assert(! (Mohair.build s).nil?)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_bad_sql
|
30
|
+
[
|
31
|
+
'select',
|
32
|
+
' select a from b',
|
33
|
+
].each do |bad_sql|
|
34
|
+
assert_raises Parslet::ParseFailed do
|
35
|
+
@parser.parse bad_sql
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_where
|
41
|
+
[
|
42
|
+
'select a from b where a > 20',
|
43
|
+
'select a,b from c where a = 20',
|
44
|
+
'select a,b from c where 20 < a and b < 234',
|
45
|
+
'select a,b from c where 20 < a or b = 234',
|
46
|
+
'select a, b from c where a = "oo"',
|
47
|
+
'select a, b from c where a = "oo" and c > 235',
|
48
|
+
].each do |where_sql|
|
49
|
+
s = @parser.parse where_sql
|
50
|
+
assert_equal(expected = "select", actual = s[:op])
|
51
|
+
assert(! s[:select].nil?)
|
52
|
+
assert(! s[:from].nil?)
|
53
|
+
assert(! s[:where].nil?)
|
54
|
+
assert(! (Mohair.build s).nil?)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_group_by
|
59
|
+
[
|
60
|
+
'select a from b group by c',
|
61
|
+
'select a from b where a > 345 group by c',
|
62
|
+
'select a from b where a > 345 and foo = "hoge" group by c',
|
63
|
+
].each do |where_sql|
|
64
|
+
s = @parser.parse where_sql
|
65
|
+
assert_equal(expected = "select", actual = s[:op])
|
66
|
+
assert(! s[:select].nil?)
|
67
|
+
assert(! s[:from].nil?)
|
68
|
+
assert(! s[:group_by].nil?)
|
69
|
+
assert(! (Mohair.build s).nil?)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
CHANGED
@@ -1,8 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mohair
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
+
hash: 25
|
4
5
|
prerelease:
|
5
|
-
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 3
|
10
|
+
version: 0.0.3
|
6
11
|
platform: ruby
|
7
12
|
authors:
|
8
13
|
- UENISHI Kota
|
@@ -10,7 +15,7 @@ autorequire:
|
|
10
15
|
bindir: bin
|
11
16
|
cert_chain: []
|
12
17
|
|
13
|
-
date: 2013-
|
18
|
+
date: 2013-05-25 00:00:00 Z
|
14
19
|
dependencies: []
|
15
20
|
|
16
21
|
description: a new type of Riak client
|
@@ -18,6 +23,7 @@ email:
|
|
18
23
|
- kuenishi@gmail.com
|
19
24
|
executables:
|
20
25
|
- mohair
|
26
|
+
- mohair_dump
|
21
27
|
extensions: []
|
22
28
|
|
23
29
|
extra_rdoc_files: []
|
@@ -29,10 +35,19 @@ files:
|
|
29
35
|
- README.md
|
30
36
|
- Rakefile
|
31
37
|
- bin/mohair
|
38
|
+
- bin/mohair_dump
|
32
39
|
- lib/mohair.rb
|
40
|
+
- lib/mohair/inserter.rb
|
33
41
|
- lib/mohair/selector.rb
|
42
|
+
- lib/mohair/sql/parser.rb
|
43
|
+
- lib/mohair/sql/select.rb
|
44
|
+
- lib/mohair/sql/tree.rb
|
34
45
|
- lib/mohair/version.rb
|
35
46
|
- mohair.gemspec
|
47
|
+
- sample_data.json
|
48
|
+
- test/plugin/selector.rb
|
49
|
+
- test/plugin/sql.rb
|
50
|
+
- test/test_helper.rb
|
36
51
|
homepage: ""
|
37
52
|
licenses: []
|
38
53
|
|
@@ -46,19 +61,27 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
46
61
|
requirements:
|
47
62
|
- - ">="
|
48
63
|
- !ruby/object:Gem::Version
|
64
|
+
hash: 3
|
65
|
+
segments:
|
66
|
+
- 0
|
49
67
|
version: "0"
|
50
68
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
51
69
|
none: false
|
52
70
|
requirements:
|
53
71
|
- - ">="
|
54
72
|
- !ruby/object:Gem::Version
|
73
|
+
hash: 3
|
74
|
+
segments:
|
75
|
+
- 0
|
55
76
|
version: "0"
|
56
77
|
requirements: []
|
57
78
|
|
58
79
|
rubyforge_project:
|
59
|
-
rubygems_version: 1.8.
|
80
|
+
rubygems_version: 1.8.25
|
60
81
|
signing_key:
|
61
82
|
specification_version: 3
|
62
83
|
summary: a new type of a riak client
|
63
|
-
test_files:
|
64
|
-
|
84
|
+
test_files:
|
85
|
+
- test/plugin/selector.rb
|
86
|
+
- test/plugin/sql.rb
|
87
|
+
- test/test_helper.rb
|