From 080ef6b896fd9e13ce5a4b955028dad85c88f020 Mon Sep 17 00:00:00 2001 From: "Michael S. Klishin" Date: Sat, 30 Jun 2012 23:49:23 +0400 Subject: [PATCH] Introduce monger.collection/insert-and-return Per discussion in Raynes/refheap#89 --- ChangeLog.md | 22 ++++++++++++++- src/clojure/monger/collection.clj | 42 ++++++++++++++++++++++++----- test/monger/test/inserting_test.clj | 30 +++++++++++++++++++++ 3 files changed, 87 insertions(+), 7 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 42b7f4e..a500918 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,6 +1,26 @@ ## Changes between 1.0.0-alpha3 and 1.1.0-alpha4 -No changes yet. +### monger.collection/insert-and-return + +`monger.collection/insert-and-return` is a new function that solves the biggest complain about Monger's `monger.collection/insert` behavior +from Monger 1.0 users. Because `monger.collection/insert` returns a write result and is supposed to be used with Validateur and +`monger.result/ok?` and similar functions, it is hard to retrieve object id in case it wasn't explicitly passed in. + +This resulted in code that looks more or less like this: + +``` clojure +(let [oid (ObjectId.) + result (merge doc {:_id oid)] + (monger.collection/insert "documents" result) + result) +``` + +To solve this problem, we introduce a new function, `monger.collection/insert-and-return`, that returns the exact inserted document +as an immutable Clojure map. The `:_id` key will be available on the returned map, even if wasn't present and had to be generated. + +`monger.collection/insert` behavior stays the same both because of backwards compatibility concerns and because there are valid cases +when a user may want to have the write result returned. + ## Changes between 1.0.0-alpha2 and 1.1.0-alpha3 diff --git a/src/clojure/monger/collection.clj b/src/clojure/monger/collection.clj index ccaafc8..f3eb961 100644 --- a/src/clojure/monger/collection.clj +++ b/src/clojure/monger/collection.clj @@ -50,10 +50,14 @@ ;; (defn ^WriteResult insert - "Saves @document@ to @collection@. You can optionally specify WriteConcern. + "Saves @document@ to @collection@ and returns write result monger.result/ok? and similar functions operate on. You can optionally specify WriteConcern. + + In case you need the exact inserted document returned, with the :_id key generated, use monger.collection/insert-and-return + instead. EXAMPLES: + ;; returns write result (monger.collection/insert \"people\" {:name \"Joe\", :age 30}) (monger.collection/insert \"people\" {:name \"Joe\", :age 30, WriteConcern/SAFE}) @@ -64,14 +68,40 @@ ^WriteConcern monger.core/*mongodb-write-concern*)) ([^String collection document ^WriteConcern concern] (.insert (.getCollection monger.core/*mongodb-database* collection) - (to-db-object document) - concern)) + (to-db-object document) + concern)) ([^DB db ^String collection document ^WriteConcern concern] (.insert (.getCollection db collection) (to-db-object document) concern))) +(defn ^clojure.lang.IPersistentMap insert-and-return + "Like monger.collection/insert but returns the inserted document as a persistent Clojure map. + + If the :_id key wasn't set on the document, it will be generated and merged into the returned map. + + EXAMPLES: + + ;; returns the entire document with :_id generated + (monger.collection/insert-and-return \"people\" {:name \"Joe\", :age 30}) + + (monger.collection/insert-and-return \"people\" {:name \"Joe\", :age 30, WriteConcern/SAFE}) + " + ([^String collection document] + (insert-and-return ^DB monger.core/*mongodb-database* collection document ^WriteConcern monger.core/*mongodb-write-concern*)) + ([^String collection document ^WriteConcern concern] + (insert-and-return ^DB monger.core/*mongodb-database* collection document concern)) + ([^DB db ^String collection document ^WriteConcern concern] + ;; MongoDB Java driver will generate the _id and set it but it tries to mutate the inserted DBObject + ;; and it does not work very well in our case, because that DBObject is short lived and produced + ;; from the Clojure map we are passing in. Plus, this approach is very awkward with immutable data + ;; structures being the default. MK. + (let [doc (merge document {:_id (ObjectId.)})] + (insert db collection doc concern) + doc))) + + (defn ^WriteResult insert-batch "Saves @documents@ do @collection@. You can optionally specify WriteConcern as a third argument. @@ -92,8 +122,8 @@ concern)) ([^DB db ^String collection ^List documents ^WriteConcern concern] (.insert (.getCollection db collection) - ^List (to-db-object documents) - concern))) + ^List (to-db-object documents) + concern))) ;; ;; monger.collection/find @@ -220,7 +250,7 @@ " ([^String collection ^Map conditions ^Map document & {:keys [fields sort remove return-new upsert keywordize] :or - {fields nil sort nil remove false return-new false upsert false keywordize true}}] + {fields nil sort nil remove false return-new false upsert false keywordize true}}] (let [coll (.getCollection monger.core/*mongodb-database* collection) maybe-fields (when fields (as-field-selector fields)) maybe-sort (when sort (to-db-object sort))] diff --git a/test/monger/test/inserting_test.clj b/test/monger/test/inserting_test.clj index 1c600a5..3dab83a 100644 --- a/test/monger/test/inserting_test.clj +++ b/test/monger/test/inserting_test.clj @@ -101,6 +101,36 @@ (is (= dbref fo))))) +;; +;; insert-and-return +;; + +(deftest insert-and-return-a-basic-document-without-id-and-with-default-write-concern + (let [collection "people" + doc {:name "Joe" :age 30} + result (mc/insert-and-return "people" doc)] + (is (= (:name doc) + (:name result))) + (is (= (:age doc) + (:age result))) + (is (:_id result)) + (is (= 1 (mc/count collection))))) + +(deftest insert-and-return-a-basic-document-without-id-but-with-a-write-concern + (let [collection "people" + doc {:name "Joe" :age 30 :ratio 3/4} + result (mc/insert-and-return "people" doc WriteConcern/FSYNC_SAFE)] + (is (= (:name doc) + (:name result))) + (is (= (:age doc) + (:age result))) + (is (= (:ratio doc) + (:ratio result))) + (is (:_id result)) + (is (= 1 (mc/count collection))))) + + + ;; ;; insert-batch ;;