Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Mongoid MapReduce provides simple aggregation functions to your models using MongoDB map/reduce

branch: master

Fetching latest commit…

Octocat-spinner-32-eaf2f5

Cannot retrieve the latest commit at this time

Octocat-spinner-32 lib
Octocat-spinner-32 spec
Octocat-spinner-32 .gitignore
Octocat-spinner-32 .rspec
Octocat-spinner-32 .travis.yml
Octocat-spinner-32 Gemfile
Octocat-spinner-32 Guardfile
Octocat-spinner-32 MIT-LICENSE
Octocat-spinner-32 README.md
Octocat-spinner-32 Rakefile
Octocat-spinner-32 mongoid-mapreduce.gemspec
README.md

Mongoid MapReduce

Mongoid MapReduce provides simple aggregation functions to your models using MongoDB map/reduce.

travis

How simple is simple?

Short answer: very!

There are two map/reduce formulae:

Aggregates: Provide a map key and a list of fields to be aggregated via addition.

Array List: Provide an array field, the values will be individually aggregated via addition.

Getting Started

First, add mongoid-mapreduce to your Gemfile:

gem 'mongoid-mapreduce'

Next, include the module in any models for collections you'll be wanting to map/reduce on:

class Employee
  include Mongoid::Document
  include Mongoid::MapReduce

  field :name
  field :division
  field :awards, :type => Integer
  field :age, :type => Integer
  field :male, :type => Integer
  field :rooms, :type => Array
end

You can now use the map_reduce method on your model to aggregate data using your choice of aggregation formula.

# Create a few example employees
Employee.create :name => 'Alan', :division => 'Software', :age => 20, :awards => 5, :male => 1, :rooms => [1,2,3]
Employee.create :name => 'Bob', :division => 'Software', :age => 25, :awards => 4, :male => 1, :rooms => [1,2,3]
Employee.create :name => 'Chris', :division => 'Hardware', :age => 30, :awards => 3, :male => 1, :rooms => [4,5,6]
Employee.create :name => 'Darcy', :division => 'Sales', :age => 35, :awards => 3, :male => 0, :rooms => [1,2,3,4,5,6]

# Aggregate formula (the default): produces 3 records, one for each division.
divs = Employee.map_reduce(:division, :fields => [:age, :awards])
divs.length               # => 3
divs.find('Software').age # => 45
divs['Hardware'].awards   # => 3
divs.first.awards         # => 9
divs.last.age             # => 35
divs.keys                 # => ['Hardware', 'Software', 'Sales']
divs.has_key?('Sales')    # => true
divs.to_hash              # => { "Software" => ..., "Hardware" => ..., "Sales" => ... }

# Array Value formula: produces 6 records, one for each room. Does not take any fields.
rooms = Employee.map_reduce(:rooms, :formula => :array_values)
rooms.length          # => 6
rooms.find(1)._count  # => 3
rooms.counts["5"]     # => 2
rooms.counts          # => { "1" => 3, "2" => 3, "3" => 3, "4" => 2, "5" => 2, "6" => 2 }

You can also add Mongoid criteria before the operation:

# Produces 2 records, one for each matching division (men only!)
divs = Employee.where(:male => 1).map_reduce(:division, :fields => [:age, :awards])
divs.length               # => 2
divs.has_key?('Sales')    # => false

You choose to supply fields as arguments or in a block:

# These are the same:
Employee.where(:age.gt => 20).map_reduce(:division, :fields => [:age, :awards])
Employee.where(:age.gt => 20).map_reduce(:division) do
  field :age
  field :awards
end

Fields can be of any type supported by Mongoid serialization, and field type is specified in block configuration:

divs = Employee.map_reduce(:division) do
  field :age, :type => Integer
  field :awards, :type => Float
end

divs.find('Software').age     # => 60
divs.find('Software').awards  # => 9.0

Additional meta fields are included in the results:

NOTE: _key_name and _key_value are discarded when converting to Hash.

# Produces 2 records, one for each matching division (men only!)
divs = Employee.map_reduce(:division, :fields => [:age, :awards])
divs.find('Software')._key_name   # => :division
divs.find('Software')._key_value  # => "Software"
divs.find('Software').division    # => "Software" (_key_name => _key_value)
divs.find('Software')._count      # => 2

# You can choose another name for the count field
Employee.map_reduce(:division, :count_field => :num).find('Software').num  #=> 2

Enhancements and Pull Requests

If you find the project useful but it doesn't meet all of your needs, feel free to fork it and send a pull request.

License

MIT license, go wild.

Something went wrong with that request. Please try again.