MongoDB 2.1 Aggregation Framework support

This commit is contained in:
Michael S. Klishin 2012-05-28 00:43:45 +04:00
parent 7d595874aa
commit 63f7099350
4 changed files with 155 additions and 7 deletions

View file

@ -1,5 +1,66 @@
## Changes between 1.0.0-beta7 and 1.0.0-beta8
### MongoDB 2.1/2.2 Aggregation Framework support
`monger.collection/aggregate` provides a convenient way to run [aggregation queries](http://docs.mongodb.org/manual/reference/aggregation/).
``` clojure
;; single stage pipeline
(mc/aggregate "docs" [{$project {:subtotal {$multiply ["$quantity", "$price"]}
:_id "$state"}}])
;; two stage pipeline
(mc/aggregate "docs" [{$project {:subtotal {$multiply ["$quantity", "$price"]}
:_id 1
:state 1}}
{$group {:_id "$state"
:total {$sum "$subtotal"}}}])
```
The following couple of tests demonstrates aggregation queries with some sample data:
``` clojure
(deftest ^{:edge-features true} test-basic-projection-with-multiplication
(let [collection "docs"
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 "NY" :subtotal 398.0}
{:_id "NY" :subtotal 299.0}
{:_id "IL" :subtotal 23.0}
{:_id "CA" :subtotal 5.9}
{:_id "IL" :subtotal 16.5}
{:_id "CA" :subtotal 199.0}]]
(mc/insert-batch collection batch)
(let [result (vec (mc/aggregate "docs" [{$project {:subtotal {$multiply ["$quantity", "$price"]}
:_id "$state"}}]))]
(is (= expected result)))))
(deftest ^{:edge-features true} test-basic-total-aggregation
(let [collection "docs"
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", :total 204.9} {:_id "IL", :total 39.5} {:_id "NY", :total 697.0}]]
(mc/insert-batch collection batch)
(let [result (vec (mc/aggregate "docs" [{$project {:subtotal {$multiply ["$quantity", "$price"]}
:_id 1
:state 1}}
{$group {:_id "$state"
:total {$sum "$subtotal"}}}]))]
(is (= expected result)))))
```
The aggregation framework is an edge feature that will be available in MongoDB 2.2.
### More Operators
Two new operator macros: `$regex`, `$options` and those used by the upcoming

View file

@ -6,13 +6,17 @@
[org.mongodb/mongo-java-driver "2.7.3"]
[com.novemberain/validateur "1.1.0"]
[clojurewerkz/support "0.4.0"]]
:test-selectors {:default (complement :performance)
:focus :focus
:indexing :indexing
:external :external
:cache :cache
:performance :performance
:all (constantly true)}
:test-selectors {:default (fn [m]
(and (not (:performance m))
(not (:edge-features m))))
:focus :focus
:indexing :indexing
:external :external
:cache :cache
:performance :performance
;; as in, edge mongodb server
:edge-features :edge-features
:all (constantly true)}
:codox {:exclude [monger.internal.pagination]}
:mailing-list {:name "clojure-monger"
:archive "https://groups.google.com/group/clojure-monger"

View file

@ -596,3 +596,17 @@
and :size (max allowed size of the collection, in bytes)."
[^String name options]
(.createCollection ^DB monger.core/*mongodb-database* name (to-db-object options)))
;;
;; Aggregation
;;
(defn aggregate
"Performs aggregation query. MongoDB 2.1/2.2+ only.
See http://docs.mongodb.org/manual/applications/aggregation/ to learn more."
[^String coll stages]
(let [res (monger.core/command {:aggregate coll :pipeline stages})]
;; this is what DBCollection#distinct does. Turning a blind eye!
(.throwOnError res)
(map #(from-db-object % true) (.get res "result"))))

View file

@ -0,0 +1,69 @@
(ns monger.test.aggregation-framework-test
(:require monger.core [monger.collection :as mc]
[monger.test.helper :as helper])
(:use clojure.test
monger.operators
monger.test.fixtures))
(helper/connect!)
(use-fixtures :each purge-docs)
(deftest ^{:edge-features true} test-basic-single-stage-$project-aggregation
(let [collection "docs"
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 [{:quantity 1 :state "CA"}
{:quantity 2 :state "NY"}
{:quantity 1 :state "NY"}
{:quantity 2 :state "IL"}
{:quantity 2 :state "CA"}
{:quantity 3 :state "IL"}]]
(mc/insert-batch collection batch)
(is (= 6 (mc/count collection)))
(let [result (vec (map #(select-keys % [:state :quantity])
(mc/aggregate "docs" [{$project {:state 1 :quantity 1}}])))]
(is (= expected result)))))
(deftest ^{:edge-features true} test-basic-projection-with-multiplication
(let [collection "docs"
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 "NY" :subtotal 398.0}
{:_id "NY" :subtotal 299.0}
{:_id "IL" :subtotal 23.0}
{:_id "CA" :subtotal 5.9}
{:_id "IL" :subtotal 16.5}
{:_id "CA" :subtotal 199.0}]]
(mc/insert-batch collection batch)
(let [result (vec (mc/aggregate "docs" [{$project {:subtotal {$multiply ["$quantity", "$price"]}
:_id "$state"}}]))]
(is (= expected result)))))
(deftest ^{:edge-features true} test-basic-total-aggregation
(let [collection "docs"
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", :total 204.9} {:_id "IL", :total 39.5} {:_id "NY", :total 697.0}]]
(mc/insert-batch collection batch)
(let [result (vec (mc/aggregate "docs" [{$project {:subtotal {$multiply ["$quantity", "$price"]}
:_id 1
:state 1}}
{$group {:_id "$state"
:total {$sum "$subtotal"}}}]))]
(is (= expected result)))))