Mapping Mongodb ObjectId to and from string automatically

Viewed 1980

I am accessing Mongo database from Clojure using Monger library. One thing that annoys me is switching back and forth between ObjectId instances and strings.

For example, the this code (mc/find-maps "posts" {}) will evaluate to maps with the value of _id entry set to instances of ObjectId class, while in my app I find it more useful to simply have it as a string for which I know that it is unique.

On the other hand for expressions like: (mc/find-map-by-id "posts" (new ObjectId id)) where I do use a String object for the id parameter, I have to use it to construct an instance of ObjectId.

Is there a way to make the values of _id convert between Strings in the application and ObjectId on the mongo side automatically and transparently? Some kind of option that, when enabled, creates maps with string representations of ids, and vice versa converts string representations of ids t object ids when used as parameters in queries?

If not, what other strategies are available?

2 Answers

I am a bit late to the party, but have to mention for the future generations.

I wasn't looking to solve the automatic str->ObjectID part due to the complexity of getting the code to understand which string must be treated as ObjectID and which must not. But for the ObjectID->str half there is a solution.

I ended up extending the monger.conversion/ConvertFromDBObject protocol like this:

(extend-protocol ConvertFromDBObject
    ObjectId
    (from-db-object [input keywordize]
      (str input)))

from-db-object gets called on all read as map operations, so this solves the reading part. As for getting ObjectIDs converted to string for the results of inserts and updates I haven't found any elegant solution and extended that same protocol for maps and invoked from-db-object on the results of insert and update operations in my wrapper functions. Extending the protocol for maps looks like this (fully stolen from the original implementation for DBObject:

(extend-protocol ConvertFromDBObject
    ; copied over from the implementation for DBObject
    clojure.lang.IPersistentMap
    (from-db-object [input keywordize]
      (reduce (if keywordize
                (fn [m ^String k]
                  (assoc m (keyword k) (from-db-object (.get input k) true)))
                (fn [m ^String k]
                  (assoc m k (from-db-object (.get input k) false))))
              {} (.keySet input))))
Related