mohair 0.0.2 → 0.0.3
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/.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
|