Merge branch 'map/reduce'

This commit is contained in:
Michael S. Klishin 2011-10-15 04:16:15 +04:00
commit aee8862bc6
3 changed files with 91 additions and 13 deletions

View file

@ -9,7 +9,9 @@
(ns monger.collection
(:refer-clojure :exclude [find remove count drop])
(:import (com.mongodb Mongo DB DBCollection WriteResult DBObject WriteConcern DBCursor) (java.util List Map) (clojure.lang IPersistentMap ISeq))
(:import [com.mongodb Mongo DB DBCollection WriteResult DBObject WriteConcern DBCursor MapReduceCommand MapReduceCommand$OutputType]
[java.util List Map]
[clojure.lang IPersistentMap ISeq])
(:require [monger core result])
(:use [monger.conversion]))
@ -281,6 +283,20 @@
(let [^DBCollection coll (.getCollection monger.core/*mongodb-database* from)]
(.rename coll to drop-target))))
;;
;; Map/Reduce
;;
(defn map-reduce
([^String collection, ^String js-mapper, ^String js-reducer, ^String output, ^Map query]
(let [^DBCollection coll (.getCollection monger.core/*mongodb-database* collection)]
(.mapReduce coll js-mapper js-reducer output (to-db-object query))))
([^String collection, ^String js-mapper, ^String js-reducer, ^String output, ^MapReduceCommand$OutputType output-type, ^Map query]
(let [^DBCollection coll (.getCollection monger.core/*mongodb-database* collection)]
(.mapReduce coll js-mapper js-reducer output output-type (to-db-object query)))))
;;
;; Implementation
;;

View file

@ -8,8 +8,8 @@
;; You must not remove this notice, or any other, from this software.
(ns monger.result
(:import (com.mongodb DBObject WriteResult)
(clojure.lang IPersistentMap))
(:import [com.mongodb DBObject WriteResult MapReduceOutput]
[clojure.lang IPersistentMap])
(:require [monger conversion]))
@ -46,4 +46,9 @@
(has-error? (.getLastError result)))
(updated-existing?
[^WriteResult result]
(updated-existing? (.getLastError result))))
(updated-existing? (.getLastError result)))
MapReduceOutput
(ok?
[^MapReduceOutput result]
(ok? ^DBObject (.getRaw result))))

View file

@ -1,12 +1,14 @@
(set! *warn-on-reflection* true)
(ns monger.test.collection
(:import [com.mongodb WriteResult WriteConcern DBCursor DBObject CommandResult$CommandFailure]
(:import [com.mongodb WriteResult WriteConcern DBCursor DBObject CommandResult$CommandFailure MapReduceOutput MapReduceCommand MapReduceCommand$OutputType]
[org.bson.types ObjectId]
[java.util Date])
(:require [monger core result util conversion]
(:require [monger core util]
[clojure stacktrace]
[monger.collection :as mgcol])
[monger.collection :as mgcol]
[monger.result :as mgres]
[monger.conversion :as mgcnv])
(:use [clojure.test]))
(monger.core/connect!)
@ -60,7 +62,7 @@
(deftest insert-a-basic-db-object-without-id-and-with-default-write-concern
(let [collection "people"
doc (monger.conversion/to-db-object { :name "Joe", :age 30 })]
doc (mgcnv/to-db-object { :name "Joe", :age 30 })]
(is (nil? (.get ^DBObject doc "_id")))
(mgcol/insert "people" doc)
(is (not (nil? (monger.util/get-id doc))))))
@ -160,8 +162,8 @@
(mgcol/insert collection doc)
(def ^DBObject found-one (mgcol/find-one collection { :language "Clojure" }))
(is (= (:_id doc) (monger.util/get-id found-one)))
(is (= (monger.conversion/from-db-object found-one true) doc))
(is (= (monger.conversion/to-db-object doc) found-one))))
(is (= (mgcnv/from-db-object found-one true) doc))
(is (= (mgcnv/to-db-object doc) found-one))))
(deftest find-one-full-document-as-map-when-collection-has-matches
@ -289,7 +291,7 @@
{ :language "Clojure", :name "incanter" }
{ :language "Scala", :name "akka" }])
(doseq [doc (take 3 (map (fn [dbo]
(monger.conversion/from-db-object dbo true))
(mgcnv/from-db-object dbo true))
(mgcol/find-seq collection { :language "Clojure" })))]
(is (= "Clojure" (:language doc))))))
@ -326,7 +328,7 @@
(is (= 1 (.count scala-libs)))
(is (= 3 (.count clojure-libs)))
(doseq [i clojure-libs]
(let [doc (monger.conversion/from-db-object i true)]
(let [doc (mgcnv/from-db-object i true)]
(is (= (:language doc) "Clojure"))))
(is (empty? (mgcol/find collection { :language "Erlang" } [:name]))))))
@ -408,7 +410,7 @@
(deftest save-a-new-basic-db-object
(let [collection "people"
doc (monger.conversion/to-db-object { :name "Joe", :age 30 })]
doc (mgcnv/to-db-object { :name "Joe", :age 30 })]
(is (nil? (monger.util/get-id doc)))
(mgcol/save "people" doc)
(is (not (nil? (monger.util/get-id doc))))))
@ -495,3 +497,58 @@
(is (not (mgcol/exists? collection)))
(is (mgcol/exists? "gadgets"))
(mgcol/drop "gadgets")))
;;
;; Map/Reduce
;;
(let [collection "widgets"
mapper "function() {
emit(this.state, this.price * this.quantity)
}"
reducer "function(key, values) {
var result = 0;
values.forEach(function(v) { result += v });
return result;
}"
batch [{ :state "CA" :quantity 1 :price 199.00 }
{ :state "NY" :quantity 2 :price 199.00 }
{ :state "NY" :quantity 1 :price 299.00 }
{ :state "IL" :quantity 2 :price 11.50 }
{ :state "CA" :quantity 2 :price 2.95 }
{ :state "IL" :quantity 3 :price 5.50 }]
expected [{:_id "CA", :value 204.9} {:_id "IL", :value 39.5} {:_id "NY", :value 697.0}]]
(deftest basic-inline-map-reduce-example
(mgcol/remove collection)
(is (mgres/ok? (mgcol/insert-batch collection batch)))
(let [output (mgcol/map-reduce collection mapper reducer nil MapReduceCommand$OutputType/INLINE {})
results (mgcnv/from-db-object ^DBObject (.results ^MapReduceOutput output) true)]
(mgres/ok? output)
(is (= expected results))))
(deftest basic-map-reduce-example-that-replaces-named-collection
(mgcol/remove collection)
(is (mgres/ok? (mgcol/insert-batch collection batch)))
(let [output (mgcol/map-reduce collection mapper reducer "mr_outputs" {})
results (mgcnv/from-db-object ^DBObject (.results ^MapReduceOutput output) true)]
(mgres/ok? output)
(is (= 3 (monger.core/count results)))
(is (= expected
(map #(mgcnv/from-db-object % true) (seq results))))
(is (= expected
(map #(mgcnv/from-db-object % true) (mgcol/find "mr_outputs"))))
(.drop ^MapReduceOutput output)))
(deftest basic-map-reduce-example-that-merged-results-into-named-collection
(mgcol/remove collection)
(is (mgres/ok? (mgcol/insert-batch collection batch)))
(mgcol/map-reduce collection mapper reducer "merged_mr_outputs" MapReduceCommand$OutputType/MERGE {})
(is (mgres/ok? (mgcol/insert collection { :state "OR" :price 17.95 :quantity 4 })))
(let [output (mgcol/map-reduce collection mapper reducer "merged_mr_outputs" MapReduceCommand$OutputType/MERGE {})]
(mgres/ok? output)
(is (= 4 (monger.core/count (.results ^MapReduceOutput output))))
(is (= ["CA" "IL" "NY" "OR"]
(map :_id (mgcol/find-maps "merged_mr_outputs"))))
(.drop ^MapReduceOutput output))))