From 3a431bf1e0144bbe0a150dc3130882a6d7c643f3 Mon Sep 17 00:00:00 2001 From: "Michael S. Klishin" Date: Sat, 13 Aug 2011 21:18:21 +0400 Subject: [PATCH] Implement most of monger.convertion.ConvertFromDBObject --- src/monger/convertion.clj | 42 +++++++++++++++++-- test/monger/test/convertion.clj | 72 +++++++++++++++++++++++++++++++-- 2 files changed, 108 insertions(+), 6 deletions(-) diff --git a/src/monger/convertion.clj b/src/monger/convertion.clj index ed662e4..85ce74c 100644 --- a/src/monger/convertion.clj +++ b/src/monger/convertion.clj @@ -1,5 +1,5 @@ (ns monger.convertion - (:import (com.mongodb DBObject BasicDBObject) + (:import (com.mongodb DBObject BasicDBObject BasicDBList) (clojure.lang IPersistentMap Keyword) (java.util List Map))) @@ -27,10 +27,46 @@ List (to-db-object [#^List input] (map to-db-object input))) - +(declare associate-pairs) (defprotocol ConvertFromDBObject - (from-db-object [input] "Converts given DBObject instance to a piece of Clojure data")) + (from-db-object [input keywordize] "Converts given DBObject instance to a piece of Clojure data")) + +(extend-protocol ConvertFromDBObject + nil + (from-db-object [input keywordize] input) + + Object + (from-db-object [input keywordize] input) + + Map + (from-db-object [#^Map input keywordize] + (associate-pairs (.entrySet input) keywordize)) + + List + (from-db-object [#^List input keywordize] + (vec (map #(from-db-object % keywordize) input))) + + DBObject + (from-db-object [#^DBObject input keywordize] + ;; DBObject provides .toMap, but the implementation in + ;; subclass GridFSFile unhelpfully throws + ;; UnsupportedOperationException + (associate-pairs (for [key-set (.keySet input)] [key-set (.get input key-set)]) + keywordize))) + + +(defn- associate-pairs [pairs keywordize] + ;; Taking the keywordize test out of the fn reduces derefs + ;; dramatically, which was the main barrier to matching pure-Java + ;; performance for this marshalling + (reduce (if keywordize + (fn [m [#^String k v]] + (assoc m (keyword k) (from-db-object v true))) + (fn [m [#^String k v]] + (assoc m k (from-db-object v false)))) + {} (reverse pairs))) + diff --git a/test/monger/test/convertion.clj b/test/monger/test/convertion.clj index 628fa38..035f506 100644 --- a/test/monger/test/convertion.clj +++ b/test/monger/test/convertion.clj @@ -1,7 +1,13 @@ (ns monger.test.convertion (:require [monger core collection convertion]) + (:import (com.mongodb DBObject BasicDBObject BasicDBList) (java.util List ArrayList)) (:use [clojure.test])) + +;; +;; DBObject to Clojure +;; + (deftest convert-nil-to-dbobject (let [input nil output (monger.convertion/to-db-object input)] @@ -32,6 +38,66 @@ (deftest convert-nested-map-to-dbobject - (let [input { :int 1, :string "Mongo", :float 22.23, :map { :int 10, :string "Clojure", :float 11.9, :list '(1 "a" :b) } } - output (monger.convertion/to-db-object input)] - (is (= 10 (.get (.get output "map") "int"))))) + (let [input { :int 1, :string "Mongo", :float 22.23, :map { :int 10, :string "Clojure", :float 11.9, :list '(1 "a" :b), :map { :key "value" } } } + output (monger.convertion/to-db-object input) + inner (.get output "map")] + (is (= 10 (.get inner "int"))) + (is (= "Clojure" (.get inner "string"))) + (is (= 11.9 (.get inner "float"))) + (is (= '(1 "a" "b") (.get inner "list"))) + (is (= { "key" "value" } (.get inner "map"))))) + + +;; +;; Clojure to DBObject +;; + +(deftest convert-nil-from-db-object + (is (nil? (monger.convertion/from-db-object nil false))) + (is (nil? (monger.convertion/from-db-object nil true)))) + +(deftest convert-integer-from-dbobject + (is (= 2 (monger.convertion/from-db-object 2 false))) + (is (= 2 (monger.convertion/from-db-object 2 true)))) + +(deftest convert-float-from-dbobject + (is (= 3.3 (monger.convertion/from-db-object 3.3 false))) + (is (= 3.3 (monger.convertion/from-db-object 3.3 true)))) + +(deftest convert-flat-db-object-to-map-without-keywordizing + (let [name "Michael" + age 26 + input (doto (BasicDBObject.) + (.put "name" name) + (.put "age" age)) + output (monger.convertion/from-db-object input false)] + (is (= (output { "name" name, "age" age }))) + (is (= (output "name") name)) + (is (nil? (output :name))) + (is (= (output "age") age)) + (is (nil? (output "points"))) + )) + +(deftest convert-flat-db-object-to-map-without-keywordizing + (let [name "Michael" + age 26 + input (doto (BasicDBObject.) + (.put "name" name) + (.put "age" age)) + output (monger.convertion/from-db-object input true)] + (is (= (output { :name name, :age age }))) + (is (= (output :name) name)) + (is (nil? (output "name"))) + (is (= (output :age) age)) + (is (nil? (output "points"))))) + + +(deftest convert-flat-db-object-to-nested-map + (let [did "b38b357f5014a3250d813a16376ca2ff4837e8e1" + nested (doto (BasicDBObject.) (.put "int" 101) (.put "dblist" (doto (BasicDBList.) (.put "0" 0) (.put "1" 1))) (.put "list" (ArrayList. ["red" "green" "blue"])) (.put "nil" nil)) + input (doto (BasicDBObject.) + (.put "_id" did) + (.put "nested" nested))] + (is (= (monger.convertion/from-db-object input false) { "_id" did, "nested" { "int" 101, "dblist" {"0" 0, "1" 1}, "list" ["red" "green" "blue"], "nil" nil } })) + (is (= (monger.convertion/from-db-object input true) { :_id did, :nested { :int 101, :dblist {:0 0, :1 1}, :list ["red" "green" "blue"], :nil nil } })) + ))