mongoid-mapreduce 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -59,9 +59,7 @@ divs.has_key?('Sales') # => true
59
59
  divs.to_hash # => { "Software" => ..., "Hardware" => ..., "Sales" => ... }
60
60
 
61
61
  # Array Value formula: produces 6 records, one for each room.
62
- rooms = Employee.map_reduce do
63
- field :rooms, :formula => :array_values
64
- end
62
+ rooms = Employee.map_reduce(:rooms, :formula => :array_values)
65
63
  rooms.length # => 6
66
64
  rooms.find(1)._count # => 3
67
65
  rooms.counts["5"] # => 2
@@ -3,4 +3,5 @@ require 'mongoid/mapreduce/base'
3
3
  require 'mongoid/mapreduce/document'
4
4
  require 'mongoid/mapreduce/reducer'
5
5
  require 'mongoid/mapreduce/results'
6
+ require 'mongoid/mapreduce/serialization'
6
7
  require 'mongoid/mapreduce/version'
@@ -11,18 +11,8 @@ module Mongoid
11
11
  #
12
12
  # Returns a Hash of results
13
13
  def map_reduce(map_key=:_id, options={}, &block)
14
- reducer = Reducer.new(self, criteria.selector, map_key)
15
-
16
- if options.key?(:count_field)
17
- reducer.count_field = options[:count_field].to_sym
18
- end
19
-
20
- if options.key?(:fields)
21
- options[:fields].each do |f|
22
- reducer.field f.to_sym
23
- end
24
- end
25
-
14
+ options[:map_key] = map_key
15
+ reducer = Reducer.new(self, criteria.selector, options)
26
16
  reducer.instance_eval(&block) if block.present?
27
17
  reducer.run
28
18
  end
@@ -0,0 +1,66 @@
1
+ require 'mongoid/mapreduce/serialization'
2
+
3
+ module Mongoid
4
+ module MapReduce
5
+ module Formula
6
+
7
+ class AggregateFields
8
+ include Mongoid::MapReduce::Serialization
9
+
10
+ def initialize(fields, options={})
11
+ options[:map_key] ||= :_id
12
+ options[:count_field] ||= :_count
13
+
14
+ @map_key = options[:map_key]
15
+ @count_field = options[:count_field]
16
+ @fields = fields
17
+ end
18
+
19
+ # Generate a map function
20
+ # Emits the map key with an array of field values
21
+ #
22
+ # Returns String
23
+ def map
24
+ fn = "function() { "
25
+ fn << "emit (this.#{@map_key}, [#{[1, @fields.collect{|k,v| "this.#{k}"}].flatten.join(", ")}]); "
26
+ fn << "}"
27
+ end
28
+
29
+ # Generates a reduce function
30
+ # Adds each value in the given array
31
+ #
32
+ # Returns String
33
+ def reduce
34
+ fn = "function(k, v) { "
35
+ fn << "var results = [#{(["0"] * (@fields.length + 1)).flatten.join(", ")}]; "
36
+ fn << "v.forEach(function(val) { "
37
+ fn << "for(var i=0; i<= #{@fields.length}; i++) { "
38
+ fn << "results[i] += (typeof val[i] == Boolean) ? (val[i] ? 1 : 0) : val[i] "
39
+ fn << "} "
40
+ fn << "}); "
41
+ fn << "return results.toString(); "
42
+ fn << "}"
43
+ end
44
+
45
+ # Process the results of a given collection
46
+ #
47
+ # collection - the MongoDB collection returned from the map/reduce op
48
+ #
49
+ # Returns Results
50
+ def process(collection)
51
+ return collection.inject(Results.new) do |h, k|
52
+ key = k.values[0]
53
+ vals = (k.values[1].is_a?(String) ? k.values[1].split(',') : k.values[1])
54
+ doc = Document.new :_key_name => @map_key.to_s, :_key_value => key, @map_key => key, @count_field => vals[0].to_i
55
+ @fields.each_with_index do |f, i|
56
+ doc[f[0].to_sym] = serialize(vals[i + 1], f[1][:type])
57
+ end
58
+ h << doc
59
+ end
60
+ end
61
+
62
+ end
63
+
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,65 @@
1
+ require 'mongoid/mapreduce/serialization'
2
+
3
+ module Mongoid
4
+ module MapReduce
5
+ module Formula
6
+
7
+ class ArrayValues
8
+ include Mongoid::MapReduce::Serialization
9
+
10
+ def initialize(fields, options={})
11
+ options[:map_key] ||= :_id
12
+ options[:count_type] ||= Integer
13
+ options[:count_field] ||= :_count
14
+
15
+ if fields.length > 1
16
+ raise "Error: The Array Values formula can only take 1 field"
17
+ end
18
+
19
+ @field_name = options[:map_key]
20
+ @field_type = options[:count_type]
21
+ @count_field = options[:count_field]
22
+ end
23
+
24
+ # Generate a map function
25
+ # Emits the value 1 for each value of the given array field
26
+ #
27
+ # Returns String
28
+ def map
29
+ fn = "function() { "
30
+ fn << "this.#{@field_name.to_s}.forEach(function(value) { "
31
+ fn << "emit(value, 1); "
32
+ fn << "}); "
33
+ fn << "}"
34
+ end
35
+
36
+ # Generates a reduce function
37
+ # Adds the given values
38
+ #
39
+ # Returns String
40
+ def reduce
41
+ fn = "function(k, v) { "
42
+ fn << "var result = 0; "
43
+ fn << "v.forEach(function(val) { result += val; }); "
44
+ fn << "return result; "
45
+ fn << "}"
46
+ end
47
+
48
+ # Process the results of a given collection
49
+ #
50
+ # collection - the MongoDB collection returned from the map/reduce op
51
+ #
52
+ # Returns Results
53
+ def process(collection)
54
+ return collection.inject(Results.new) do |h, k|
55
+ key = k.values[0].to_s =~ /(^[-+]?[0-9]+$)|(\.0+)$/ ? Integer(k.values[0]) : Float(k.values[0])
56
+ val = serialize(k.values[1].is_a?(String) ? k.values[1].split(',') : k.values[1], @field_type)
57
+ h << Document.new(:_key_name => @field_name, :_key_value => key, key.to_s => val, @count_field => val)
58
+ end
59
+ end
60
+
61
+ end
62
+
63
+ end
64
+ end
65
+ end
@@ -1,3 +1,6 @@
1
+ require 'mongoid/mapreduce/formula/aggregate_fields.rb'
2
+ require 'mongoid/mapreduce/formula/array_values.rb'
3
+
1
4
  module Mongoid
2
5
  module MapReduce
3
6
 
@@ -9,100 +12,41 @@ module Mongoid
9
12
  #
10
13
  # klass - Mongoid model Class
11
14
  # selector - Selector to use for search (often from criteria)
12
- # map_key - Key to use in map function
15
+ # options - Hash of options:
16
+ # count_field - Name of field used to store count in results
17
+ # formula - Name of formula to be used (underscore)
18
+ # map_key - Name of field used as key in map function
13
19
  #
14
20
  # Returns nothing
15
- def initialize(klass, selector, map_key)
21
+ def initialize(klass, selector, options)
22
+ options[:klass] = klass
23
+ options[:selector] = selector
24
+ options[:formula] ||= :aggregate_fields
25
+
16
26
  @klass = klass
17
27
  @selector = selector
18
- @map_key = map_key
19
- @count_field = :_count
28
+ @formula_name = options[:formula]
29
+ @options = options
20
30
  @fields = {}
21
- end
22
-
23
- # Obtain the field we are using for the map, if using an array values map.
24
- #
25
- # Returns true or false
26
- def map_array_field
27
- @fields.select { |k, v| v[:formula] == :array_values }.first
28
- end
29
-
30
- # Determines whether or not we are mapping from an array value
31
- #
32
- # Returns true or false
33
- def map_from_array?
34
- @fields.select { |k, v| v[:formula] == :array_values }.any?
35
- end
36
31
 
37
- # Generates the JavaScript map function
38
- #
39
- # If we have any fields defined with a map function of :array_values, use that to map
40
- # otherwise, use our aggregate map function.
41
- #
42
- # Returns String
43
- def map
44
- fn = "function() { "
45
- if map_from_array?
46
- fn << map_array_values(map_array_field)
47
- else
48
- fn << map_aggregates
32
+ if options.key?(:fields)
33
+ options[:fields].each do |f|
34
+ field f.to_sym
35
+ end
49
36
  end
50
- fn << "}"
51
- fn
52
- end
53
-
54
- # Generate a map function from one unique map key and a number of aggregate sources
55
- #
56
- #
57
- def map_aggregates
58
- fields = @fields.select { |k, v| v[:formula] == :aggregate }
59
- "emit (this.#{@map_key}, [#{[1, fields.collect{|k,v| "this.#{k}"}].flatten.join(", ")}]); "
60
37
  end
61
38
 
62
- # Generate a map function from one unique map key and a number of aggregate sources
63
- #
39
+ # Expose the currently selected formula, from instance variable if possible.
64
40
  #
65
- def map_array_values(field)
66
- "this.#{field[0].to_s}.forEach(function(value) { emit(value, 1); }); "
67
- end
68
-
69
- # Generates the JavaScript reduce function
70
- #
71
- # Returns String
72
- def reduce
73
- fn = "function(k, v) { "
74
- if map_from_array?
75
- fn << reduce_array_values
76
- else
77
- fn << reduce_aggregates
41
+ # Returns Formula object.
42
+ def formula
43
+ # Find and initialize our formula
44
+ klass = "Mongoid::MapReduce::Formula::#{@formula_name.to_s.camelize}"
45
+ begin
46
+ @formula ||= klass.constantize.new(@fields, @options)
47
+ rescue NameError
48
+ raise "Could not load formula for #{klass}"
78
49
  end
79
- fn << "}"
80
- fn
81
- end
82
-
83
- # Generates a reduce function for aggregate map
84
- #
85
- # Returns String
86
- def reduce_aggregates
87
- fields = @fields.select { |k, v| v[:formula] == :aggregate }
88
- fn = ""
89
- fn << "var results = [#{(["0"] * (fields.length + 1)).flatten.join(", ")}]; "
90
- fn << "v.forEach(function(val) { "
91
- fn << "for(var i=0; i<= #{fields.length}; i++) { "
92
- fn << "results[i] += (typeof val[i] == Boolean) ? (val[i] ? 1 : 0) : val[i] "
93
- fn << "} "
94
- fn << "}); "
95
- fn << "return results.toString(); "
96
- end
97
-
98
- # Generates a reduce function for array values
99
- #
100
- # Returns String
101
- def reduce_array_values
102
- fn = ""
103
- fn << "var result = 0; "
104
- fn << "v.forEach(function(val) { result += val; }); "
105
- fn << "return result; "
106
50
  end
107
51
 
108
52
  # Adds a field to the map/reduce operation
@@ -112,49 +56,21 @@ module Mongoid
112
56
  # Returns nothing.
113
57
  def field(sym, options={})
114
58
  options[:type] ||= Integer
115
- options[:formula] ||= :aggregate
116
59
  @fields[sym.to_sym] = options
117
60
  end
118
61
 
119
- # Serialize an object to the specified class
120
- #
121
- # obj - Object to serialize
122
- # klass - Class to prefer
123
- #
124
- # Returns serialized object or nil
125
- def serialize(obj, klass)
126
- return nil if obj.blank?
127
- obj = obj.is_a?(Boolean) ? (obj ? 1 : 0) : obj
128
- obj = obj.to_s =~ /(^[-+]?[0-9]+$)|(\.0+)$/ ? Integer(obj) : Float(obj)
129
- Mongoid::Fields::Mappings.for(klass).allocate.serialize(obj)
130
- end
131
-
132
62
  # Runs the map/reduce operation and returns the result
133
63
  #
134
64
  # Returns Mongoid::MapReduce::Results object (array)
135
65
  # containing Mongoid::MapReduce::Document objects (hashes)
136
66
  def run
137
67
  begin
138
- res = @klass.collection.map_reduce(map, reduce, { query: @selector, out: "#map_reduce" } ).find.to_a
139
- return res.inject(Results.new) do |h, k|
140
- idx = k.values[0]
141
- d = (k.values[1].is_a?(String) ? k.values[1].split(',') : k.values[1])
142
-
143
- if d.is_a?(Array)
144
- doc = Document.new :_key_name => @map_key.to_s, :_key_value => idx, @map_key => idx, @count_field => d[0].to_i
145
- @fields.each_with_index do |h, i|
146
- doc[h[0].to_sym] = serialize(d[i + 1], h[1][:type])
147
- end
148
- else
149
- f = map_array_field[0]
150
- k = serialize(idx, map_array_field[1][:type])
151
- v = d.to_i
152
- doc = Document.new :_key_name => f, :_key_value => k, k.to_s => v, @count_field => v
153
- end
154
- h << doc
155
- end
68
+ coll = @klass.collection.map_reduce(formula.map, formula.reduce, { query: @selector, out: "#map_reduce" } ).find.to_a
69
+ rescue
70
+ raise "Error: could not execute map reduce function"
156
71
  end
157
72
 
73
+ formula.process(coll)
158
74
  end
159
75
 
160
76
  end
@@ -0,0 +1,20 @@
1
+ module Mongoid
2
+ module MapReduce
3
+ module Serialization
4
+
5
+ # Serialize an object to the specified class
6
+ #
7
+ # obj - Object to serialize
8
+ # klass - Class to prefer
9
+ #
10
+ # Returns serialized object or nil
11
+ def serialize(obj, klass)
12
+ return nil if obj.blank?
13
+ obj = obj.is_a?(Boolean) ? (obj ? 1 : 0) : obj
14
+ obj = obj.to_s =~ /(^[-+]?[0-9]+$)|(\.0+)$/ ? Integer(obj) : Float(obj)
15
+ Mongoid::Fields::Mappings.for(klass).allocate.serialize(obj)
16
+ end
17
+
18
+ end
19
+ end
20
+ end
@@ -1,7 +1,7 @@
1
1
  module Mongoid
2
2
  module MapReduce
3
3
 
4
- VERSION = '0.1.2'
4
+ VERSION = '0.1.3'
5
5
 
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mongoid-mapreduce
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-09-23 00:00:00.000000000Z
12
+ date: 2011-09-24 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: mongoid
16
- requirement: &70107200564540 !ruby/object:Gem::Requirement
16
+ requirement: &70361782922640 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '2.0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70107200564540
24
+ version_requirements: *70361782922640
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: bson_ext
27
- requirement: &70107200564040 !ruby/object:Gem::Requirement
27
+ requirement: &70361782922040 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ~>
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '1.3'
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *70107200564040
35
+ version_requirements: *70361782922040
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: growl
38
- requirement: &70107200563660 !ruby/object:Gem::Requirement
38
+ requirement: &70361782921580 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '0'
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *70107200563660
46
+ version_requirements: *70361782921580
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: rake
49
- requirement: &70107200563100 !ruby/object:Gem::Requirement
49
+ requirement: &70361782920960 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ~>
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: 0.9.2
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *70107200563100
57
+ version_requirements: *70361782920960
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: rspec
60
- requirement: &70107200562580 !ruby/object:Gem::Requirement
60
+ requirement: &70361782920360 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ~>
@@ -65,10 +65,10 @@ dependencies:
65
65
  version: '2.6'
66
66
  type: :development
67
67
  prerelease: false
68
- version_requirements: *70107200562580
68
+ version_requirements: *70361782920360
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: guard-rspec
71
- requirement: &70107200562120 !ruby/object:Gem::Requirement
71
+ requirement: &70361782919780 !ruby/object:Gem::Requirement
72
72
  none: false
73
73
  requirements:
74
74
  - - ~>
@@ -76,7 +76,7 @@ dependencies:
76
76
  version: 0.4.3
77
77
  type: :development
78
78
  prerelease: false
79
- version_requirements: *70107200562120
79
+ version_requirements: *70361782919780
80
80
  description: Mongoid MapReduce provides simple aggregation features for your Mongoid
81
81
  models
82
82
  email:
@@ -87,8 +87,11 @@ extra_rdoc_files: []
87
87
  files:
88
88
  - lib/mongoid/mapreduce/base.rb
89
89
  - lib/mongoid/mapreduce/document.rb
90
+ - lib/mongoid/mapreduce/formula/aggregate_fields.rb
91
+ - lib/mongoid/mapreduce/formula/array_values.rb
90
92
  - lib/mongoid/mapreduce/reducer.rb
91
93
  - lib/mongoid/mapreduce/results.rb
94
+ - lib/mongoid/mapreduce/serialization.rb
92
95
  - lib/mongoid/mapreduce/version.rb
93
96
  - lib/mongoid/mapreduce.rb
94
97
  - lib/mongoid-mapreduce.rb