Skip to content

Commit

Permalink
Added support for mongoid 3.0.
Browse files Browse the repository at this point in the history
  • Loading branch information
dblock committed Nov 1, 2012
1 parent 15c9d34 commit c06785f
Show file tree
Hide file tree
Showing 8 changed files with 48 additions and 38 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
@@ -0,0 +1,10 @@
Next Release (1.0)
==================

* Added ability to maintain a snapshot of multiple collections atomically - [@aaw](https://github.com/aaw).
* Added support for Mongoid 3.0 - [@dblock](https://github.com/dblock).

Version 0.1.0
=============

* Initial public release - [@aaw](https://github.com/aaw).
14 changes: 4 additions & 10 deletions Gemfile
@@ -1,14 +1,8 @@
source "http://rubygems.org"
# Add dependencies required to use your gem here.
# Example:
# gem "activesupport", ">= 2.3.5"
gem "mongoid_slug", ">= 0.8.2"

# Add dependencies to develop your gem here.
# Include everything needed to run rake, tests, features, etc.
group :development do
gem "mongoid", "~> 2.0.0"
gem "bson_ext", "~> 1.3.0"
gem "rspec", "~> 2.5.0"
gem "jeweler", "~> 1.5.2"
gem "mongoid", "~> 3.0"
gem "mongoid_slug", :git => "https://github.com/dblock/mongoid-slug.git", :branch => "v0.10.0-mongoid3"
gem "rspec", "~> 2.11.0"
gem "jeweler", "~> 1.8.4"
end
22 changes: 13 additions & 9 deletions README.md
@@ -1,7 +1,7 @@
Mongoid Collection Snapshot
===========================

Easy maintenance of collections of processed data in MongoDB with the Mongoid ODM.
Easy maintenance of collections of processed data in MongoDB with the Mongoid 3.x ODM.

Quick example:
--------------
Expand All @@ -10,13 +10,13 @@ Suppose that you have a Mongoid model called `Artwork`, stored
in a MongoDB collection called `artworks` and the underlying documents
look something like:

{ name: 'Flowers', artist: 'Andy Warhol', price: 3000000 }
{ name: 'Flowers', artist: 'Andy Warhol', price: 3000000 }

From time to time, your system runs a map/reduce job to compute the
average price of each artist's works, resulting in a collection called
`artist_average_price` that contains documents that look like:

{ _id: { artist: 'Andy Warhol'}, value: { price: 1500000 } }
{ _id: { artist: 'Andy Warhol'}, value: { price: 1500000 } }

If your system wants to maintain and use this average price data, it has
to do so at the level of raw MongoDB operations, since
Expand Down Expand Up @@ -55,11 +55,15 @@ In the example above, we'd set up our average artist price collection like:
}
EOS

Artwork.collection.map_reduce(map, reduce, :out => collection_snapshot.name)
Mongoid.default_session.command(
"mapreduce" => "artworks",
map: map,
reduce: reduce,
out: collection_snapshot.name)
end

def average_price(artist)
doc = collection_snapshot.findOne({'_id.artist': artist})
doc = collection_snapshot.find({'_id.artist': artist}).first
doc['value']['sum']/doc['value']['count']
end
end
Expand Down Expand Up @@ -92,17 +96,17 @@ in your build or query methods:
# ...
# define map/reduce for average and max aggregations
# ...
Artwork.collection.map_reduce(map_avg, reduce_avg, :out => collection_snapshot('average'))
Artwork.collection.map_reduce(map_max, reduce_max, :out => collection_snapshot('max'))
Mongoid.default_session.command("mapreduce" => "artworks", map: map_avg, reduce: reduce_avg, out: collection_snapshot('average'))
Mongoid.default_session.command("mapreduce" => "artworks", map: map_max, reduce: reduce_max, out: collection_snapshot('max'))
end

def average_price(artist)
doc = collection_snapshot('average').findOne({'_id.artist': artist})
doc = collection_snapshot('average').find({'_id.artist': artist}).first
doc['value']['sum']/doc['value']['count']
end

def max_price(artist)
doc = collection_snapshot('max').findOne({'_id.artist': artist})
doc = collection_snapshot('max').find({'_id.artist': artist}).first
doc['value']['max']
end
end
6 changes: 3 additions & 3 deletions lib/mongoid_collection_snapshot.rb
Expand Up @@ -26,14 +26,14 @@ def latest

def collection_snapshot(name=nil)
if name
Mongoid.master.collection("#{self.collection.name}.#{name}.#{workspace_slug}")
Mongoid.default_session["#{self.collection.name}.#{name}.#{workspace_slug}"]
else
Mongoid.master.collection("#{self.collection.name}.#{workspace_slug}")
Mongoid.default_session["#{self.collection.name}.#{workspace_slug}"]
end
end

def drop_snapshot_collections
Mongoid.master.collections.each do |collection|
Mongoid.default_session.collections.each do |collection|
collection.drop if collection.name =~ /^#{self.collection.name}\.([^\.]+\.)?#{workspace_slug}$/
end
end
Expand Down
8 changes: 6 additions & 2 deletions spec/models/average_artist_price.rb
Expand Up @@ -20,11 +20,15 @@ def build
}
EOS

Artwork.collection.map_reduce(map, reduce, :out => collection_snapshot.name)
Mongoid.default_session.command(
"mapreduce" => "artworks",
map: map,
reduce: reduce,
out: collection_snapshot.name)
end

def average_price(artist)
doc = collection_snapshot.find_one({'_id.artist' => artist})
doc = collection_snapshot.where({'_id.artist' => artist}).first
doc['value']['sum']/doc['value']['count']
end

Expand Down
2 changes: 1 addition & 1 deletion spec/models/multi_collection_snapshot.rb
Expand Up @@ -8,7 +8,7 @@ def build
end

def get_names
['foo', 'bar', 'baz'].map{ |x| collection_snapshot(x).find_one['name'] }.join('')
['foo', 'bar', 'baz'].map{ |x| collection_snapshot(x).find.first['name'] }.join('')
end

end
12 changes: 6 additions & 6 deletions spec/mongoid/collection_snapshot_spec.rb
Expand Up @@ -54,22 +54,22 @@ module Mongoid

it "safely cleans up all collections used by the snapshot" do
# Create some collections with names close to the snapshots we'll create
Mongoid.master["#{MultiCollectionSnapshot.collection.name}.do.not_delete"].insert({'a' => 1})
Mongoid.master["#{MultiCollectionSnapshot.collection.name}.snapshorty"].insert({'a' => 1})
Mongoid.master["#{MultiCollectionSnapshot.collection.name}.hello.1"].insert({'a' => 1})
Mongoid.default_session["#{MultiCollectionSnapshot.collection.name}.do.not_delete"].insert({'a' => 1})
Mongoid.default_session["#{MultiCollectionSnapshot.collection.name}.snapshorty"].insert({'a' => 1})
Mongoid.default_session["#{MultiCollectionSnapshot.collection.name}.hello.1"].insert({'a' => 1})

MultiCollectionSnapshot.create
before_create = Mongoid.master.collections.map{ |c| c.name }
before_create = Mongoid.default_session.collections.map{ |c| c.name }
before_create.length.should > 0

sleep(1)
MultiCollectionSnapshot.create
after_create = Mongoid.master.collections.map{ |c| c.name }
after_create = Mongoid.default_session.collections.map{ |c| c.name }
collections_created = (after_create - before_create).sort
collections_created.length.should == 3

MultiCollectionSnapshot.latest.destroy
after_destroy = Mongoid.master.collections.map{ |c| c.name }
after_destroy = Mongoid.default_session.collections.map{ |c| c.name }
collections_destroyed = (after_create - after_destroy).sort
collections_created.should == collections_destroyed
end
Expand Down
12 changes: 5 additions & 7 deletions spec/spec_helper.rb
Expand Up @@ -5,20 +5,18 @@
require 'mongoid'

Mongoid.configure do |config|
name = "mongoid_collection_snapshot_test"
config.master = Mongo::Connection.new.db(name)
config.logger = Logger.new('/dev/null')
config.connect_to("mongoid_collection_snapshot_test")
end

require File.expand_path("../../lib/mongoid_collection_snapshot", __FILE__)
Dir["#{File.dirname(__FILE__)}/models/**/*.rb"].each { |f| require f }

Rspec.configure do |c|
RSpec.configure do |c|
c.before(:each) do
Mongoid.master.collections.select {|c| c.name !~ /system/ }.each(&:drop)
Mongoid.purge!
end
c.after(:all) do
Mongoid.master.command({'dropDatabase' => 1})
c.after(:all) do
Mongoid.default_session.drop
end
end

0 comments on commit c06785f

Please sign in to comment.