marbu 0.1.0
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/Rakefile +13 -0
- data/Readme.md +58 -0
- data/bin/marbu-web +20 -0
- data/bin/marbu-web-mongo-config.rb +8 -0
- data/lib/core_ext/array.rb +7 -0
- data/lib/core_ext/enumerable.rb +25 -0
- data/lib/core_ext/hash.rb +35 -0
- data/lib/core_ext/indifferent_access.rb +11 -0
- data/lib/core_ext/object.rb +25 -0
- data/lib/core_ext/open_struct.rb +3 -0
- data/lib/core_ext/string.rb +25 -0
- data/lib/marbu/builder.rb +48 -0
- data/lib/marbu/builders/mongodb.rb +106 -0
- data/lib/marbu/exceptions.rb +7 -0
- data/lib/marbu/formatters/base.rb +6 -0
- data/lib/marbu/formatters/dummy.rb +9 -0
- data/lib/marbu/formatters/formatters.rb +3 -0
- data/lib/marbu/formatters/one_line.rb +9 -0
- data/lib/marbu/models/db/db.rb +3 -0
- data/lib/marbu/models/db/mongodb/exceptions.rb +59 -0
- data/lib/marbu/models/db/mongodb/mongodb.rb +54 -0
- data/lib/marbu/models/db/mongodb/structure.rb +40 -0
- data/lib/marbu/models/exception_link.rb +11 -0
- data/lib/marbu/models/models.rb +3 -0
- data/lib/marbu/models/mrf/base.rb +133 -0
- data/lib/marbu/models/mrf/code.rb +32 -0
- data/lib/marbu/models/mrf/finalize.rb +9 -0
- data/lib/marbu/models/mrf/map.rb +7 -0
- data/lib/marbu/models/mrf/map_reduce_finalize.rb +107 -0
- data/lib/marbu/models/mrf/misc.rb +65 -0
- data/lib/marbu/models/mrf/mrf.rb +8 -0
- data/lib/marbu/models/mrf/query.rb +39 -0
- data/lib/marbu/models/mrf/reduce.rb +7 -0
- data/lib/marbu/server/public/css/style.css +149 -0
- data/lib/marbu/server/public/images/remove.png +0 -0
- data/lib/marbu/server/public/js/builder.js +200 -0
- data/lib/marbu/server/views/_builder_col_code.haml +8 -0
- data/lib/marbu/server/views/_builder_col_emit_keys.haml +13 -0
- data/lib/marbu/server/views/_builder_col_emit_values.haml +12 -0
- data/lib/marbu/server/views/_builder_data_samples.haml +8 -0
- data/lib/marbu/server/views/_builder_misc.haml +20 -0
- data/lib/marbu/server/views/_collection_nested_list.haml +0 -0
- data/lib/marbu/server/views/_footer.haml +2 -0
- data/lib/marbu/server/views/_header.haml +3 -0
- data/lib/marbu/server/views/_header_col.haml +6 -0
- data/lib/marbu/server/views/builder.haml +37 -0
- data/lib/marbu/server/views/layout.haml +19 -0
- data/lib/marbu/server/views/mapreduce.haml +9 -0
- data/lib/marbu/server/views/root.haml +11 -0
- data/lib/marbu/server/views/sample_data.haml +17 -0
- data/lib/marbu/server.rb +199 -0
- data/lib/marbu/server_sinatra.rb +168 -0
- data/lib/marbu/version.rb +3 -0
- data/lib/marbu.rb +65 -0
- data/spec/constants.rb +149 -0
- data/spec/rubymine-server.rb +0 -0
- data/spec/spec_helper.rb +63 -0
- data/spec/unit/builders/builder_spec.rb +5 -0
- data/spec/unit/builders/mongodb_spec.rb +52 -0
- data/spec/unit/models/base_spec.rb +33 -0
- data/spec/unit/models/map_reduce_finalize_spec.rb +28 -0
- data/spec/unit/models/map_spec.rb +22 -0
- data/spec/unit/models/query_spec.rb +23 -0
- metadata +209 -0
data/Rakefile
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
require 'bundler'
|
|
2
|
+
Bundler.setup
|
|
3
|
+
|
|
4
|
+
require "rake"
|
|
5
|
+
require "rspec"
|
|
6
|
+
require "rspec/core/rake_task"
|
|
7
|
+
|
|
8
|
+
RSpec::Core::RakeTask.new("spec:unit") do |spec|
|
|
9
|
+
spec.pattern = "spec/unit/**/*_spec.rb"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
task :spec => [ "spec:unit" ]
|
|
13
|
+
task :default => :spec
|
data/Readme.md
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
MA(p) R(educe) BU(ilder)
|
|
2
|
+
========================
|
|
3
|
+
|
|
4
|
+
Marbu enables you to add mapreduce code-generating functionality to your software within seconds.
|
|
5
|
+
|
|
6
|
+
What does it do?
|
|
7
|
+
----------------
|
|
8
|
+
|
|
9
|
+
Marbu contains three destinct parts
|
|
10
|
+
|
|
11
|
+
1. The MapReduceFinalize-Model which acts as an Interface for the MapReduceBuilder
|
|
12
|
+
2. The map-reduce core functionality: this creates map reduce code for different map reduce systems
|
|
13
|
+
3. A Sinatra App which lets you instantly play around with the builder and shows you what you can do.
|
|
14
|
+
|
|
15
|
+
Installation
|
|
16
|
+
-----------------
|
|
17
|
+
Install with 'gem install marbu'.
|
|
18
|
+
|
|
19
|
+
For bundler add "gem 'marbu'" to your Gemfile.
|
|
20
|
+
|
|
21
|
+
Stand alone usage
|
|
22
|
+
-----------------
|
|
23
|
+
|
|
24
|
+
Marbu comes complete with a little Sinatra App to help you along. You start the app with 'marbu-web marbu-web-mongo-config.rb'.
|
|
25
|
+
This will start Marbu with the provided MongoDB-Configuration file.
|
|
26
|
+
|
|
27
|
+
If everything is working, you can go to 'localhost:5678' (if you are already running something on 5678 then try 5679) and
|
|
28
|
+
you should see all databases in your local MongoDB installation.
|
|
29
|
+
|
|
30
|
+
After selecting a Database you will see all collections within this database. You select a collection and go into the 'marbu' mode.
|
|
31
|
+
In the 'marbu' mode you can build your map - reduce - finalize functionality. After that run your query and see what you get. Done!
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
## Development
|
|
35
|
+
|
|
36
|
+
Clone the repo
|
|
37
|
+
|
|
38
|
+
$ git clone git@github.com:QEDio/qstate.git
|
|
39
|
+
|
|
40
|
+
Update submodules
|
|
41
|
+
|
|
42
|
+
$ git submodule init && git submodule update
|
|
43
|
+
|
|
44
|
+
Install dependencies
|
|
45
|
+
|
|
46
|
+
$ bundle install
|
|
47
|
+
|
|
48
|
+
Run the tests
|
|
49
|
+
|
|
50
|
+
$ rake
|
|
51
|
+
|
|
52
|
+
Running the built-in Sinatra server can be done using the method above or using a standard config.ru file just like any other Rack-based app. Just make sure to provide the config file path through the corresponding environment variable:
|
|
53
|
+
|
|
54
|
+
$ MARBUCONFIG=bin/marbu-web-mongo-config.rb rackup
|
|
55
|
+
|
|
56
|
+
You can also activate the debugger and pick a specific server if you want:
|
|
57
|
+
|
|
58
|
+
$ MARBUCONFIG=bin/marbu-web-mongo-config.rb thin --debug --rackup config.ru start
|
data/bin/marbu-web
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
|
|
4
|
+
begin
|
|
5
|
+
require 'vegas'
|
|
6
|
+
rescue LoadError
|
|
7
|
+
require 'rubygems'
|
|
8
|
+
require 'vegas'
|
|
9
|
+
end
|
|
10
|
+
require 'marbu/server'
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
Vegas::Runner.new(Marbu::Server, 'marbu-web', {
|
|
14
|
+
:before_run => lambda {|v|
|
|
15
|
+
path = (ENV['MARBUCONFIG'] || v.args.first)
|
|
16
|
+
load path.to_s.strip if path
|
|
17
|
+
}
|
|
18
|
+
}) do |runner, opts, app|
|
|
19
|
+
|
|
20
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module Enumerable
|
|
2
|
+
def mean
|
|
3
|
+
sum / size
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
# returns a new hash/array
|
|
7
|
+
# keeps object references in tact
|
|
8
|
+
# symbolize a hash recursivly
|
|
9
|
+
# if we encounter an array, we will also call symbolize for every hash or array found
|
|
10
|
+
def symbolize_keys_rec
|
|
11
|
+
if is_a?(Hash)
|
|
12
|
+
inject({}) do |options, (key, value)|
|
|
13
|
+
options[(key.to_sym rescue key) || key] = (value.is_a?(Hash)||value.is_a?(Array)) ? value.symbolize_keys_rec : value
|
|
14
|
+
options
|
|
15
|
+
end
|
|
16
|
+
elsif is_a?(Array)
|
|
17
|
+
inject([]) do |options, value|
|
|
18
|
+
options << ((value.is_a?(Hash)||value.is_a?(Array)) ? value.symbolize_keys_rec : value)
|
|
19
|
+
options
|
|
20
|
+
end
|
|
21
|
+
else
|
|
22
|
+
raise Exception.new("Can't do that!")
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
class Hash
|
|
2
|
+
def has_nested_key?(key)
|
|
3
|
+
return true if has_key?(key)
|
|
4
|
+
values.select{|v| v.is_a?(Hash)}.each do |value|
|
|
5
|
+
return value.has_nested_key?(key)
|
|
6
|
+
end
|
|
7
|
+
return false
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def nested_key(key)
|
|
11
|
+
return fetch(key) if has_key?(key)
|
|
12
|
+
values.select{|v| v.is_a?(Hash)}.each do |value|
|
|
13
|
+
return value.nested_key(key)
|
|
14
|
+
end
|
|
15
|
+
return default(key)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def deep_merge(other_hash)
|
|
19
|
+
a = self.dup
|
|
20
|
+
other_hash.each do |k, v|
|
|
21
|
+
if v.is_a? Hash
|
|
22
|
+
if a.has_key? k
|
|
23
|
+
a[k] = a[k].deep_merge(v)
|
|
24
|
+
else
|
|
25
|
+
a[k] = v
|
|
26
|
+
end
|
|
27
|
+
else
|
|
28
|
+
a[k] = v
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
a
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
end
|
|
35
|
+
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module CallerMethodName
|
|
2
|
+
|
|
3
|
+
private
|
|
4
|
+
|
|
5
|
+
def current_method
|
|
6
|
+
caller[0] =~ /\d:in `([^']+)'/
|
|
7
|
+
$1
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def caller_method_name
|
|
11
|
+
parse_caller(caller(2).first).last
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def parse_caller(at)
|
|
15
|
+
if /^(.+?):(\d+)(?::in `(.*)')?/ =~ at
|
|
16
|
+
file = Regexp.last_match[1]
|
|
17
|
+
line = Regexp.last_match[2].to_i
|
|
18
|
+
method = Regexp.last_match[3]
|
|
19
|
+
[file, line, method]
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
Object.__send__(:include, CallerMethodName)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
class String
|
|
2
|
+
# Truncates the string after the first +chars+ (100) at word boundary and
|
|
3
|
+
# adds +ending+ ("...").
|
|
4
|
+
def truncate_at_word chars=100, ending="..."
|
|
5
|
+
sub(/^(.{#{chars}}[\w.]*)(.*)/m){ $2.empty? ? $1 : $1 + ending}
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
# returns a url/ascii/old-systems/non-utf8 friendly representation of self
|
|
9
|
+
def callname
|
|
10
|
+
@_callname ||= dup.
|
|
11
|
+
gsub(/[Ää]+/i,'ae').
|
|
12
|
+
gsub(/[Üü]+/i,'ue').
|
|
13
|
+
gsub(/[Öö]+/i,'oe').
|
|
14
|
+
gsub(/[ß]+/i,'ss').
|
|
15
|
+
downcase.
|
|
16
|
+
gsub(/[^a-z0-9]+/i, '-').
|
|
17
|
+
gsub(/(^[-]+|[-]+$)/, '')
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def to_bool
|
|
21
|
+
return true if match('true')
|
|
22
|
+
return false
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
module Marbu
|
|
4
|
+
# the builder class is a lightweight container that holds a MapReduceModel and the requester Builder class
|
|
5
|
+
# its basically a front for the internals of the builders
|
|
6
|
+
class Builder
|
|
7
|
+
attr_accessor :map_reduce_model
|
|
8
|
+
|
|
9
|
+
def initialize(mrm, options = {})
|
|
10
|
+
if mrm.is_a?(Marbu::Models::MapReduceFinalize)
|
|
11
|
+
@map_reduce_model = mrm
|
|
12
|
+
elsif mrm.is_a?(Hash)
|
|
13
|
+
@map_reduce_model = Marbu::Models::MapReduceFinalize.new(mrm)
|
|
14
|
+
else
|
|
15
|
+
raise Exception.new("Parameter mrm was neither of type MapReduceFinalize nor Hash, but #{mrm.class}. Aborting")
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
@builder_clasz = options[:builder] || Marbu::Builder::Mongodb
|
|
19
|
+
@formatter_clasz = options[:formatter] || Marbu::Formatter::Dummy
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def map(format = :text)
|
|
23
|
+
@formatter_clasz.perform(@builder_clasz.map(@map_reduce_model.map, format))
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def reduce(format = :text)
|
|
27
|
+
@formatter_clasz.perform(@builder_clasz.reduce(@map_reduce_model.reduce, format))
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def finalize(format = :text)
|
|
31
|
+
@formatter_clasz.perform(@builder_clasz.finalize(@map_reduce_model.finalize, format))
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def query
|
|
35
|
+
@map_reduce_model.query.condition
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def log
|
|
39
|
+
self.to_s
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def to_s
|
|
43
|
+
puts "Map: #{map}"
|
|
44
|
+
puts "Reduce: #{reduce}"
|
|
45
|
+
puts "Finalize: #{finalize}"
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
module Marbu
|
|
2
|
+
class Builder
|
|
3
|
+
class Mongodb
|
|
4
|
+
def self.map(map, format)
|
|
5
|
+
case format
|
|
6
|
+
when :text then map_int(map)
|
|
7
|
+
when :mongodb then cleanup(map_int(map))
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.reduce(reduce, format)
|
|
12
|
+
case format
|
|
13
|
+
when :text then self.reduce_int(reduce)
|
|
14
|
+
when :mongodb then cleanup(self.reduce_int(reduce))
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def self.finalize(finalize, format)
|
|
20
|
+
case format
|
|
21
|
+
when :text then self.finalize_int(finalize)
|
|
22
|
+
when :mongodb then cleanup(self.finalize_int(finalize))
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
def self.map_int(map)
|
|
28
|
+
<<-JS
|
|
29
|
+
function(){
|
|
30
|
+
#{get_value(:map)}
|
|
31
|
+
#{map.code.text}
|
|
32
|
+
#{get_emit(:map, map.values, map.keys)}
|
|
33
|
+
}
|
|
34
|
+
JS
|
|
35
|
+
end
|
|
36
|
+
def self.reduce_int(reduce)
|
|
37
|
+
<<-JS
|
|
38
|
+
function(key,values){
|
|
39
|
+
#{get_value(:reduce)}
|
|
40
|
+
#{reduce.code.text}
|
|
41
|
+
#{get_emit(:reduce, reduce.values)}
|
|
42
|
+
}
|
|
43
|
+
JS
|
|
44
|
+
end
|
|
45
|
+
def self.finalize_int(finalize)
|
|
46
|
+
<<-JS
|
|
47
|
+
function(key, value){
|
|
48
|
+
#{finalize.code.text}
|
|
49
|
+
#{get_emit(:finalize, finalize.values)}
|
|
50
|
+
}
|
|
51
|
+
JS
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def self.cleanup(code)
|
|
55
|
+
code.gsub!(/[\n|\r]/,'')
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def self.get_value(function)
|
|
59
|
+
case function
|
|
60
|
+
when :map then "value=this.value;"
|
|
61
|
+
when :reduce then "value=values[0];"
|
|
62
|
+
else raise Exception.new("Value-foo for #{function} not defined!")
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def self.get_emit(function, values, keys = nil)
|
|
67
|
+
case function
|
|
68
|
+
when :map then emit_map(keys, values)
|
|
69
|
+
when :reduce then emit_reduce(values)
|
|
70
|
+
when :finalize then emit_finalize(values)
|
|
71
|
+
else raise Exception.new("Emit for #{function} not defined!")
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def self.emit_map(keys, values)
|
|
76
|
+
emit = "emit( #{emit_keys(keys)}, "
|
|
77
|
+
emit += emit_core(values)
|
|
78
|
+
emit += " );"
|
|
79
|
+
return emit
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def self.emit_reduce(values)
|
|
83
|
+
"return " + emit_core(values) + ";"
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def self.emit_finalize(values)
|
|
87
|
+
"return " + emit_core(values) + ";"
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def self.emit_keys(keys)
|
|
91
|
+
emit_core(keys)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def self.emit_core(values)
|
|
95
|
+
core = "{ "
|
|
96
|
+
values.each do |v|
|
|
97
|
+
core += " #{v.name}: #{v.function||v.name},"
|
|
98
|
+
end
|
|
99
|
+
# delete last comma
|
|
100
|
+
core = core[0..core.size-2]
|
|
101
|
+
core += " }"
|
|
102
|
+
return core
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
module Marbu
|
|
2
|
+
module Models
|
|
3
|
+
module Db
|
|
4
|
+
class MongoDb
|
|
5
|
+
class Exception < StandardError
|
|
6
|
+
REFERENCE_ERROR = 'ReferenceError'
|
|
7
|
+
NS_ERROR = 'NS_ERROR'
|
|
8
|
+
REGEX_DB_COMMAND = { :regex => /(ns doesn't exist)/,
|
|
9
|
+
:mapping => { :function => {:type => :string, :value => 'Buu'},
|
|
10
|
+
:error_type => {:type => :string, :value => NS_ERROR},
|
|
11
|
+
:variable => {:type => :string, :value => 'Buu'},
|
|
12
|
+
:assertion_code => {:type => :string, :value => 'Buu'},
|
|
13
|
+
:error_message => {:type => :string, :value => 'Buu'} }}
|
|
14
|
+
#REGEX_REF_ERROR = { :regex => /(.*?)'(.*?)'(.*?): \((assertion): '(.*?) (.*?): (.*?): (ReferenceError): (.*?)(is.*?)(nofile_b)?:\d'; (assertionCode): '(.*)';.*(errmsg): '(.*?)'/,
|
|
15
|
+
# mapping => { :function => 5, :error_type => 8, :variable => 9, :assertion_code => 13, :error_message => 15 }}}
|
|
16
|
+
REGEXS = [ REGEX_DB_COMMAND ]
|
|
17
|
+
|
|
18
|
+
def self.explain(e, mrf)
|
|
19
|
+
parsed_exception = parse(e)
|
|
20
|
+
|
|
21
|
+
case parsed_exception[:error_type]
|
|
22
|
+
when REFERENCE_ERROR then explain_reference_error(parsed_exception, mrf)
|
|
23
|
+
when NS_ERROR then { :id => NS_ERROR, :message => explain_ns_error(parsed_exception, mrf)}
|
|
24
|
+
|
|
25
|
+
else raise Exception.new("The Error #{e.to_s} is unknown to me. Parsed: #{parsed_exception.inspect}")
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def self.parse(e)
|
|
30
|
+
{}.tap do |parsed_exception|
|
|
31
|
+
# http://rubular.com/r/MYM9ADhz4Z
|
|
32
|
+
REGEXS.each do |regex|
|
|
33
|
+
e.result["errmsg"] =~ regex[:regex]
|
|
34
|
+
if( $1.present? )
|
|
35
|
+
parsed_exception[:orig_error] = e
|
|
36
|
+
[:function, :error_type, :variable, :assertion_code, :error_message].each do |key|
|
|
37
|
+
case regex[:mapping][key][:type]
|
|
38
|
+
when :string then parsed_exception[key] = regex[:mapping][key][:value]
|
|
39
|
+
else "Unknown type: #{regex[:mapping][key][:type]}"
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
break
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def self.explain_ns_error( parsed_exception, mrf )
|
|
49
|
+
"Either the database #{mrf.misc.database} or the collection #{mrf.misc.input_collection} is unknown."
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def self.explain_reference_error( parsed_exception, mrf )
|
|
53
|
+
"The variable #{parsed_exception[:variable]} defined in function #{parsed_exception[:function]} is unknown. This either means you are trying to access a datafield that doesn't exist in the collection, or you misspelled a variable name."
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
require 'mongoid'
|
|
4
|
+
require 'uuid'
|
|
5
|
+
|
|
6
|
+
module Marbu
|
|
7
|
+
module Models
|
|
8
|
+
module Db
|
|
9
|
+
class MongoDb
|
|
10
|
+
include Mongoid::Document
|
|
11
|
+
store_in :marbu
|
|
12
|
+
|
|
13
|
+
field :uuid, type: String
|
|
14
|
+
field :name, type: String
|
|
15
|
+
field :map_reduce_finalize, type: MapReduceFinalize
|
|
16
|
+
|
|
17
|
+
def initialize( ext_params = {} )
|
|
18
|
+
super
|
|
19
|
+
params = default_params.merge( ext_params.delete_if{|k,v| v.blank? })
|
|
20
|
+
|
|
21
|
+
self.uuid = params[:uuid]
|
|
22
|
+
self.name = params[:name]
|
|
23
|
+
self.map_reduce_finalize = MapReduceFinalize.new( params[:map_reduce_finalize] )
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def default_params
|
|
27
|
+
{
|
|
28
|
+
:name => "NoName00",
|
|
29
|
+
:uuid => UUID.new.generate(:compact),
|
|
30
|
+
:map_reduce_finalize => {
|
|
31
|
+
:map => {
|
|
32
|
+
:keys => [{:name => "map_key1"}],
|
|
33
|
+
:values => [{:name => "map_value1"}]
|
|
34
|
+
},
|
|
35
|
+
:reduce => {
|
|
36
|
+
:keys => [{:name => "reduce_key1"}],
|
|
37
|
+
:values => [{:name => "reduce_value1"}]
|
|
38
|
+
},
|
|
39
|
+
:finalize => {
|
|
40
|
+
:keys => [{:name => "finalize_key1"}],
|
|
41
|
+
:values => [{:name => "finalize"}]
|
|
42
|
+
},
|
|
43
|
+
:misc => {
|
|
44
|
+
:database => 'database',
|
|
45
|
+
:input_collection => 'input_collection',
|
|
46
|
+
:output_collection => 'output_collection'
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
module Marbu
|
|
2
|
+
module Models
|
|
3
|
+
module Db
|
|
4
|
+
class MongoDb
|
|
5
|
+
class Structure
|
|
6
|
+
def self.get_collection_structure( collection_info )
|
|
7
|
+
raise "collection_info needs to be Marbu::Models::Misc, but is: #{collection_info.class}" unless collection_info.is_a?(Marbu::Models::Misc)
|
|
8
|
+
|
|
9
|
+
collection = collection_info.collection
|
|
10
|
+
document = collection.find().first()
|
|
11
|
+
|
|
12
|
+
get_collection_structure_int(document)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def self.get_first_and_last_document( collection_info )
|
|
16
|
+
raise "collection_info needs to be Marbu::Models::Misc, but is: #{collection_info.class}" unless collection_info.is_a?(Marbu::Models::Misc)
|
|
17
|
+
|
|
18
|
+
collection = collection_info.collection
|
|
19
|
+
|
|
20
|
+
[collection.find().sort([['_id', 1]]).first(), collection.find().sort([['_id', -1]]).first()]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
def self.get_collection_structure_int(document)
|
|
26
|
+
{}.tap do |hsh|
|
|
27
|
+
document.each_pair do |key, value|
|
|
28
|
+
if( value.is_a?(Hash) )
|
|
29
|
+
hsh[key] = get_collection_structure_int(value)
|
|
30
|
+
else
|
|
31
|
+
hsh[key] = key
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|