mongoid-mapreduce 0.1.2 → 0.1.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/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