Merge branch 'master' of github.com:michaelklishin/monger

This commit is contained in:
Michael Klishin 2013-04-15 21:24:13 +04:00
commit bdf0082372
11 changed files with 149 additions and 29 deletions

3
.gitignore vendored
View file

@ -2,8 +2,7 @@ pom.xml
*jar *jar
/lib/ /lib/
/classes/ /classes/
.lein-failures .lein-*
.lein-deps-sum
TAGS TAGS
checkouts/* checkouts/*
doc/* doc/*

12
CONTRIBUTING.md Normal file
View file

@ -0,0 +1,12 @@
## Pre-requisites
The project uses [Leiningen 2](https://leiningen.org) and requires MongoDB `2.4+` to be running
locally. Make
sure you have those two installed and then run tests against all supported Clojure versions using
lein2 all test
## Pull Requests
Then create a branch and make your changes on it. Once you are done with your changes and all
tests pass, write a [good, detailed commit message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) submit a pull request on GitHub.

View file

@ -1,3 +1,21 @@
## Changes between 1.5.0 and 1.6.0
### One More Cache Implementation
`monger.cache/db-aware-monger-cache-factory` will return a MongoDB-backed `clojure.core.cache`
implementation that can use any database:
``` clojure
(require '[monger.core :as mg])
(require '[monger.cache :as cache])
(let [db (mg/get-db "altcache")
coll "cache_entries"
c (cache/db-aware-monger-cache-factory db coll)]
(comment "This cache instance will use the altcache DB"))
```
## Changes between 1.4.0 and 1.5.0 ## Changes between 1.4.0 and 1.5.0
### Full Text Search Support ### Full Text Search Support

View file

@ -52,7 +52,7 @@ definition to your `pom.xml`:
With Leiningen: With Leiningen:
[com.novemberain/monger "1.5.0-rc1"] [com.novemberain/monger "1.5.0"]
With Maven: With Maven:
@ -60,7 +60,7 @@ With Maven:
<dependency> <dependency>
<groupId>com.novemberain</groupId> <groupId>com.novemberain</groupId>
<artifactId>monger</artifactId> <artifactId>monger</artifactId>
<version>1.5.0-rc1</version> <version>1.5.0</version>
</dependency> </dependency>
@ -132,6 +132,7 @@ on Github.
## License ## License
Copyright (C) 2011-2012 Michael S. Klishin Copyright (C) 2011-2013 [Michael S. Klishin](http://twitter.com/michaelklishin)
Distributed under the [Eclipse Public License](http://www.eclipse.org/legal/epl-v10.html), the same as Clojure. Double licensed under the [Eclipse Public License](http://www.eclipse.org/legal/epl-v10.html) (the same as Clojure) or
the [Apache Public License 2.0](http://www.apache.org/licenses/LICENSE-2.0.html).

View file

@ -1,4 +1,4 @@
(defproject com.novemberain/monger "1.5.0-rc2-SNAPSHOT" (defproject com.novemberain/monger "1.6.0-beta2-SNAPSHOT"
:description "Monger is a Clojure MongoDB client for a more civilized age: friendly, flexible and with batteries included" :description "Monger is a Clojure MongoDB client for a more civilized age: friendly, flexible and with batteries included"
:url "http://clojuremongodb.info" :url "http://clojuremongodb.info"
:min-lein-version "2.0.0" :min-lein-version "2.0.0"
@ -39,11 +39,11 @@
:1.6 {:dependencies [[org.clojure/clojure "1.6.0-master-SNAPSHOT"]]} :1.6 {:dependencies [[org.clojure/clojure "1.6.0-master-SNAPSHOT"]]}
:master {:dependencies [[org.clojure/clojure "1.6.0-master-SNAPSHOT"]]} :master {:dependencies [[org.clojure/clojure "1.6.0-master-SNAPSHOT"]]}
:dev {:resource-paths ["test/resources"] :dev {:resource-paths ["test/resources"]
:dependencies [[clj-time "0.4.4" :exclusions [org.clojure/clojure]] :dependencies [[clj-time "0.5.0" :exclusions [org.clojure/clojure]]
[cheshire "5.0.2" :exclusions [org.clojure/clojure]] [cheshire "5.0.2" :exclusions [org.clojure/clojure]]
[org.clojure/tools.cli "0.2.1" :exclusions [org.clojure/clojure]] [org.clojure/tools.cli "0.2.1" :exclusions [org.clojure/clojure]]
[org.clojure/core.cache "0.6.1" :exclusions [org.clojure/clojure]] [org.clojure/core.cache "0.6.2" :exclusions [org.clojure/clojure]]
[ring/ring-core "1.1.2"]] [ring/ring-core "1.1.8"]]
:plugins [[codox "0.6.4"]] :plugins [[codox "0.6.4"]]
:codox {:sources ["src/clojure"] :codox {:sources ["src/clojure"]
:output-dir "doc/api" :output-dir "doc/api"
@ -55,7 +55,7 @@
monger.ring.session-store]}} monger.ring.session-store]}}
;; only clj-time/JodaTime available, used to test monger.joda-time w/o clojure.data.json ;; only clj-time/JodaTime available, used to test monger.joda-time w/o clojure.data.json
:dev2 {:resource-paths ["test/resources"] :dev2 {:resource-paths ["test/resources"]
:dependencies [[clj-time "0.4.2" :exclusions [org.clojure/clojure]]]}} :dependencies [[clj-time "0.5.0" :exclusions [org.clojure/clojure]]]}}
:aliases {"all" ["with-profile" "dev:dev,1.3:dev,1.4:dev,dj01x:dev,dj02x:dev,1.6"]} :aliases {"all" ["with-profile" "dev:dev,1.3:dev,1.4:dev,dj01x:dev,dj02x:dev,1.6"]}
:repositories {"sonatype" {:url "http://oss.sonatype.org/content/repositories/releases" :repositories {"sonatype" {:url "http://oss.sonatype.org/content/repositories/releases"
:snapshots false :snapshots false

View file

@ -4,9 +4,11 @@
:author "Michael S. Klishin"} :author "Michael S. Klishin"}
monger.cache monger.cache
(:require [monger.collection :as mc] (:require [monger.collection :as mc]
[clojure.core.cache :as cache]) [clojure.core.cache :as cache]
(:use monger.conversion) [monger.conversion :as cnv])
(:import clojure.core.cache.CacheProtocol)) (:import clojure.core.cache.CacheProtocol
[com.mongodb DB DBObject WriteConcern]
java.util.Map))
;; ;;
;; Implementation ;; Implementation
@ -15,6 +17,24 @@
(def ^{:const true} (def ^{:const true}
default-cache-collection "cache_entries") default-cache-collection "cache_entries")
(defn- ^DBObject find-one
[^DB db ^String collection ^Map ref]
(.findOne (.getCollection db (name collection))
(cnv/to-db-object ref)))
(defn- find-by-id
"A version of monger.collection/find-by-id that does not require the
fields argument"
[^DB db ^String collection id]
(find-one db collection {:_id id}))
(defn- find-map-by-id
"A version of monger.collection/find-by-map-id that accepts database
as an argument"
[^DB db ^String collection id]
(cnv/from-db-object ^DBObject (find-one db collection {:_id id}) true))
;; ;;
;; API ;; API
;; ;;
@ -24,11 +44,8 @@
(extend-protocol cache/CacheProtocol (extend-protocol cache/CacheProtocol
BasicMongerCache BasicMongerCache
(lookup [c k] (lookup [c k]
(:value (mc/find-map-by-id (:collection c) k))) (let [m (mc/find-map-by-id (:collection c) k)]
#_ (lookup [c k not-found] (:value m)))
(if-let [doc (mc/find-map-by-id (:collection c) k)]
(:value doc)
not-found))
(has? [c k] (has? [c k]
(not (nil? (mc/find-by-id (get c :collection) k)))) (not (nil? (mc/find-by-id (get c :collection) k))))
(hit [this k] (hit [this k]
@ -52,3 +69,35 @@
(BasicMongerCache. collection)) (BasicMongerCache. collection))
([collection base] ([collection base]
(cache/seed (BasicMongerCache. collection) base))) (cache/seed (BasicMongerCache. collection) base)))
(defrecord DatabaseAwareMongerCache [db collection])
(extend-protocol cache/CacheProtocol
DatabaseAwareMongerCache
(lookup [c k]
(let [m (find-map-by-id (:db c) (:collection c) k)]
(:value m)))
(has? [c k]
(not (nil? (find-by-id (:db c) (:collection c) k))))
(hit [this k]
this)
(miss [c k v]
(mc/insert (:db c) (:collection c) {:_id k :value v} WriteConcern/SAFE)
c)
(evict [c k]
(mc/remove-by-id (:db c) (:collection c) k)
c)
(seed [c m]
(mc/insert-batch (:db c) (:collection c) (map (fn [[k v]]
{:_id k :value v}) m) WriteConcern/SAFE)
c))
(defn db-aware-monger-cache-factory
([db]
(DatabaseAwareMongerCache. db default-cache-collection))
([db collection]
(DatabaseAwareMongerCache. db collection))
([db collection base]
(cache/seed (DatabaseAwareMongerCache. db collection) base)))

View file

@ -555,9 +555,10 @@
EXAMPLES EXAMPLES
;; create a regular index ;; create a regular index
(monger.collection/ensure-index \"documents\" {\"language\" 1}) ;; clojure.core/array-map produces an ordered map
(monger.collection/ensure-index \"documents\" (array-map \"language\" 1))
;; create a unique index ;; create a unique index
(monger.collection/ensure-index \"pages\" {:url 1} {:unique true}) (monger.collection/ensure-index \"pages\" (array-map :url 1) {:unique true})
" "
([^String collection ^Map keys] ([^String collection ^Map keys]
(.ensureIndex (.getCollection monger.core/*mongodb-database* (name collection)) (as-field-selector keys))) (.ensureIndex (.getCollection monger.core/*mongodb-database* (name collection)) (as-field-selector keys)))

View file

@ -26,7 +26,7 @@
(defrecord ClojureReaderBasedMongoDBSessionStore [^String collection-name]) (defrecord ClojureReaderBasedMongoDBSessionStore [^String collection-name])
(defmethod print-dup java.util.Date (defmethod print-dup java.util.Date
[^java.util.Date d ^java.io.OutputStream out] [^java.util.Date d ^java.io.Writer out]
(.write out (.write out
(str "#=" (str "#="
`(java.util.Date. ~(.getYear d) `(java.util.Date. ~(.getYear d)
@ -37,7 +37,7 @@
~(.getSeconds d))))) ~(.getSeconds d)))))
(defmethod print-dup org.bson.types.ObjectId (defmethod print-dup org.bson.types.ObjectId
[oid out] [oid ^java.io.Writer out]
(.write out (.write out
(str "#=" (str "#="
`(org.bson.types.ObjectId. ~(str oid))))) `(org.bson.types.ObjectId. ~(str oid)))))

View file

@ -1,5 +1,6 @@
(ns monger.test.cache-test (ns monger.test.cache-test
(:require [monger.test.helper :as helper] (:require [monger.test.helper :as helper]
[monger.core :as mg]
[monger.collection :as mc]) [monger.collection :as mc])
(:use clojure.core.cache clojure.test monger.cache) (:use clojure.core.cache clojure.test monger.cache)
(:import [clojure.core.cache BasicCache FIFOCache LRUCache TTLCache] (:import [clojure.core.cache BasicCache FIFOCache LRUCache TTLCache]
@ -121,3 +122,42 @@
"Value" :skey "Value" :skey
l :lkey l :lkey
"keyword" "kkey")))) "keyword" "kkey"))))
(deftest ^{:cache true}
test-has?-with-db-aware-monger-cache
(testing "that has? returns false for misses"
(let [db (mg/get-db "altcache")
coll "db_aware_monger_cache_entries"
c (db-aware-monger-cache-factory db coll)]
(is (not (has? c (str (UUID/randomUUID)))))
(is (not (has? c (str (UUID/randomUUID)))))))
(testing "that has? returns true for hits"
(let [db (mg/get-db "altcache")
coll "db_aware_monger_cache_entries"
c (db-aware-monger-cache-factory db coll {"a" 1 "b" "cache" "c" 3/4})]
(is (has? c "a"))
(is (has? c "b"))
(is (has? c "c"))
(is (not (has? c "d"))))))
(deftest ^{:cache true}
test-lookup-with-basic-moger-cache
(testing "that lookup returns nil for misses"
(let [db (mg/get-db "altcache")
coll "db_aware_monger_cache_entries"
c (db-aware-monger-cache-factory db coll)]
(are [v] (is (nil? (lookup c v)))
:missing-key
"missing-key"
(gensym "missing-key"))))
(testing "that lookup returns cached values for hits"
(let [l (Long/valueOf 10000)
db (mg/get-db "altcache")
coll "db_aware_monger_cache_entries"
c (db-aware-monger-cache-factory db coll {:skey "Value" :lkey l "kkey" :keyword})]
(are [v k] (is (= v (lookup c k)))
"Value" :skey
l :lkey
"keyword" "kkey"))))

View file

@ -20,7 +20,7 @@
(deftest ^{:edge-features true :search true} test-basic-full-text-search-query (deftest ^{:edge-features true :search true} test-basic-full-text-search-query
(let [coll "docs"] (let [coll "docs"]
(mc/ensure-index coll {:subject "text" :content "text"}) (mc/ensure-index coll (array-map :subject "text" :content "text"))
(mc/insert coll {:subject "hello there" :content "this should be searchable"}) (mc/insert coll {:subject "hello there" :content "this should be searchable"})
(mc/insert coll {:subject "untitled" :content "this is just noize"}) (mc/insert coll {:subject "untitled" :content "this is just noize"})
(let [res (ms/search coll "hello") (let [res (ms/search coll "hello")

View file

@ -26,11 +26,11 @@
(mc/create-index collection ["language"]) (mc/create-index collection ["language"])
(mc/drop-index collection "language_1") (mc/drop-index collection "language_1")
(is (nil? (second (mc/indexes-on collection)))) (is (nil? (second (mc/indexes-on collection))))
(mc/ensure-index collection { "language" 1 } {:unique true}) (mc/ensure-index collection (array-map "language" 1) {:unique true})
(is (= "language_1" (is (= "language_1"
(:name (second (mc/indexes-on collection))))) (:name (second (mc/indexes-on collection)))))
(mc/ensure-index collection { "language" 1 }) (mc/ensure-index collection (array-map "language" 1))
(mc/ensure-index collection { "language" 1 } { :unique true }) (mc/ensure-index collection (array-map "language" 1) { :unique true })
(mc/drop-indexes collection))) (mc/drop-indexes collection)))
(deftest ^{:indexing true :edge-features true :time-consuming true} test-ttl-collections (deftest ^{:indexing true :edge-features true :time-consuming true} test-ttl-collections
@ -38,7 +38,7 @@
ttl 30 ttl 30
sleep 120] sleep 120]
(mc/remove coll) (mc/remove coll)
(mc/ensure-index coll {:created-at 1} {:expireAfterSeconds ttl}) (mc/ensure-index coll (array-map :created-at 1) {:expireAfterSeconds ttl})
(dotimes [i 100] (dotimes [i 100]
(mc/insert coll {:type "signup" :created-at (-> i secs ago) :i i})) (mc/insert coll {:type "signup" :created-at (-> i secs ago) :i i}))
(dotimes [i 100] (dotimes [i 100]