Sunday, May 6, 2012

#Clojure , #MongoDB and removing a set of items

When using congomongo library to access MongoDB in Clojure, it is sometimes required to remove a collection of items. As the example on GitHub page shows only how to remove one item, I have pasted the code here.

Setup test data

To setup test data, we would use 100 items in :points collection.

(use 'somnium.congomongo)
 
(mass-insert! :points
  (for [x (range 0 10) y (range 0 10)] {:x x :y y}))
 
(count (fetch :points))
;=> 100

Please note, that I am not writing any code specific to establishing a connection here. For more information on using congomongo library, please visit congomongo github site.

The obvious way

The most obvious way to do so, would be to pass a list objects to destroy! function. Sadly, it won't work:

user=> (destroy! :points (fetch :points))
;=> ClassCastException clojure.lang.LazySeq cannot be 
;=> cast to com.mongodb.DBObject  
;=> somnium.congomongo/destroy! (congomongo.clj:419)

Using for loop

We can also invoke destroy! a hundred times:

(count (fetch :points))
;=> 100
 
(for [p (fetch :points)]
  (destroy! :points p))
 
(count (fetch :points))
;=> 0

It will work, but it is not optimal.

Using where clause

The best way to remove a collection of items in MongoDB using congomongo, it would be to use where clause for a destroy! function:

(count (fetch :points))
;=> 100
 
(destroy! :points {:x {:$gt 4}})
 
(count (fetch :points))
;=> 50

It is also possible to refer MongoDB items by their ids in where clause:

(count (fetch :points))
;=> 100
 
(destroy! :points {:_id {:$in (map :_id (fetch :points))}})
 
(count (fetch :points))
;=> 0