Compare commits

..

2 commits

Author SHA1 Message Date
Michael S. Klishin
f0fda3774c 1.2.1-SNAPSHOT 2012-08-29 19:24:55 +04:00
Michael S. Klishin
26d3fda015 Update change log 2012-08-29 17:49:17 +04:00
70 changed files with 3958 additions and 5123 deletions

12
.gitignore vendored
View file

@ -1,16 +1,10 @@
pom.xml* pom.xml
*jar *jar
/lib/ /lib/
/classes/ /classes/
.lein-* .lein-failures
.lein-deps-sum
TAGS TAGS
checkouts/* checkouts/*
doc/* doc/*
deploy.docs.sh deploy.docs.sh
target/*
todo.org
.nrepl-*
.idea/
*.iml
/.clj-kondo/.cache
/.lsp/.cache

View file

@ -1,20 +1,11 @@
language: clojure language: clojure
sudo: required lein: lein2
lein: lein
dist: xenial
before_script: before_script:
# Give MongoDB server some time to boot
- sleep 15
- mongod --version
- ./bin/ci/before_script.sh - ./bin/ci/before_script.sh
script: lein do clean, javac, test script: lein2 javac && lein2 all test
jdk: jdk:
- openjdk10 - openjdk6
- oraclejdk11 - openjdk7
- openjdk12 - oraclejdk7
services: services:
- mongodb - mongodb
branches:
only:
- master
- 3.5.x-stable

View file

@ -1,13 +0,0 @@
## Pre-requisites
The project uses [Leiningen 2](http://leiningen.org) and requires a recent MongoDB to be running
locally. Make sure you have those two installed and then run tests against all supported Clojure versions using
./bin/ci/before_script.sh
lein all do clean, javac, 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,563 +1,6 @@
## Changes between 3.5.x and 3.6.0 (unreleased) ## Changes between 1.2.0 and 1.2.1
### UUID Representation Option
Added a new connection option, `:uuid-representation`.
Contributed by @okorz001.
GitHub issue: [#212](https://github.com/michaelklishin/monger/issues/212)
### Operator List Update
For MongoDB 4.x.
Contributed by @mjrb.
GitHub issue: [#196](https://github.com/michaelklishin/monger/pull/196)
### Dependency Update
Contributed by @robhanlon22.
GitHub issue: [#206](https://github.com/michaelklishin/monger/pull/206)
## Changes between 3.1.x and 3.5.0 (Dec 10th, 2018)
### MongoDB Java Driver Update
MongoDB Java driver dependency has been updated to `3.9.x`.
This means that Monger now **requires JDK 8**.
Contributed by @Linicks.
### 3rd Party Library Compatibility
* Cheshire `5.8.x`
* clj-time `0.15.1`
* ring-core `0.15.1`
* Ragtime `0.7.x`.
### URI Connection Usability Improvement
URIs that don't specify a database will now be rejected as invalid.
Contributed by Chris Broome.
## Changes between 3.0.x and 3.1.0 (September 17th, 2016)
### MongoDB Java Driver Update
MongoDB Java driver dependency has been updated to `3.3.0`.
### Cursor Hinting Option Fix
Contributed by Stijn Opheide.
### Improved DBObject to Clojure Map conversion performance
New `from-db-object` implementation for `DBObject` avoids creation of an unnecessary
sequence and instead directly accesses `DBObject` instance in reduce. This should
offer performance improvement of about 20%. A performance test can be found
at [monger.test.stress-test](https://github.com/michaelklishin/monger/blob/master/test/monger/test/stress_test.clj).
Contributed by Juho Teperi.
### Authencation Function No Longer Ignores Credentials
In some cases Monger ignored provided credentials.
Contributed by Artem Chistyakov.
### Macro Type Hint Fixes
Contributed by Andre Ambrosio Boechat.
## Changes between 2.1.0 and 3.0.0
Monger 3.0 is based on the [MongoDB Java driver 3.0](https://www.mongodb.com/blog/post/introducing-30-java-driver)
and has some (relatively minor) **breaking API changes**.
### Error Handling Built Around Write Concerns
Monger no longer provides `monger.core/get-last-error`. It is no
longer needed: write concerns and exceptions is now the primary way for clients
to be notified of operation failures.
### New Authentication API
MongoDB 3.0 supports different authentication mechanisms. Multiple
credentials can be specified for a single connection. The client
and the server then can negotiate what authentication mechanism to use
and which set of credentials succeed.
Monger introduces a new namespace for credential instantiation:
`monger.credentials`. The most common function that relies on
authentication mechanism negotiation is `monger.credentials/for`:
``` clojure
(require '[monger.core :as mg])
(require '[monger.credentials :as mcr])
(let [creds (mcr/for "username" "db-name" "pa$$w0rd")
conn (mg/connect-with-credentials "127.0.0.1" creds)]
)
```
`mg/connect-with-credentials` is the most convenient function to
connect with if you plan on using authentication.
When connecting using a URI, the API hasn't changed.
### monger.search is Gone
`monger.search` is gone. MongoDB 3.0 supports search queries
using regular query operators, namely `$text`. `monger.operators` is
extended to include `$text`, `$search`, `$language`, and `$natural`.
An example of a search query in 3.0:
``` clojure
(require '[monger.core :as mg])
(require '[monger.credentials :as mcr])
(require '[monger.collection :as mc])
(require '[monger.operators :refer [$text $search]])
(let [creds (mcr/for "username" "db-name" "pa$$w0rd")
conn (mg/connect-with-credentials "127.0.0.1" creds)
db (mg/get-db conn "db-name")]
(mc/find-maps db "collection" {$text {$search "hello"}}))
```
### Add allow-disk-use and Cursor Options to Aggregates
`monger.collection/aggregate` now supports `:cursor` and `:allow-disk-use` options.
Contributed by Bartek Marcinowski.
### JSON Serialization of BSON Timestamps
JSON serialisation extensions now support BSON timestamps.
Contributed by Tom McMillen.
## Changes between 2.0.0 and 2.1.0
### Clojure 1.7 Compatibility
Monger now compiles with Clojure 1.7.
### MongoDB Java Driver Update
MongoDB Java driver dependency has been updated to `2.13.x`.
### $each Operator
The `$each` operator now can be used via `monger.operators`.
Contributed by Juha Jokimäki.
## Changes between 1.8.0 and 2.0.0
`2.0` is a major release that has **breaking public API changes**.
### Explicit Connection/DB/GridFS Argument
In Monger 2.0, all key public API functions require an explicit
DB/connection/GridFS object to be provided instead of relying on
a shared dynamic var. This makes Monger much easier to use with
systems such as Component and Jig, as well as concurrent
applications that need to work with multiple connections, database,
or GridFS filesystems.
In other words, instead of
``` clojure
(require '[monger.collection :as mc])
(mc/insert "libraries" {:name "Monger"})
```
it is now necessary to do
``` clojure
(require '[monger.collection :as mc])
(mc/insert db "libraries" {:name "Monger"})
```
This also means that `monger.core/connect!` and
`monger.core/connect-via-uri!` were removed, as was
`monger.multi` namespaces.
To connect to MongoDB, use `monger.core/connect`:
``` clojure
(require '[monger.core :as mg])
(let [conn (mg/connect)])
```
or `monger.core/connect-via-uri`:
``` clojure
(require '[monger.core :as mg])
(let [{:keys [conn db]} (mg/connect-via-uri "mongodb://clojurewerkz/monger:monger@127.0.0.1/monger-test4")])
```
To get a database reference, use `monger.core/get-db`, which now requires a connection
object:
``` clojure
(require '[monger.core :as mg])
(let [conn (mg/connect)
db (mg/get-db conn "monger-test")])
```
### Options as Maps
Functions that take options now require a proper Clojure map instead of
pseudo keyword arguments:
``` clojure
# in Monger 1.x
(mc/update db coll {} {:score 0} :multi true)
# in Monger 2.x
(mc/update db coll {} {:score 0} {:multi true})
```
## Changes between 1.8.0-beta2 and 1.8.0
### Clojure 1.6
Monger now depends on `org.clojure/clojure` version `1.6.0`. It is
still compatible with Clojure 1.4 and if your `project.clj` depends on
a different version, it will be used, but 1.6 is the default now.
## Changes between 1.8.0-beta1 and 1.8.0-beta2
### monger.result Use with WriteConcerns is Deprecated
MongoDB Java driver 2.12.x [no longer guarantees connection affinity](https://github.com/mongodb/mongo-java-driver/releases/tag/r2.12.0-rc0) for thread pool
threads.
This means that `WriteConcern#getLastError` is no longer a safe from concurrency
hazards. Therefore the use of `monger.result` functions on `WriteConcern` instances
is now **deprecated** in MongoDB Java client and Monger.
### MongoDB Java Driver Update
MongoDB Java driver dependency has been [updated to 2.12.x](https://github.com/mongodb/mongo-java-driver/releases/tag/r2.12.0-rc0).
### Default WriteConcern Change
Monger now uses [`WriteConcern/ACKNOWLEDGED`](http://api.mongodb.org/java/2.12/com/mongodb/WriteConcern.html#ACKNOWLEDGED) by default. Functionality-wise
it is the same as `WriteConcern/SAFE` in earlier versions.
## Changes between 1.7.0 and 1.8.0-beta1
### monger.core/connect-via-uri
`monger.core/connect-via-uri` is a version of `monger.core/connect-via-uri!`
which returns the connection instead of mutating a var.
It should be used by projects that are built from reloadable
components, together with `monger.multi.*`.
## Changes between 1.7.0-beta1 and 1.7.0
### MongoDB Java Driver Update
MongoDB Java driver dependency has been [updated to 2.11.3](https://github.com/mongodb/mongo-java-driver/releases/tag/r2.11.3).
### Ragtime Dependency Dropped
Ragtime is now an optional dependency: if your project uses `monger.ragtime`, you
need to add Ragtime to your own `project.clj`:
``` clojure
[ragtime/ragtime.core "0.3.4"]
```
### Validateur Dependency Dropped
[Validateur](http://clojurevalidations.info) is no longer a dependency.
## Changes between 1.6.0 and 1.7.0-beta1
### Fune Tuning Cursor Options
`monger.query` DSL now provides a way to fine tune database cursor
options:
``` clojure
(with-collection "products"
...
(options {:notimeout true, :slaveok false}) ;; where keyword matches Bytes/QUERYOPTION_*
(options [:notimeout :slaveok])
(options com.mongodb.Bytes/QUERYOPTION_NOTIMEOUT) ;; support Java constants
(options :notimeout)
...
```
`monger.cursor` is a new namespace that provides the plumbing for cursor
fine tuning but should not be widely used directly.
### Joda Time Integration Improvements: LocalDate
`LocalDate` instance serialization is now supported
by Monger Joda Time integration.
Contributed by Timo Sulg.
### Clojure 1.3 Is No Longer Supported
Monger now officially supports Clojure 1.4+.
### Cheshire Upgrade
[Cheshire](https://github.com/dakrone/cheshire) dependency has been upgraded to 5.2.0
### ClojureWerkz Support Upgrade
ClojureWerkz Support dependency has been updated to `0.19.0`.
### Validateur 1.5.0
[Validateur](https://github.com/michaelklishin/validateur) dependency has been upgraded to 1.5.0.
## Changes between 1.5.0 and 1.6.0
### monger.multi.collection
`monger.multi.collection` is a new namespace with functions that are very similar to those
in the `monger.collection` namespace but always take a database reference as an explicit argument.
They are supposed to be used in cases when relying on `monger.core/*mongodb-database*` is not
enough.
Erik Bakstad contributed most of this work.
### MongoDB Java Driver Update
MongoDB Java driver dependency has been [updated to 2.11.2](https://github.com/mongodb/mongo-java-driver/wiki/Release-Notes).
### monger.core/drop-db
`monger.core/drop-db` is a new function that drops a database by name.
### 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"))
```
### Ragtime changes
Bug fix: `monger.ragtime/applied-migration-ids` now returns a vector (instead of a set) in order to preserve the original creation order of the migrations.
Ragtime dependency has been updated to 0.3.3.
## Changes between 1.4.0 and 1.5.0
### Full Text Search Support
Full text search in MongoDB 2.4 can be used via commands but Monger 1.5 also provides
convenience functions in the `monger.search` namespace:
* `monger.search/search` for performing queries
* `monger.search/results-from` for obtaining hit documents sorted by score
``` clojure
(require '[monger.collection :as mc])
(require '[monger.search :as ms])
(mc/ensure-index coll {:subject "text" :content "text"})
(mc/insert coll {:subject "hello there" :content "this should be searchable"})
(mc/insert coll {:subject "untitled" :content "this is just noize"})
(println (ms/results-from (ms/search coll "hello"))
```
### MongoDB Java Driver Update
MongoDB Java driver dependency has been [updated to 2.11.0](https://github.com/mongodb/mongo-java-driver/wiki/Release-Notes).
### New Geospatial Operators
`monger.operators` now defines a few more operators for convenience:
* `$getWithin`
* `$getIntersects`
* `$near`
Of course, these and any other new operators can be passed as strings (e.g. `"$near"`)
as well.
### monger.core/admin-db
`monger.core/admin-db` is a new convenience function that returns the `admin` database
reference.
### monger.command/admin-command
`monger.command/admin-command` is a new convenience function for running commands
on the `admin` database.
### monger.core/mongo-options Updates
`monger.core/mongo-options` options are now up-to-date with the most recent
MongoDB Java driver.
### Factory DSL Is Gone
Monger's factory DSL (an undocumented experimental feature) has been removed from `monger.testkit`. It did
not work as well as we expected and there are better alternatives available now.
### Clojure 1.5 By Default
Monger now depends on `org.clojure/clojure` version `1.5.1`. It is still compatible with Clojure 1.3+ and if your `project.clj` depends
on a different version, it will be used, but 1.5 is the default now.
We encourage all users to upgrade to 1.5, it is a drop-in replacement for the majority of projects out there.
### Authentication On Default Database
`monger.core/authenticate` now has a 2-arity version that will authenticate
on the default database:
``` clojure
(let [username "myservice"
pwd "LGo5h#B`cTRQ>28tba6u"]
(monger.core/use-db! "mydb")
;; authenticates requests for mydb
(monger.core/authenticate username (.toCharArray pwd)))
```
### ClojureWerkz Support Upgrade
ClojureWerkz Support dependency has been updated to version `0.15.0`.
This means Monger now will use Cheshire `5.0.x`.
### Explicit DBCursor Closure by monger.collection/find-maps and the like
`monger.collection/find-maps` and the like will now explicitly close DB cursors.
GH issue: 47
## Changes between 1.3.0 and 1.4.0
### Cheshire Upgrade
`clojurewerkz.support.json` now requires [Cheshire] `5.0`. There were some incompatible changes
in Cheshire `5.0`, see [Cheshire change log](https://github.com/dakrone/cheshire/blob/master/ChangeLog.md#changes-between-cheshire-500-and-40x).
### data.json Dependency Fixes
`monger.json` no longer requires `data.json` to be present at compile time.
### MongoDB Java Driver Update
MongoDB Java driver dependency has been updated to 2.10.0.
### ClojureWerkz Support Upgrade
ClojureWerkz Support dependency has been updated to version `0.9.0`.
## Changes between 1.2.0 and 1.3.0
### monger.core/disconnect!
`monger.core/disconnect!` closes the default database connection.
### Ragtime 0.3.0
Ragtime dependency has been updated to 0.3.0.
### MongoDB Java Driver Update
MongoDB Java driver dependency has been updated to 2.9.2.
### Cheshire Support
`monger.json` and `monger.joda-time` will now use [Cheshire](https://github.com/dakrone/cheshire) if it is available. [clojure.data.json](https://github.com/clojure/data.json)
is no longer a hard dependency (but still supported if available).
Because `clojure.data.json` is no longer a hard Monger dependency, you need to either add it as explicit
dependency to your project or switch to Cheshire.
To switch to Cheshire (you may need to update your code that uses `clojure.data.json` directly!),
add the following to your `:dependencies` list:
``` clojure
[cheshire "4.0.3"]
```
For `clojure.data.json` version `0.1.2.`:
``` clojure
[org.clojure/data.json "0.2.0"]
```
### ClojureWerkz Support 0.7.0
ClojureWerkz Support dependency has been updated to version `0.7.0`.
No changes yet.
## Changes between 1.1.0 and 1.2.0 ## Changes between 1.1.0 and 1.2.0
@ -1181,3 +624,4 @@ given ObjectId. `monger.collection/remove-by-id` is its counterpart for removing
### monger.core/get-db-names ### monger.core/get-db-names
monger.core/get-db-names returns a set of databases. Contributed by Toby Hede. monger.core/get-db-names returns a set of databases. Contributed by Toby Hede.

100
README.md
View file

@ -1,43 +1,45 @@
# Monger, a modern Clojure MongoDB Driver # Monger, a modern Clojure MongoDB Driver
[![Build Status](https://travis-ci.org/xingzhefeng/monger.svg?branch=master)](https://travis-ci.org/xingzhefeng/monger)
Monger is an idiomatic [Clojure MongoDB driver](http://clojuremongodb.info) for a more civilized age. Monger is an idiomatic [Clojure MongoDB driver](http://clojuremongodb.info) for a more civilized age.
It has batteries included, offers powerful expressive query DSL, It has batteries included, offers powerful expressive query DSL, strives to support every MongoDB 2.0+ feature and has sane defaults. Monger is built from the
strives to support modern MongoDB features and have the "look and feel" and ground up for Clojure 1.3+ and sits on top of the official MongoDB Java driver.
flexibility of the MongoDB shell.
Monger is built from for modern Clojure versions and sits on top of
the official MongoDB Java driver.
## Project Goals ## Project Goals
There is one MongoDB client for Clojure that has been around since 2009. So, why create another one? Monger authors There is one MongoDB client for Clojure that has been around since 2009. So, why create another one? Monger authors
wanted a client that would wanted a client that will
* Support most of modern MongoDB features, focus on those that really matter. * Support most of MongoDB 2.0+ features, focus on those that really matter.
* Be [well documented](http://clojuremongodb.info). * Be [well documented](http://clojuremongodb.info).
* Be [well tested](https://github.com/michaelklishin/monger/tree/master/test/monger/test). * Be [well tested](https://github.com/michaelklishin/monger/tree/master/test/monger/test).
* Target modern Clojure versions. * Target Clojure 1.3.0 and later from the ground up.
* Be as close to the Mongo shell query language as practical * Be as close to the Mongo shell query language as practical
* Integrate with libraries like Joda Time, [Cheshire](https://github.com/dakrone/cheshire), clojure.data.json, [Ragtime](https://github.com/weavejester/ragtime). * Integrate with libraries like clojure.data.json, Joda Time, [Ragtime](https://github.com/weavejester/ragtime).
* Support URI connections to be friendly to Heroku and other PaaS providers. * Support URI connections to be friendly to Heroku and other PaaS providers.
* Not carry technical debt from 2009 forever. * Not carry technical debt from 2009 forever.
* Integrate usage of JavaScript files and ClojureScript (as soon as the compiler gets artifact it is possible to depend on for easy embedding). * Integrate usage of JavaScript files and ClojureScript (as soon as the compiler gets artifact it is possible to depend on for easy embedding).
## Community
[Monger has a mailing list](https://groups.google.com/forum/#!forum/clojure-mongodb). Feel free to join it and ask any questions you may have.
To subscribe for announcements of releases, important changes and so on, please follow [@ClojureWerkz](https://twitter.com/#!/clojurewerkz) on Twitter.
## Project Maturity ## Project Maturity
Monger is not a young project: started in July 2011, it is over 7 Monger is not a young project: started in July 2011, it is over 1 year old with active production use from week 1.
years old with active production use from week 1.
## Artifacts ## Artifacts
Monger artifacts are [released to Monger artifacts are [released to Clojars](https://clojars.org/com.novemberain/monger). If you are using Maven, add the following repository
Clojars](https://clojars.org/com.novemberain/monger). If you are using definition to your `pom.xml`:
Maven, add the following repository definition to your `pom.xml`:
``` xml ``` xml
<repository> <repository>
@ -50,60 +52,68 @@ Maven, add the following repository definition to your `pom.xml`:
With Leiningen: With Leiningen:
[com.novemberain/monger "3.5.0"] [com.novemberain/monger "1.2.0"]
With Maven: With Maven:
<dependency> <dependency>
<groupId>com.novemberain</groupId> <groupId>com.novemberain</groupId>
<artifactId>monger</artifactId> <artifactId>monger</artifactId>
<version>3.5.0</version> <version>1.2.0</version>
</dependency> </dependency>
## Getting Started ## Getting Started
Please refer to our [Getting Started Please refer to our [Getting Started guide](http://clojuremongodb.info/articles/getting_started.html). Don't hesitate to join our [mailing list](https://groups.google.com/forum/#!forum/clojure-mongodb) and ask questions, too!
guide](http://clojuremongodb.info/articles/getting_started.html). Don't
hesitate to join our [mailing
list](https://groups.google.com/forum/#!forum/clojure-mongodb) and ask
questions, too!
## Documentation & Examples ## Documentation & Examples
Please see our [documentation guides site](http://clojuremongodb.info/) and [API reference](http://reference.clojuremongodb.info). Please see our [documentation guides site](http://clojuremongodb.info/) and [API reference](http://reference.clojuremongodb.info).
Our [test suite](https://github.com/michaelklishin/monger/tree/master/test/monger/test) Our [test suite](https://github.com/michaelklishin/monger/tree/master/test/monger/test) also has many code examples.
also has many code examples.
## Community
[Monger has a mailing list](https://groups.google.com/forum/#!forum/clojure-mongodb). Feel
free to join it and ask any questions you may have.
To subscribe for announcements of releases, important changes and so
on, please follow [@ClojureWerkz](https://twitter.com/#!/clojurewerkz)
on Twitter.
## Supported Clojure versions ## Supported Clojure versions
Monger requires Clojure 1.8+. The most recent Monger is built from the ground up for Clojure 1.3 and up. Clojure 1.4 is recommended.
stable release is highly recommended.
## Continuous Integration Status ## Continuous Integration Status
[![Continuous Integration status](https://secure.travis-ci.org/michaelklishin/monger.svg)](http://travis-ci.org/michaelklishin/monger) [![Continuous Integration status](https://secure.travis-ci.org/michaelklishin/monger.png)](http://travis-ci.org/michaelklishin/monger)
## Monger Is a ClojureWerkz Project ## Monger Is a ClojureWerkz Project
Monger is part of the [group of Clojure libraries known as ClojureWerkz](http://clojurewerkz.org), together with Monger is part of the [group of Clojure libraries known as ClojureWerkz](http://clojurewerkz.org), together with
[Cassaforte](http://clojurecassandra.info), [Langohr](http://clojurerabbitmq.info), [Elastisch](http://clojureelasticsearch.info), [Quartzite](http://clojurequartz.info) and several others. [Neocons](https://github.com/michaelklishin/neocons), [Langohr](https://github.com/michaelklishin/langohr), [Elastisch](https://github.com/clojurewerkz/elastisch), [Welle](https://github.com/michaelklishin/welle), [Quartzite](https://github.com/michaelklishin/quartzite) and several others.
## Write Performance
Monger insert operations are efficient and have very little overhead compared to the underlying Java driver. Here
are some numbers on a MacBook Pro from fall 2010 with Core i7 and an Intel SSD drive:
```
Testing monger.test.stress
Inserting 1000 documents...
"Elapsed time: 25.699 msecs"
Inserting 10000 documents...
"Elapsed time: 135.069 msecs"
Inserting 100000 documents...
"Elapsed time: 515.969 msecs"
```
With the `SAFE` write concern, it takes roughly 0.5 second to insert 100,000 documents with Clojure 1.3.0.
@ -112,14 +122,7 @@ Monger is part of the [group of Clojure libraries known as ClojureWerkz](http://
Monger uses [Leiningen 2](https://github.com/technomancy/leiningen/blob/master/doc/TUTORIAL.md). Make sure you have it installed and then run tests against Monger uses [Leiningen 2](https://github.com/technomancy/leiningen/blob/master/doc/TUTORIAL.md). Make sure you have it installed and then run tests against
supported Clojure versions using supported Clojure versions using
./bin/ci/before_script.sh lein2 all test
lein all do clean, javac, test
Or, if you don't have mongodb installed, you can use docker
docker-compose up
./bin/ci/before_script_docker.sh
lein all do clean, javac, test
Then create a branch and make your changes on it. Once you are done with your changes and all tests pass, submit a pull request Then create a branch and make your changes on it. Once you are done with your changes and all tests pass, submit a pull request
on Github. on Github.
@ -128,7 +131,6 @@ on Github.
## License ## License
Copyright (C) 2011-2018 [Michael S. Klishin](http://twitter.com/michaelklishin), Alex Petrov, and the ClojureWerkz team. Copyright (C) 2011-2012 Michael S. Klishin
Double licensed under the [Eclipse Public License](http://www.eclipse.org/legal/epl-v10.html) (the same as Clojure) or Distributed under the [Eclipse Public License](http://www.eclipse.org/legal/epl-v10.html), the same as Clojure.
the [Apache Public License 2.0](http://www.apache.org/licenses/LICENSE-2.0.html).

View file

@ -1,18 +1,8 @@
#!/bin/sh #!/bin/sh
# Check which MongoDB shell is available
if command -v mongosh >/dev/null 2>&1; then
MONGO_SHELL="mongosh"
elif command -v mongo >/dev/null 2>&1; then
MONGO_SHELL="mongo"
else
echo "Error: Neither mongo nor mongosh shell found. Please install MongoDB shell."
exit 1
fi
# MongoDB Java driver won't run authentication twice on the same DB instance, # MongoDB Java driver won't run authentication twice on the same DB instance,
# so we need to use multiple DBs. # so we need to use multiple DBs.
$MONGO_SHELL --eval 'db.createUser({"user": "clojurewerkz/monger", "pwd": "monger", roles: ["dbAdmin"], mechanisms: ["SCRAM-SHA-1"], passwordDigestor: "client"})' monger-test mongo --eval 'db.addUser("clojurewerkz/monger", "monger")' monger-test
$MONGO_SHELL --eval 'db.createUser({"user": "clojurewerkz/monger", "pwd": "monger", roles: ["dbAdmin"], mechanisms: ["SCRAM-SHA-1"], passwordDigestor: "client"})' monger-test2 mongo --eval 'db.addUser("clojurewerkz/monger", "monger")' monger-test2
$MONGO_SHELL --eval 'db.createUser({"user": "clojurewerkz/monger", "pwd": "monger", roles: ["dbAdmin"], mechanisms: ["SCRAM-SHA-1"], passwordDigestor: "client"})' monger-test3 mongo --eval 'db.addUser("clojurewerkz/monger", "monger")' monger-test3
$MONGO_SHELL --eval 'db.createUser({"user": "clojurewerkz/monger", "pwd": "monger", roles: ["dbAdmin"], mechanisms: ["SCRAM-SHA-1"], passwordDigestor: "client"})' monger-test4 mongo --eval 'db.addUser("clojurewerkz/monger", "monger")' monger-test4

View file

@ -1,18 +0,0 @@
#!/bin/sh
# Check which MongoDB shell is available in the container
if docker exec mongo_test which mongosh >/dev/null 2>&1; then
MONGO_SHELL="mongosh"
elif docker exec mongo_test which mongo >/dev/null 2>&1; then
MONGO_SHELL="mongo"
else
echo "Error: Neither mongo nor mongosh shell found in the container."
exit 1
fi
# MongoDB Java driver won't run authentication twice on the same DB instance,
# so we need to use multiple DBs.
docker exec mongo_test $MONGO_SHELL --eval 'db.createUser({"user": "clojurewerkz/monger", "pwd": "monger", roles: ["dbAdmin"], mechanisms: ["SCRAM-SHA-1"], passwordDigestor: "client"})' monger-test
docker exec mongo_test $MONGO_SHELL --eval 'db.createUser({"user": "clojurewerkz/monger", "pwd": "monger", roles: ["dbAdmin"], mechanisms: ["SCRAM-SHA-1"], passwordDigestor: "client"})' monger-test2
docker exec mongo_test $MONGO_SHELL --eval 'db.createUser({"user": "clojurewerkz/monger", "pwd": "monger", roles: ["dbAdmin"], mechanisms: ["SCRAM-SHA-1"], passwordDigestor: "client"})' monger-test3
docker exec mongo_test $MONGO_SHELL --eval 'db.createUser({"user": "clojurewerkz/monger", "pwd": "monger", roles: ["dbAdmin"], mechanisms: ["SCRAM-SHA-1"], passwordDigestor: "client"})' monger-test4

View file

@ -1,11 +0,0 @@
#!/bin/sh
# MongoDB seems to need some time to boot first. MK.
sleep 5
# MongoDB Java driver won't run authentication twice on the same DB instance,
# so we need to use multiple DBs.
mongo --eval 'db.createUser({"user": "clojurewerkz/monger", "pwd": "monger", roles: ["dbAdmin"], passwordDigestor: "client"})' monger-test
mongo --eval 'db.createUser({"user": "clojurewerkz/monger", "pwd": "monger", roles: ["dbAdmin"], passwordDigestor: "client"})' monger-test2
mongo --eval 'db.createUser({"user": "clojurewerkz/monger", "pwd": "monger", roles: ["dbAdmin"], passwordDigestor: "client"})' monger-test3
mongo --eval 'db.createUser({"user": "clojurewerkz/monger", "pwd": "monger", roles: ["dbAdmin"], passwordDigestor: "client"})' monger-test4

View file

@ -1,8 +0,0 @@
#!/bin/sh
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 9DA31620334BD75D9DCB49F368818C72E52529D4
echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu xenial/mongodb-org/4.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-4.0.list
sudo apt-get update
sudo apt-get install -y mongodb-org

View file

@ -1,11 +0,0 @@
# Use root/example as user/password credentials
version: '3.1'
services:
mongo:
image: mongo
container_name: mongo_test
restart: always
ports:
- "27017:27017"

View file

@ -1,25 +1,24 @@
(defproject com.novemberain/monger "4.0.0-SNAPSHOT" (defproject com.novemberain/monger "1.2.1-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.5.1" :min-lein-version "2.0.0"
:license {:name "Eclipse Public License" :license {:name "Eclipse Public License"}
:url "http://www.eclipse.org/legal/epl-v10.html"} :dependencies [[org.clojure/clojure "1.4.0"]
:dependencies [[org.clojure/clojure "1.11.1"] [org.mongodb/mongo-java-driver "2.9.0"]
[org.mongodb/mongodb-driver "3.12.11"] [com.novemberain/validateur "1.2.0"]
[clojurewerkz/support "1.5.0"]] [clojurewerkz/support "0.6.0"]
[ragtime/ragtime.core "0.2.0"]]
:test-selectors {:default (fn [m] :test-selectors {:default (fn [m]
(and (not (:performance m)) (and (not (:performance m))
(not (:edge-features m)) (not (:edge-features m))
(not (:time-consuming m)))) (not (:time-consuming m))))
:focus :focus :focus :focus
:authentication :authentication
:updating :updating :updating :updating
:indexing :indexing :indexing :indexing
:external :external :external :external
:cache :cache :cache :cache
:gridfs :gridfs :gridfs :gridfs
:command :command :command :command
:integration :integration
:performance :performance :performance :performance
;; as in, edge mongodb server ;; as in, edge mongodb server
:edge-features :edge-features :edge-features :edge-features
@ -27,32 +26,36 @@
:all (constantly true)} :all (constantly true)}
:source-paths ["src/clojure"] :source-paths ["src/clojure"]
:java-source-paths ["src/java"] :java-source-paths ["src/java"]
:javac-options ["-target" "1.8" "-source" "1.8"] :javac-options ["-target" "1.6" "-source" "1.6"]
:codox {:exclude [monger.internal.pagination
monger.internal.fn
;; these are not fully baked yet or have changes
;; that are not entirely backwards compatible with 1.0. MK.
monger.testkit
monger.ring.session-store]}
:mailing-list {:name "clojure-mongodb" :mailing-list {:name "clojure-mongodb"
:archive "https://groups.google.com/group/clojure-mongodb" :archive "https://groups.google.com/group/clojure-mongodb"
:post "clojure-mongodb@googlegroups.com"} :post "clojure-mongodb@googlegroups.com"}
:profiles {:1.10 {:dependencies [[org.clojure/clojure "1.10.2"]]} :profiles {:1.3 {:dependencies [[org.clojure/clojure "1.3.0"]]}
:1.9 {:dependencies [[org.clojure/clojure "1.9.0"]]} :1.5 {:dependencies [[org.clojure/clojure "1.5.0-master-SNAPSHOT"]]}
:dev {:resource-paths ["test/resources"] :dev {:resource-paths ["test/resources"]
:dependencies [[clj-time "0.15.1" :exclusions [org.clojure/clojure]] :dependencies [[clj-time "0.4.2" :exclusions [org.clojure/clojure]]
[cheshire "5.8.1" :exclusions [org.clojure/clojure]] [org.clojure/data.json "0.1.2" :exclusions [org.clojure/clojure]]
[org.clojure/data.json "2.5.0" :exclusions [org.clojure/clojure]] [org.clojure/tools.cli "0.2.1" :exclusions [org.clojure/clojure]]
[org.clojure/tools.cli "0.4.1" :exclusions [org.clojure/clojure]] [org.clojure/core.cache "0.6.0" :exclusions [org.clojure/clojure]]
[org.clojure/core.cache "0.7.1" :exclusions [org.clojure/clojure]] [ring/ring-core "1.1.0"]]
[ring/ring-core "1.7.1" :exclusions [org.clojure/clojure]] :plugins [[codox "0.6.1"]]
[com.novemberain/validateur "2.6.0" :exclusions [org.clojure/clojure]] :codox {:sources ["src/clojure"]
[ch.qos.logback/logback-classic "1.2.3" :exclusions [org.slf4j/slf4j-api]] :output-dir "doc/api"}}
[ragtime/core "0.7.2" :exclusions [org.clojure/clojure]]]
:plugins [[lein-codox "0.10.5"]]
:codox {:source-paths ["src/clojure"]
:namespaces [#"^monger\.(?!internal)"]}}
;; 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.15.2" :exclusions [org.clojure/clojure]]]}} :dependencies [[clj-time "0.4.2" :exclusions [org.clojure/clojure]]]}}
:aliases {"all" ["with-profile" "dev:dev,1.10:dev,1.9:dev"]} :aliases {"all" ["with-profile" "dev:dev,1.3:dev,1.5"]
:repositories {"sonatype" {:url "https://oss.sonatype.org/content/repositories/releases" "ci" ["with-profile" "dev:dev,1.3:dev,1.5"]}
:repositories {"sonatype" {:url "http://oss.sonatype.org/content/repositories/releases"
:snapshots false :snapshots false
:releases {:checksum :fail :update :always}} :releases {:checksum :fail :update :always}}
"sonatype-snapshots" {:url "https://oss.sonatype.org/content/repositories/snapshots" "sonatype-snapshots" {:url "http://oss.sonatype.org/content/repositories/snapshots"
:snapshots true :snapshots true
:releases {:checksum :fail :update :always}}}) :releases {:checksum :fail :update :always}}}
:aot [monger.conversion])

View file

@ -1,46 +1,12 @@
;; This source code is dual-licensed under the Apache License, version (ns ^{:doc "clojure.core.cache implementation(s) on top of MongoDB.
;; 2.0, and the Eclipse Public License, version 1.0.
;;
;; The APL v2.0:
;;
;; ----------------------------------------------------------------------------------
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team
;;
;; Licensed under the Apache License, Version 2.0 (the "License");
;; you may not use this file except in compliance with the License.
;; You may obtain a copy of the License at
;;
;; http://www.apache.org/licenses/LICENSE-2.0
;;
;; Unless required by applicable law or agreed to in writing, software
;; distributed under the License is distributed on an "AS IS" BASIS,
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;; See the License for the specific language governing permissions and
;; limitations under the License.
;; ----------------------------------------------------------------------------------
;;
;; The EPL v1.0:
;;
;; ----------------------------------------------------------------------------------
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team.
;; All rights reserved.
;;
;; This program and the accompanying materials are made available under the terms of
;; the Eclipse Public License Version 1.0,
;; which accompanies this distribution and is available at
;; http://www.eclipse.org/legal/epl-v10.html.
;; ----------------------------------------------------------------------------------
(ns monger.cache
"clojure.core.cache implementation(s) on top of MongoDB.
Related documentation guide: http://clojuremongodb.info/articles/integration.html" Related documentation guide: http://clojuremongodb.info/articles/integration.html"
(:require [monger.collection :as mc :refer [find-one find-by-id find-map-by-id]] :author "Michael S. Klishin"}
[clojure.core.cache :as cache] monger.cache
[monger.conversion :as cnv]) (:require [monger.collection :as mc]
(:import clojure.core.cache.CacheProtocol [clojure.core.cache :as cache])
[com.mongodb DB DBObject WriteConcern] (:use monger.conversion)
java.util.Map)) (:import clojure.core.cache.CacheProtocol))
;; ;;
;; Implementation ;; Implementation
@ -53,33 +19,36 @@
;; API ;; API
;; ;;
(defrecord BasicMongerCache [db collection]) (defrecord BasicMongerCache [collection])
(extend-protocol cache/CacheProtocol (extend-protocol cache/CacheProtocol
BasicMongerCache BasicMongerCache
(lookup [c k] (lookup [c k]
(let [m (mc/find-map-by-id (:db c) (:collection c) k)] (:value (mc/find-map-by-id (:collection c) k)))
(:value m))) #_ (lookup [c k not-found]
(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 (:db c) (:collection c) k)))) (not (nil? (mc/find-by-id (get c :collection) k))))
(hit [this k] (hit [this k]
this) this)
(miss [c k v] (miss [c k v]
(mc/insert (:db c) (:collection c) {:_id k :value v}) (mc/insert (get c :collection) {:_id k :value v})
c) c)
(evict [c k] (evict [c k]
(mc/remove-by-id (:db c) (:collection c) k) (mc/remove-by-id (get c :collection) k)
c) c)
(seed [c m] (seed [c m]
(mc/insert-batch (:db c) (:collection c) (map (fn [[k v]] (mc/insert-batch (get c :collection) (map (fn [[k v]]
{:_id k :value v}) m)) {:_id k :value v}) m))
c)) c))
(defn basic-monger-cache-factory (defn basic-monger-cache-factory
([^DB db] ([]
(BasicMongerCache. db default-cache-collection)) (BasicMongerCache. default-cache-collection))
([^DB db collection] ([collection]
(BasicMongerCache. db collection)) (BasicMongerCache. collection))
([^DB db collection base] ([collection base]
(cache/seed (BasicMongerCache. db collection) base))) (cache/seed (BasicMongerCache. collection) base)))

View file

@ -1,43 +1,15 @@
;; This source code is dual-licensed under the Apache License, version ;; Copyright (c) 2011-2012 Michael S. Klishin
;; 2.0, and the Eclipse Public License, version 1.0.
;;
;; The APL v2.0:
;;
;; ----------------------------------------------------------------------------------
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team
;; Copyright (c) 2012 Toby Hede ;; Copyright (c) 2012 Toby Hede
;; Copyright (c) 2012 Baishampayan Ghose ;; Copyright (c) 2012 Baishampayan Ghose
;; ;;
;; Licensed under the Apache License, Version 2.0 (the "License"); ;; The use and distribution terms for this software are covered by the
;; you may not use this file except in compliance with the License. ;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
;; You may obtain a copy of the License at ;; which can be found in the file epl-v10.html at the root of this distribution.
;; ;; By using this software in any fashion, you are agreeing to be bound by
;; http://www.apache.org/licenses/LICENSE-2.0 ;; the terms of this license.
;; ;; You must not remove this notice, or any other, from this software.
;; Unless required by applicable law or agreed to in writing, software
;; distributed under the License is distributed on an "AS IS" BASIS,
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;; See the License for the specific language governing permissions and
;; limitations under the License.
;; ----------------------------------------------------------------------------------
;;
;; The EPL v1.0:
;;
;; ----------------------------------------------------------------------------------
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team.
;; Copyright (c) 2012 Toby Hede
;; Copyright (c) 2012 Baishampayan Ghose
;;
;; All rights reserved.
;;
;; This program and the accompanying materials are made available under the terms of
;; the Eclipse Public License Version 1.0,
;; which accompanies this distribution and is available at
;; http://www.eclipse.org/legal/epl-v10.html.
;; ----------------------------------------------------------------------------------
(ns monger.collection (ns ^{:doc "Provides key functionality for interaction with MongoDB: inserting, querying, updating and deleting documents, performing Aggregation Framework
"Provides key functionality for interaction with MongoDB: inserting, querying, updating and deleting documents, performing Aggregation Framework
queries, creating and dropping indexes, creating collections and more. queries, creating and dropping indexes, creating collections and more.
For more advanced read queries, see monger.query. For more advanced read queries, see monger.query.
@ -49,20 +21,24 @@
* http://clojuremongodb.info/articles/querying.html * http://clojuremongodb.info/articles/querying.html
* http://clojuremongodb.info/articles/updating.html * http://clojuremongodb.info/articles/updating.html
* http://clojuremongodb.info/articles/deleting.html * http://clojuremongodb.info/articles/deleting.html
* http://clojuremongodb.info/articles/aggregation.html" * http://clojuremongodb.info/articles/aggregation.html"}
(:refer-clojure :exclude [find remove count drop distinct empty? any? update]) monger.collection
(:import [com.mongodb Mongo DB DBCollection WriteResult DBObject WriteConcern (:refer-clojure :exclude [find remove count drop distinct empty?])
DBCursor MapReduceCommand MapReduceCommand$OutputType AggregationOutput (:import [com.mongodb Mongo DB DBCollection WriteResult DBObject WriteConcern DBCursor MapReduceCommand MapReduceCommand$OutputType]
AggregationOptions AggregationOptions$OutputMode]
[java.util List Map] [java.util List Map]
[java.util.concurrent TimeUnit]
[clojure.lang IPersistentMap ISeq] [clojure.lang IPersistentMap ISeq]
org.bson.types.ObjectId) org.bson.types.ObjectId)
(:require [monger.core :as mc] (:require [monger core result])
[monger.result :as mres] (:use [monger.conversion]))
[monger.conversion :refer :all]
[monger.constraints :refer :all] ;;
[monger.util :refer [into-array-list]])) ;; Implementation
;;
(definline check-not-nil!
[ref ^String message]
`(when (nil? ~ref)
(throw (IllegalArgumentException. ~message))))
;; ;;
@ -74,17 +50,28 @@
;; ;;
(defn ^WriteResult insert (defn ^WriteResult insert
"Saves document to collection and returns a write result monger.result/acknowledged? "Saves @document@ to @collection@ and returns write result monger.result/ok? and similar functions operate on. You can optionally specify WriteConcern.
and related functions operate on. You can optionally specify a WriteConcern.
In case you need the exact inserted document returned, with the :_id key generated, In case you need the exact inserted document returned, with the :_id key generated, use monger.collection/insert-and-return
use monger.collection/insert-and-return instead." instead.
([^DB db ^String coll document]
(.insert (.getCollection db (name coll)) EXAMPLES:
;; returns write result
(monger.collection/insert \"people\" {:name \"Joe\", :age 30})
(monger.collection/insert \"people\" {:name \"Joe\", :age 30, WriteConcern/SAFE})
"
([^String collection document]
(.insert (.getCollection monger.core/*mongodb-database* (name collection))
(to-db-object document) (to-db-object document)
^WriteConcern mc/*mongodb-write-concern*)) ^WriteConcern monger.core/*mongodb-write-concern*))
([^DB db ^String coll document ^WriteConcern concern] ([^String collection document ^WriteConcern concern]
(.insert (.getCollection db (name coll)) (.insert (.getCollection monger.core/*mongodb-database* (name collection))
(to-db-object document)
concern))
([^DB db ^String collection document ^WriteConcern concern]
(.insert (.getCollection db (name collection))
(to-db-object document) (to-db-object document)
concern))) concern)))
@ -92,30 +79,49 @@
(defn ^clojure.lang.IPersistentMap insert-and-return (defn ^clojure.lang.IPersistentMap insert-and-return
"Like monger.collection/insert but returns the inserted document as a persistent Clojure map. "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 If the :_id key wasn't set on the document, it will be generated and merged into the returned map.
map."
([^DB db ^String coll document] EXAMPLES:
(insert-and-return db coll document ^WriteConcern mc/*mongodb-write-concern*))
([^DB db ^String coll document ^WriteConcern concern] ;; returns the entire document with :_id generated
;; MongoDB Java driver will generate the _id and set it but it (monger.collection/insert-and-return \"people\" {:name \"Joe\", :age 30})
;; tries to mutate the inserted DBObject and it does not work
;; very well in our case, because that DBObject is short lived (monger.collection/insert-and-return \"people\" {:name \"Joe\", :age 30, WriteConcern/SAFE})
;; and produced from the Clojure map we are passing in. Plus, "
;; this approach is very awkward with immutable data structures ([^String collection document]
;; being the default. MK. (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 {:_id (ObjectId.)} document)] (let [doc (merge {:_id (ObjectId.)} document)]
(insert db coll doc concern) (insert db collection doc concern)
doc))) doc)))
(defn ^WriteResult insert-batch (defn ^WriteResult insert-batch
"Saves documents to collection. You can optionally specify WriteConcern as a third argument." "Saves @documents@ do @collection@. You can optionally specify WriteConcern as a third argument.
([^DB db ^String coll ^List documents]
(.insert (.getCollection db (name coll)) EXAMPLES:
(monger.collection/insert-batch \"people\" [{:name \"Joe\", :age 30}, {:name \"Paul\", :age 27}])
(monger.collection/insert-batch \"people\" [{:name \"Joe\", :age 30}, {:name \"Paul\", :age 27}] WriteConcern/NORMAL)
"
([^String collection ^List documents]
(.insert (.getCollection monger.core/*mongodb-database* (name collection))
^List (to-db-object documents) ^List (to-db-object documents)
^WriteConcern mc/*mongodb-write-concern*)) ^WriteConcern monger.core/*mongodb-write-concern*))
([^DB db ^String coll ^List documents ^WriteConcern concern] ([^String collection ^List documents ^WriteConcern concern]
(.insert (.getCollection db (name coll)) (.insert (.getCollection monger.core/*mongodb-database* (name collection))
^List (to-db-object documents)
concern))
([^DB db ^String collection ^List documents ^WriteConcern concern]
(.insert (.getCollection db (name collection))
^List (to-db-object documents) ^List (to-db-object documents)
concern))) concern)))
@ -126,83 +132,126 @@
(defn ^DBCursor find (defn ^DBCursor find
"Queries for objects in this collection. "Queries for objects in this collection.
This function returns DBCursor, which allows you to iterate over DBObjects. This function returns DBCursor, which allows you to iterate over DBObjects.
If you want to manipulate clojure sequences maps, use find-maps." If you want to manipulate clojure sequences maps, please @find-maps@.
([^DB db ^String coll]
(.find (.getCollection db (name coll)))) EXAMPLES:
([^DB db ^String coll ^Map ref] ;; return all objects in this collection.
(.find (.getCollection db (name coll)) (mgcol/find \"people\")
;; return all objects matching query
(mgcol/find \"people\" {:company \"Comp Corp\"})
;; return all objects matching query, taking only specified fields
(mgcol/find \"people\" {:company \"Comp Corp\"} [:first_name :last_name])
"
([^String collection]
(.find (.getCollection monger.core/*mongodb-database* (name collection))))
([^String collection ^Map ref]
(.find (.getCollection monger.core/*mongodb-database* (name collection))
(to-db-object ref))) (to-db-object ref)))
([^DB db ^String coll ^Map ref fields] ([^String collection ^Map ref fields]
(.find (.getCollection db (name coll)) (.find (.getCollection monger.core/*mongodb-database* (name collection))
(to-db-object ref)
(as-field-selector fields)))
([^DB db ^String collection ^Map ref fields]
(.find (.getCollection db (name collection))
(to-db-object ref) (to-db-object ref)
(as-field-selector fields)))) (as-field-selector fields))))
(defn find-maps (defn find-maps
"Queries for objects in this collection. "Queries for objects in this collection.
This function returns clojure Seq of Maps. This function returns clojure Seq of Maps.
If you want to work directly with DBObject, use find." If you want to work directly with DBObject, use find.
([^DB db ^String coll] "
(with-open [dbc (find db coll)] ([^String collection]
(map (fn [x] (from-db-object x true)) dbc))) (map (fn [x] (from-db-object x true)) (find collection)))
([^DB db ^String coll ^Map ref] ([^String collection ^Map ref]
(with-open [dbc (find db coll ref)] (map (fn [x] (from-db-object x true)) (find collection ref)))
(map (fn [x] (from-db-object x true)) dbc))) ([^String collection ^Map ref fields]
([^DB db ^String coll ^Map ref fields] (map (fn [x] (from-db-object x true)) (find collection ref fields)))
(find-maps db coll ref fields true)) ([^DB db ^String collection ^Map ref fields]
([^DB db ^String coll ^Map ref fields keywordize] (map (fn [x] (from-db-object x true)) (find db collection ref fields))))
(with-open [dbc (find db coll ref fields)]
(map (fn [x] (from-db-object x keywordize)) dbc))))
(defn find-seq (defn find-seq
"Queries for objects in this collection, returns ISeq of DBObjects." "Queries for objects in this collection, returns ISeq of DBObjects."
([^DB db ^String coll] ([^String collection]
(with-open [dbc (find db coll)] (seq (find collection)))
(seq dbc))) ([^String collection ^Map ref]
([^DB db ^String coll ^Map ref] (seq (find collection ref)))
(with-open [dbc (find db coll ref)] ([^String collection ^Map ref fields]
(seq dbc))) (seq (find collection ref fields)))
([^DB db ^String coll ^Map ref fields] ([^DB db ^String collection ^Map ref fields]
(with-open [dbc (find db coll ref fields)] (seq (find db collection ref fields))))
(seq dbc))))
;; ;;
;; monger.collection/find-one ;; monger.collection/find-one
;; ;;
(defn ^DBObject find-one (defn ^DBObject find-one
"Returns a single DBObject from this collection matching the query." "Returns a single DBObject from this collection matching the query.
([^DB db ^String coll ^Map ref]
(.findOne (.getCollection db (name coll)) EXAMPLES:
(mgcol/find-one collection {:language \"Clojure\"})
;; Return only :language field.
;; Note that _id field is always returned.
(mgcol/find-one collection {:language \"Clojure\"} [:language])
"
([^String collection ^Map ref]
(.findOne (.getCollection monger.core/*mongodb-database* (name collection))
(to-db-object ref))) (to-db-object ref)))
([^DB db ^String coll ^Map ref fields] ([^String collection ^Map ref fields]
(.findOne (.getCollection db (name coll)) (.findOne (.getCollection monger.core/*mongodb-database* (name collection))
(to-db-object ref)
^DBObject (as-field-selector fields)))
([^DB db ^String collection ^Map ref fields]
(.findOne (.getCollection db (name collection))
(to-db-object ref) (to-db-object ref)
^DBObject (as-field-selector fields)))) ^DBObject (as-field-selector fields))))
(defn ^IPersistentMap find-one-as-map (defn ^IPersistentMap find-one-as-map
"Returns a single object converted to Map from this collection matching the query." "Returns a single object converted to Map from this collection matching the query."
([^DB db ^String coll ^Map ref] ([^String collection ^Map ref]
(from-db-object ^DBObject (find-one db coll ref) true)) (from-db-object ^DBObject (find-one collection ref) true))
([^DB db ^String coll ^Map ref fields] ([^String collection ^Map ref fields]
(from-db-object ^DBObject (find-one db coll ref fields) true)) (from-db-object ^DBObject (find-one collection ref fields) true))
([^DB db ^String coll ^Map ref fields keywordize] ([^String collection ^Map ref fields keywordize]
(from-db-object ^DBObject (find-one db coll ref fields) keywordize))) (from-db-object ^DBObject (find-one collection ref fields) keywordize)))
;; ;;
;; monger.collection/find-and-modify ;; monger.collection/find-and-modify
;; ;;
(defn ^IPersistentMap find-and-modify (defn ^DBObject find-and-modify
"Atomically modify a document (at most one) and return it." "Atomically modify a document (at most one) and return it.
([^DB db ^String coll ^Map conditions ^Map document {:keys [fields sort remove return-new upsert keywordize] :or
{fields nil EXAMPLES:
sort nil
remove false ;; Find and modify a document
return-new false (mgcol/find-and-modify collection {:language \"Python\"} {:language \"Clojure\"})
upsert false
keywordize true}}] ;; If multiple documents match, choose the first one in the specified order
(let [coll (.getCollection db (name coll)) (mgcol/find-and-modify collection {:language \"Python\"} {:language \"Clojure\"} :sort {:language -1})
;; Remove the object before returning
(mgcol/find-and-modify collection {:language \"Python\"} {} :remove true)
;; Return the modified object instead of the old one
(mgcol/find-and-modify collection {:language \"Python\"} {:language \"Clojure\"} :return-new true)
;; Retrieve a subset of fields
(mgcol/find-and-modify collection {:language \"Python\"} {:language \"Clojure\"} :fields [ :language ])
;; Create the object if it doesn't exist
(mgcol/find-and-modify collection {:language \"Factor\"} {:language \"Clojure\"} :upsert true)
"
([^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}}]
(let [coll (.getCollection monger.core/*mongodb-database* (name collection))
maybe-fields (when fields (as-field-selector fields)) maybe-fields (when fields (as-field-selector fields))
maybe-sort (when sort (to-db-object sort))] maybe-sort (when sort (to-db-object sort))]
(from-db-object (from-db-object
@ -214,122 +263,150 @@
;; ;;
(defn ^DBObject find-by-id (defn ^DBObject find-by-id
"Returns a single object with matching _id field." "Returns a single object with matching _id field.
([^DB db ^String coll id]
EXAMPLES:
(mgcol/find-one-by-id collection (ObjectId. \"4ef45ab4744e9fd632640e2d\"))
;; Return only :language field.
;; Note that _id field is always returned.
(mgcol/find-one-by-id collection (ObjectId. \"4ef45ab4744e9fd632640e2d\") [:language])
"
([^String collection id]
(check-not-nil! id "id must not be nil") (check-not-nil! id "id must not be nil")
(find-one db coll {:_id id})) (find-one collection {:_id id}))
([^DB db ^String coll id fields] ([^String collection id fields]
(check-not-nil! id "id must not be nil") (check-not-nil! id "id must not be nil")
(find-one db coll {:_id id} fields))) (find-one collection {:_id id} fields))
([^DB db ^String collection id fields]
(check-not-nil! id "id must not be nil")
(find-one db collection {:_id id} fields)))
(defn ^IPersistentMap find-map-by-id (defn ^IPersistentMap find-map-by-id
"Returns a single object, converted to map with matching _id field." "Returns a single object, converted to map with matching _id field."
([^DB db ^String coll id] ([^String collection id]
(check-not-nil! id "id must not be nil") (check-not-nil! id "id must not be nil")
(find-one-as-map db coll {:_id id})) (from-db-object ^DBObject (find-one-as-map collection {:_id id}) true))
([^DB db ^String coll id fields] ([^String collection id fields]
(check-not-nil! id "id must not be nil") (check-not-nil! id "id must not be nil")
(find-one-as-map db coll {:_id id} fields)) (from-db-object ^DBObject (find-one-as-map collection {:_id id} fields) true))
([^DB db ^String coll id fields keywordize] ([^String collection id fields keywordize]
(check-not-nil! id "id must not be nil") (check-not-nil! id "id must not be nil")
(find-one-as-map db coll {:_id id} fields keywordize))) (from-db-object ^DBObject (find-one-as-map collection {:_id id} fields) keywordize)))
;;
;; monger.collection/group
;;
;; TBD
;; ;;
;; monger.collection/count ;; monger.collection/count
;; ;;
(defn count (defn count
"Returns the number of documents in this collection. "Returns the number of documents in this collection.
Takes optional conditions as an argument." Takes optional conditions as an argument.
(^long [^DB db ^String coll]
(.count (.getCollection db (name coll)))) (monger.collection/count collection)
(^long [^DB db ^String coll ^Map conditions]
(.count (.getCollection db (name coll)) (to-db-object conditions)))) (monger.collection/count collection {:first_name \"Paul\"})"
(^long [^String collection]
(.count (.getCollection monger.core/*mongodb-database* (name collection))))
(^long [^String collection ^Map conditions]
(.count (.getCollection monger.core/*mongodb-database* (name collection)) (to-db-object conditions)))
(^long [^DB db ^String collection ^Map conditions]
(.count (.getCollection db (name collection)) (to-db-object conditions))))
(defn any? (defn any?
"Whether the collection has any items at all, or items matching query." "Wether the collection has any items at all, or items matching query.
([^DB db ^String coll]
(> (count db coll) 0)) EXAMPLES:
([^DB db ^String coll ^Map conditions]
(> (count db coll conditions) 0))) ;; wether the collection has any items
(mgcol/any? collection)
(mgcol/any? collection {:language \"Clojure\"}))
"
([^String collection]
(> (count collection) 0))
([^String collection ^Map conditions]
(> (count collection conditions) 0))
([^DB db ^String collection ^Map conditions]
(> (count db collection conditions) 0)))
(defn empty? (defn empty?
"Whether the collection is empty." "Wether the collection is empty.
[^DB db ^String coll]
(= (count db coll {}) 0)) EXAMPLES:
(mgcol/empty? \"things\")
"
([^String collection]
(= (count collection) 0))
([^DB db ^String collection]
(= (count db collection {}) 0)))
;; monger.collection/update ;; monger.collection/update
(defn ^WriteResult update (defn ^WriteResult update
"Performs an update operation. "Performs an update operation.
Please note that update is potentially destructive operation. It updates document with the given set Please note that update is potentially destructive operation. It will update your document with the given set
emptying the fields not mentioned in the new document. In order to only change certain fields, use emptying the fields not mentioned in (^Map document). In order to only change certain fields, please use
\"$set\". \"$set\".
You can use all the MongoDB modifier operations ($inc, $set, $unset, $push, $pushAll, $addToSet, $pop, $pull EXAMPLES
$pullAll, $rename, $bit) here as well.
(monger.collection/update \"people\" {:first_name \"Raul\"} {\"$set\" {:first_name \"Paul\"}})
You can use all the Mongodb Modifier Operations ($inc, $set, $unset, $push, $pushAll, $addToSet, $pop, $pull
$pullAll, $rename, $bit) here, as well
EXAMPLES
(monger.collection/update \"people\" {:first_name \"Paul\"} {\"$set\" {:index 1}})
(monger.collection/update \"people\" {:first_name \"Paul\"} {\"$inc\" {:index 5}})
(monger.collection/update \"people\" {:first_name \"Paul\"} {\"$unset\" {:years_on_stage 1}})
It also takes modifiers, such as :upsert and :multi.
EXAMPLES
;; add :band field to all the records found in \"people\" collection, otherwise only the first matched record
;; will be updated
(monger.collection/update \"people\" {} {\"$set\" {:band \"The Beatles\"}} :multi true)
;; inserts the record if it did not exist in the collection
(monger.collection/update \"people\" {:first_name \"Yoko\"} {:first_name \"Yoko\" :last_name \"Ono\"} :upsert true)
It also takes options, such as :upsert and :multi.
By default :upsert and :multi are false." By default :upsert and :multi are false."
([^DB db ^String coll ^Map conditions ^Map document] ([^String collection ^Map conditions ^Map document & {:keys [upsert multi write-concern] :or {upsert false
(update db coll conditions document {}))
([^DB db ^String coll ^Map conditions ^Map document {:keys [upsert multi write-concern]
:or {upsert false
multi false multi false
write-concern mc/*mongodb-write-concern*}}] write-concern monger.core/*mongodb-write-concern*}}]
(.update (.getCollection db (name coll)) (.update (.getCollection monger.core/*mongodb-database* (name collection))
(to-db-object conditions) (to-db-object conditions)
(to-db-object document) (to-db-object document)
upsert upsert
multi multi
write-concern))) write-concern)))
(defn ^WriteResult upsert
"Performs an upsert.
This is a convenience function that delegates to monger.collection/update and
sets :upsert to true.
See monger.collection/update documentation"
([^DB db ^String coll ^Map conditions ^Map document]
(upsert db coll conditions document {}))
([^DB db ^String coll ^Map conditions ^Map document {:keys [multi write-concern]
:or {multi false
write-concern mc/*mongodb-write-concern*}}]
(update db coll conditions document {:multi multi :write-concern write-concern :upsert true})))
(defn ^WriteResult update-by-id (defn ^WriteResult update-by-id
"Update a document with given id" "Update a document with given id"
([^DB db ^String coll id ^Map document] [^String collection id ^Map document & {:keys [upsert write-concern] :or {upsert false
(update-by-id db coll id document {})) write-concern monger.core/*mongodb-write-concern*}}]
([^DB db ^String coll id ^Map document {:keys [upsert write-concern]
:or {upsert false
write-concern mc/*mongodb-write-concern*}}]
(check-not-nil! id "id must not be nil") (check-not-nil! id "id must not be nil")
(.update (.getCollection db (name coll)) (.update (.getCollection monger.core/*mongodb-database* (name collection))
(to-db-object {:_id id}) (to-db-object {:_id id})
(to-db-object document) (to-db-object document)
upsert upsert
false false
write-concern))) write-concern))
(defn ^WriteResult update-by-ids
"Update documents by given ids"
([^DB db ^String coll ids ^Map document]
(update-by-ids db coll ids document {}))
([^DB db ^String coll ids ^Map document {:keys [upsert write-concern]
:or {upsert false
write-concern mc/*mongodb-write-concern*}}]
(check-not-nil! (seq ids) "ids must not be nil or empty")
(.update (.getCollection db (name coll))
(to-db-object {:_id {"$in" ids}})
(to-db-object document)
upsert
true
write-concern)))
;; monger.collection/save ;; monger.collection/save
@ -341,13 +418,22 @@
If the object is already in the database, it will be updated. If the object is already in the database, it will be updated.
This function returns write result. If you want to get the exact persisted document back, This function returns write result. If you want to get the exact persisted document back,
use `save-and-return`." use `save-and-return`.
([^DB db ^String coll ^Map document]
(.save (.getCollection db (name coll)) EXAMPLES
(monger.collection/save \"people\" {:first_name \"Ian\" :last_name \"Gillan\"})
"
([^String collection ^Map document]
(.save (.getCollection monger.core/*mongodb-database* (name collection))
(to-db-object document) (to-db-object document)
mc/*mongodb-write-concern*)) monger.core/*mongodb-write-concern*))
([^DB db ^String coll ^Map document ^WriteConcern write-concern] ([^String collection ^Map document ^WriteConcern write-concern]
(.save (.getCollection db (name coll)) (.save (.getCollection monger.core/*mongodb-database* (name collection))
(to-db-object document)
write-concern))
([^DB db ^String collection ^Map document ^WriteConcern write-concern]
(.save (.getCollection db (name collection))
(to-db-object document) (to-db-object document)
write-concern))) write-concern)))
@ -360,51 +446,76 @@
This function returns the exact persisted document back, including the `:_id` key in This function returns the exact persisted document back, including the `:_id` key in
case of an insert. case of an insert.
If you want to get write result back, use `save`." If you want to get write result back, use `save`.
([^DB db ^String coll ^Map document]
(save-and-return db coll document ^WriteConcern mc/*mongodb-write-concern*)) EXAMPLES
([^DB db ^String coll ^Map document ^WriteConcern write-concern]
(monger.collection/save-and-return \"people\" {:first_name \"Ian\" :last_name \"Gillan\"})
"
([^String collection ^Map document]
(save-and-return ^DB monger.core/*mongodb-database* collection document ^WriteConcern monger.core/*mongodb-write-concern*))
([^String collection ^Map document ^WriteConcern write-concern]
(save-and-return ^DB monger.core/*mongodb-database* collection document write-concern))
([^DB db ^String collection ^Map document ^WriteConcern write-concern]
;; see the comment in insert-and-return. Here we additionally need to make sure to not scrap the :_id key if ;; see the comment in insert-and-return. Here we additionally need to make sure to not scrap the :_id key if
;; it is already present. MK. ;; it is already present. MK.
(let [doc (merge {:_id (ObjectId.)} document)] (let [doc (merge {:_id (ObjectId.)} document)]
(save db coll doc write-concern) (save db collection doc write-concern)
doc))) doc)))
;; monger.collection/remove ;; monger.collection/remove
(defn ^WriteResult remove (defn ^WriteResult remove
"Removes objects from the database." "Removes objects from the database.
([^DB db ^String coll]
(.remove (.getCollection db (name coll)) (to-db-object {}))) EXAMPLES
([^DB db ^String coll ^Map conditions]
(.remove (.getCollection db (name coll)) (to-db-object conditions)))) (monger.collection/remove collection) ;; Removes all documents from DB
(monger.collection/remove collection {:language \"Clojure\"}) ;; Removes documents based on given query
"
([^String collection]
(.remove (.getCollection monger.core/*mongodb-database* (name collection)) (to-db-object {})))
([^String collection ^Map conditions]
(.remove (.getCollection monger.core/*mongodb-database* (name collection)) (to-db-object conditions)))
([^DB db ^String collection ^Map conditions]
(.remove (.getCollection db (name collection)) (to-db-object conditions))))
(defn ^WriteResult remove-by-id (defn ^WriteResult remove-by-id
"Removes a single document with given id" "Removes a single document with given id"
[^DB db ^String coll id] ([^String collection id]
(remove-by-id monger.core/*mongodb-database* collection id))
([^DB db ^String collection id]
(check-not-nil! id "id must not be nil") (check-not-nil! id "id must not be nil")
(let [coll (.getCollection db (name coll))] (let [coll (.getCollection db (name collection))]
(.remove coll (to-db-object {:_id id})))) (.remove coll (to-db-object {:_id id})))))
(defn purge-many
"Purges (removes all documents from) multiple collections. Intended
to be used in test environments."
[^DB db xs]
(doseq [coll xs]
(remove db coll)))
;; ;;
;; monger.collection/create-index ;; monger.collection/create-index
;; ;;
(defn create-index (defn create-index
"Forces creation of index on a set of fields, if one does not already exists." "Forces creation of index on a set of fields, if one does not already exists.
([^DB db ^String coll ^Map keys]
(.createIndex (.getCollection db (name coll)) (as-field-selector keys))) EXAMPLES
([^DB db ^String coll ^Map keys ^Map options]
(.createIndex (.getCollection db (name coll)) ;; Will create an index on the \"language\" field
(monger.collection/create-index collection {\"language\" 1})
(monger.collection/create-index collection {\"language\" 1} {:unique true :name \"unique_language\"})
"
([^String collection ^Map keys]
(.createIndex (.getCollection monger.core/*mongodb-database* (name collection)) (as-field-selector keys)))
([^String collection ^Map keys options]
(.createIndex (.getCollection monger.core/*mongodb-database* (name collection))
(as-field-selector keys)
(to-db-object options)))
([^DB db ^String collection ^Map keys ^Map options]
(.createIndex (.getCollection db (name collection))
(as-field-selector keys) (as-field-selector keys)
(to-db-object options)))) (to-db-object options))))
@ -420,17 +531,25 @@
Options are: Options are:
:unique (boolean) to create a unique index :unique (boolean) to create a unique index
:name (string) to specify a custom index name and not rely on the generated one" :name (string) to specify a custom index name and not rely on the generated one
([^DB db ^String coll ^Map keys]
(.createIndex (.getCollection db (name coll)) (as-field-selector keys))) EXAMPLES
([^DB db ^String coll ^Map keys ^Map options]
(.createIndex (.getCollection db (name coll)) ;; create a regular index
(monger.collection/ensure-index \"documents\" {\"language\" 1})
;; create a unique index
(monger.collection/ensure-index \"pages\" {:url 1} {:unique true})
"
([^String collection ^Map keys]
(.ensureIndex (.getCollection monger.core/*mongodb-database* (name collection)) (as-field-selector keys)))
([^String collection ^Map keys ^Map options]
(.ensureIndex (.getCollection monger.core/*mongodb-database* (name collection))
(as-field-selector keys) (as-field-selector keys)
(to-db-object options))) (to-db-object options)))
([^DB db ^String coll ^Map keys ^String index-name unique?] ([^String collection ^Map keys ^String name ^Boolean unique?]
(.createIndex (.getCollection db (name coll)) (.ensureIndex (.getCollection monger.core/*mongodb-database* (name collection))
(as-field-selector keys) (as-field-selector keys)
index-name name
unique?))) unique?)))
@ -439,9 +558,15 @@
;; ;;
(defn indexes-on (defn indexes-on
"Return a list of the indexes for this collection." "Return a list of the indexes for this collection.
[^DB db ^String coll]
(from-db-object (.getIndexInfo (.getCollection db (name coll))) true)) EXAMPLES
(monger.collection/indexes-on collection)
"
[^String collection]
(from-db-object (.getIndexInfo (.getCollection monger.core/*mongodb-database* (name collection))) true))
;; ;;
@ -450,15 +575,17 @@
(defn drop-index (defn drop-index
"Drops an index from this collection." "Drops an index from this collection."
[^DB db ^String coll idx] ([^String collection ^String idx-name]
(if (string? idx) (.dropIndex (.getCollection monger.core/*mongodb-database* (name collection)) idx-name))
(.dropIndex (.getCollection db (name coll)) ^String idx) ([^DB db ^String collection ^String idx-name]
(.dropIndex (.getCollection db (name coll)) (to-db-object idx)))) (.dropIndex (.getCollection db (name collection)) idx-name)))
(defn drop-indexes (defn drop-indexes
"Drops all indixes from this collection." "Drops all indixes from this collection."
[^DB db ^String coll] ([^String collection]
(.dropIndexes (.getCollection db (name coll)))) (.dropIndexes (.getCollection monger.core/*mongodb-database* (name collection))))
([^DB db ^String collection]
(.dropIndexes (.getCollection db (name collection)))))
;; ;;
@ -467,32 +594,49 @@
(defn exists? (defn exists?
"Checks whether collection with certain name exists." "Checks weather collection with certain name exists.
([^DB db ^String coll]
(.collectionExists db coll))) EXAMPLE:
(monger.collection/exists? \"coll\")
"
([^String collection]
(.collectionExists monger.core/*mongodb-database* collection))
([^DB db ^String collection]
(.collectionExists db collection)))
(defn create (defn create
"Creates a collection with a given name and options. "Creates a collection with a given name and options."
([^String collection ^Map options]
Options are: (.createCollection monger.core/*mongodb-database* collection (to-db-object options)))
([^DB db ^String collection ^Map options]
:capped (pass true to create a capped collection) (.createCollection db collection (to-db-object options))))
:max (number of documents)
:size (max allowed size of the collection, in bytes)"
[^DB db ^String coll ^Map options]
(.createCollection db coll (to-db-object options)))
(defn drop (defn drop
"Deletes collection from database." "Deletes collection from database.
[^DB db ^String coll]
(.drop (.getCollection db (name coll)))) EXAMPLE:
(monger.collection/drop \"collection-to-drop\")
"
([^String collection]
(.drop (.getCollection monger.core/*mongodb-database* (name collection))))
([^DB db ^String collection]
(.drop (.getCollection db (name collection)))))
(defn rename (defn rename
"Renames collection." "Renames collection.
([^DB db ^String from, ^String to]
(.rename (.getCollection db (name from)) (name to))) EXAMPLE:
([^DB db ^String from ^String to drop-target?]
(.rename (.getCollection db (name from)) (name to) drop-target?))) (monger.collection/rename \"old_name\" \"new_name\")
"
([^String from, ^String to]
(.rename (.getCollection monger.core/*mongodb-database* from) to))
([^String from ^String to ^Boolean drop-target]
(.rename (.getCollection monger.core/*mongodb-database* from) to drop-target))
([^DB db ^String from ^String to ^Boolean drop-target]
(.rename (.getCollection db from) to drop-target)))
;; ;;
;; Map/Reduce ;; Map/Reduce
@ -500,11 +644,11 @@
(defn map-reduce (defn map-reduce
"Performs a map reduce operation" "Performs a map reduce operation"
([^DB db ^String coll ^String js-mapper ^String js-reducer ^String output ^Map query] ([^String collection ^String js-mapper ^String js-reducer ^String output ^Map query]
(let [coll (.getCollection db (name coll))] (let [coll (.getCollection monger.core/*mongodb-database* (name collection))]
(.mapReduce coll js-mapper js-reducer output (to-db-object query)))) (.mapReduce coll js-mapper js-reducer output (to-db-object query))))
([^DB db ^String coll ^String js-mapper ^String js-reducer ^String output ^MapReduceCommand$OutputType output-type ^Map query] ([^String collection ^String js-mapper ^String js-reducer ^String output ^MapReduceCommand$OutputType output-type ^Map query]
(let [coll (.getCollection db (name coll))] (let [coll (.getCollection monger.core/*mongodb-database* (name collection))]
(.mapReduce coll js-mapper js-reducer output output-type (to-db-object query))))) (.mapReduce coll js-mapper js-reducer output output-type (to-db-object query)))))
@ -514,55 +658,39 @@
(defn distinct (defn distinct
"Finds distinct values for a key" "Finds distinct values for a key"
([^DB db ^String coll ^String key] ([^String collection ^String key]
(.distinct (.getCollection db (name coll)) ^String (to-db-object key))) (.distinct (.getCollection monger.core/*mongodb-database* (name collection)) ^String (to-db-object key)))
([^DB db ^String coll ^String key ^Map query] ([^String collection ^String key ^Map query]
(.distinct (.getCollection db (name coll)) ^String (to-db-object key) (to-db-object query)))) (.distinct (.getCollection monger.core/*mongodb-database* (name collection)) ^String (to-db-object key) (to-db-object query)))
([^DB db ^String collection ^String key ^Map query]
(.distinct (.getCollection db (name collection)) ^String (to-db-object key) (to-db-object query))))
;;
;; create/capped collections
;;
(defn create
"Creates a collection. Options are: :capped (pass true to create a capped collection), :max (number of documents)
and :size (max allowed size of the collection, in bytes)."
[^String collection options]
(.createCollection ^DB monger.core/*mongodb-database* collection (to-db-object options)))
;; ;;
;; Aggregation ;; Aggregation
;; ;;
(defn- build-aggregation-options
^AggregationOptions
[{:keys [^Boolean allow-disk-use cursor ^Long max-time]}]
(cond-> (AggregationOptions/builder)
allow-disk-use (.allowDiskUse allow-disk-use)
cursor (.outputMode AggregationOptions$OutputMode/CURSOR)
max-time (.maxTime max-time TimeUnit/MILLISECONDS)
(:batch-size cursor) (.batchSize (int (:batch-size cursor)))
true .build))
(defn aggregate (defn aggregate
"Executes an aggregation query. MongoDB 2.2+ only. "Performs aggregation query. MongoDB 2.1/2.2+ only.
Accepts the options :allow-disk-use and :cursor (a map with the :batch-size
key), as described in the MongoDB manual. Additionally, the :max-time option
is supported, for specifying a limit on the execution time of the query in
milliseconds.
:keywordize option that control if resulting map keys will be turned into keywords, default is true.
See http://docs.mongodb.org/manual/applications/aggregation/ to learn more." See http://docs.mongodb.org/manual/applications/aggregation/ to learn more."
[^DB db ^String coll stages & opts] [^String collection stages]
(let [coll (.getCollection db (name coll)) (let [res (monger.core/command {:aggregate collection :pipeline stages})]
agg-opts (build-aggregation-options opts) ;; this is what DBCollection#distinct does. Turning a blind eye!
pipe (into-array-list (to-db-object stages)) (.throwOnError res)
res (.aggregate coll pipe agg-opts) (map #(from-db-object % true) (.get res "result"))))
{:keys [^Boolean keywordize]
:or {keywordize true}} opts]
(map #(from-db-object % keywordize) (iterator-seq res))))
(defn explain-aggregate
"Returns the explain plan for an aggregation query. MongoDB 2.2+ only.
See http://docs.mongodb.org/manual/applications/aggregation/ to learn more."
[^DB db ^String coll stages & opts]
(let [coll (.getCollection db (name coll))
agg-opts (build-aggregation-options opts)
pipe (into-array-list (to-db-object stages))
res (.explainAggregate coll pipe agg-opts)]
(from-db-object res true)))
;; ;;
;; Misc ;; Misc
;; ;;
@ -573,5 +701,5 @@
(defn system-collection? (defn system-collection?
"Evaluates to true if the given collection name refers to a system collection. System collections "Evaluates to true if the given collection name refers to a system collection. System collections
are prefixed with system. or fs. (default GridFS collection prefix)" are prefixed with system. or fs. (default GridFS collection prefix)"
[^String coll] [^String collection]
(re-find system-collection-pattern coll)) (re-find system-collection-pattern collection))

View file

@ -1,40 +1,14 @@
;; This source code is dual-licensed under the Apache License, version ;; Copyright (c) 2011-2012 Michael S. Klishin
;; 2.0, and the Eclipse Public License, version 1.0.
;;
;; The APL v2.0:
;;
;; ----------------------------------------------------------------------------------
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team
;; Copyright (c) 2012 Toby Hede ;; Copyright (c) 2012 Toby Hede
;; ;;
;; Licensed under the Apache License, Version 2.0 (the "License"); ;; The use and distribution terms for this software are covered by the
;; you may not use this file except in compliance with the License. ;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
;; You may obtain a copy of the License at ;; which can be found in the file epl-v10.html at the root of this distribution.
;; ;; By using this software in any fashion, you are agreeing to be bound by
;; http://www.apache.org/licenses/LICENSE-2.0 ;; the terms of this license.
;; ;; You must not remove this notice, or any other, from this software.
;; Unless required by applicable law or agreed to in writing, software
;; distributed under the License is distributed on an "AS IS" BASIS,
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;; See the License for the specific language governing permissions and
;; limitations under the License.
;; ----------------------------------------------------------------------------------
;;
;; The EPL v1.0:
;;
;; ----------------------------------------------------------------------------------
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team.
;; Copyright (c) 2012 Toby Hede
;; All rights reserved.
;;
;; This program and the accompanying materials are made available under the terms of
;; the Eclipse Public License Version 1.0,
;; which accompanies this distribution and is available at
;; http://www.eclipse.org/legal/epl-v10.html.
;; ----------------------------------------------------------------------------------
(ns monger.command (ns ^{:doc "Provides convenience functions for performing most commonly used MongoDB commands.
"Provides convenience functions for performing most commonly used MongoDB commands.
For a lower-level API that gives maximum flexibility, see `monger.core/command`. To use For a lower-level API that gives maximum flexibility, see `monger.core/command`. To use
MongoDB 2.2 Aggregation Framework, see `monger.collection/aggregate`. MongoDB 2.2 Aggregation Framework, see `monger.collection/aggregate`.
@ -42,67 +16,70 @@
* http://clojuremongodb.info/articles/commands.html * http://clojuremongodb.info/articles/commands.html
* http://clojuremongodb.info/articles/aggregation.html * http://clojuremongodb.info/articles/aggregation.html
* http://clojuremongodb.info/articles/mapreduce.html" * http://clojuremongodb.info/articles/mapreduce.html"}
(:require monger.core monger.command
[monger.conversion :refer :all]) (:require monger.core)
(:import [com.mongodb MongoClient DB DBObject])) (:use monger.conversion)
(:import com.mongodb.DB))
;;
;; API
;;
(defn admin-command
"Executes a command on the admin database"
[^MongoClient conn m]
(monger.core/command (monger.core/admin-db conn) m))
(defn raw-admin-command
"Executes a command on the admin database"
[^MongoClient conn ^DBObject cmd]
(monger.core/raw-command (monger.core/admin-db conn) cmd))
(defn collection-stats (defn collection-stats
[^DB database collection] ([collection]
(monger.core/command database {:collstats collection})) (collection-stats monger.core/*mongodb-database* collection))
([^DB database collection]
(monger.core/command database { :collstats collection })))
(defn db-stats (defn db-stats
[^DB database] ([]
(monger.core/command database {:dbStats 1})) (db-stats monger.core/*mongodb-database*))
([^DB database]
(monger.core/command database {:dbStats 1 })))
(defn reindex-collection (defn reindex-collection
"Forces an existing collection to be reindexed using the reindexCollection command" "Forces an existing collection to be reindexed using the reindexCollection command"
[^DB database ^String collection] ([^String collection]
(monger.core/command database {:reIndex collection})) (reindex-collection monger.core/*mongodb-database* collection))
([^DB database ^String collection]
(monger.core/command database { :reIndex collection })))
(defn rename-collection (defn rename-collection
"Changes the name of an existing collection using the renameCollection command" "Changes the name of an existing collection using the renameCollection command"
[^DB db ^String from ^String to] ([^String from ^String to]
(monger.core/command db (sorted-map :renameCollection from :to to))) (reindex-collection monger.core/*mongodb-database* from to))
([^DB database ^String from ^String to]
(monger.core/command database { :renameCollection from :to to })))
(defn convert-to-capped (defn convert-to-capped
"Converts an existing, non-capped collection to a capped collection using the convertToCapped command" "Converts an existing, non-capped collection to a capped collection using the convertToCapped command"
[^DB db ^String collection ^long size] ([^String collection ^long size]
(monger.core/command db (sorted-map :convertToCapped collection :size size))) (convert-to-capped monger.core/*mongodb-database* collection size))
([^DB database ^String collection ^long size]
(monger.core/command database {:convertToCapped collection :size size})))
(defn empty-capped (defn empty-capped
"Removes all documents from a capped collection using the emptycapped command" "Removes all documents from a capped collection using the emptycapped command"
[^DB db ^String collection] ([^String collection]
(monger.core/command db {:emptycapped collection})) (empty-capped monger.core/*mongodb-database* collection))
([^DB database ^String collection]
(monger.core/command database {:emptycapped collection})))
(defn compact (defn compact
"Rewrites and defragments a single collection using the compact command. This also forces all indexes on the collection to be rebuilt" "Rewrites and defragments a single collection using the compact command. This also forces all indexes on the collection to be rebuilt"
[^DB db ^String collection] ([^String collection]
(monger.core/command db {:compact collection})) (compact monger.core/*mongodb-database* collection))
([^DB database ^String collection]
(monger.core/command database {:compact collection})))
(defn server-status (defn server-status
[^DB db] ([]
(monger.core/command db {:serverStatus 1})) (server-status monger.core/*mongodb-database*))
([^DB database]
(monger.core/command database {:serverStatus 1 })))
(defn top (defn top
[^MongoClient conn] []
(monger.core/command (monger.core/admin-db conn) {:top 1})) (monger.core/command (monger.core/get-db "admin") {:top 1}))

View file

@ -1,44 +0,0 @@
;; This source code is dual-licensed under the Apache License, version
;; 2.0, and the Eclipse Public License, version 1.0.
;;
;; The APL v2.0:
;;
;; ----------------------------------------------------------------------------------
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team
;;
;; Licensed under the Apache License, Version 2.0 (the "License");
;; you may not use this file except in compliance with the License.
;; You may obtain a copy of the License at
;;
;; http://www.apache.org/licenses/LICENSE-2.0
;;
;; Unless required by applicable law or agreed to in writing, software
;; distributed under the License is distributed on an "AS IS" BASIS,
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;; See the License for the specific language governing permissions and
;; limitations under the License.
;; ----------------------------------------------------------------------------------
;;
;; The EPL v1.0:
;;
;; ----------------------------------------------------------------------------------
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team.
;; All rights reserved.
;;
;; This program and the accompanying materials are made available under the terms of
;; the Eclipse Public License Version 1.0,
;; which accompanies this distribution and is available at
;; http://www.eclipse.org/legal/epl-v10.html.
;; ----------------------------------------------------------------------------------
(ns monger.constraints)
;;
;; API
;;
(definline check-not-nil!
[ref ^String message]
`(when (nil? ~ref)
(throw (IllegalArgumentException. ~message))))

View file

@ -1,40 +1,27 @@
;; This source code is dual-licensed under the Apache License, version ;; Original author is Andrew Boekhoff
;; 2.0, and the Eclipse Public License, version 1.0.
;; ;;
;; The APL v2.0:
;;
;; ----------------------------------------------------------------------------------
;; Portions of the code are Copyright (c) 2009 Andrew Boekhoff ;; Portions of the code are Copyright (c) 2009 Andrew Boekhoff
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team ;; Copyright (c) 2011-2012 Michael S. Klishin
;; ;;
;; Licensed under the Apache License, Version 2.0 (the "License"); ;; Permission is hereby granted, free of charge, to any person obtaining a copy
;; you may not use this file except in compliance with the License. ;; of this software and associated documentation files (the "Software"), to deal
;; You may obtain a copy of the License at ;; in the Software without restriction, including without limitation the rights
;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
;; copies of the Software, and to permit persons to whom the Software is
;; furnished to do so, subject to the following conditions:
;; ;;
;; http://www.apache.org/licenses/LICENSE-2.0 ;; The above copyright notice and this permission notice shall be included in
;; all copies or substantial portions of the Software.
;; ;;
;; Unless required by applicable law or agreed to in writing, software ;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
;; distributed under the License is distributed on an "AS IS" BASIS, ;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
;; See the License for the specific language governing permissions and ;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
;; limitations under the License. ;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
;; ---------------------------------------------------------------------------------- ;; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
;; ;; THE SOFTWARE.
;; The EPL v1.0:
;;
;; ----------------------------------------------------------------------------------
;; Portions of the code are Copyright (c) 2009 Andrew Boekhoff
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team.
;; All rights reserved.
;;
;; This program and the accompanying materials are made available under the terms of
;; the Eclipse Public License Version 1.0,
;; which accompanies this distribution and is available at
;; http://www.eclipse.org/legal/epl-v10.html.
;; ----------------------------------------------------------------------------------
(ns monger.conversion (ns ^{:doc "Provides functions that convert between MongoDB Java driver classes (DBObject, DBList) and Clojure
"Provides functions that convert between MongoDB Java driver classes (DBObject, DBList) and Clojure
data structures (maps, collections). Most of the time, application developers won't need to use these data structures (maps, collections). Most of the time, application developers won't need to use these
functions directly because Monger Query DSL and many other functions convert documents to Clojure sequences and functions directly because Monger Query DSL and many other functions convert documents to Clojure sequences and
maps automatically. However, this namespace is part of the public API and guaranteed to be stable between minor releases. maps automatically. However, this namespace is part of the public API and guaranteed to be stable between minor releases.
@ -42,12 +29,12 @@
Related documentation guides: Related documentation guides:
* http://clojuremongodb.info/articles/inserting.html * http://clojuremongodb.info/articles/inserting.html
* http://clojuremongodb.info/articles/querying.html" * http://clojuremongodb.info/articles/querying.html"}
monger.conversion
(:import [com.mongodb DBObject BasicDBObject BasicDBList DBCursor] (:import [com.mongodb DBObject BasicDBObject BasicDBList DBCursor]
[clojure.lang IPersistentMap Named Keyword Ratio] [clojure.lang IPersistentMap Named Keyword Ratio]
[java.util List Map Date Set] [java.util List Map Date Set]
org.bson.types.ObjectId org.bson.types.ObjectId))
(org.bson.types Decimal128)))
(defprotocol ConvertToDBObject (defprotocol ConvertToDBObject
(^com.mongodb.DBObject to-db-object [input] "Converts given piece of Clojure data to BasicDBObject MongoDB Java driver uses")) (^com.mongodb.DBObject to-db-object [input] "Converts given piece of Clojure data to BasicDBObject MongoDB Java driver uses"))
@ -55,7 +42,7 @@
(extend-protocol ConvertToDBObject (extend-protocol ConvertToDBObject
nil nil
(to-db-object [input] (to-db-object [input]
nil) input)
String String
(to-db-object [^String input] (to-db-object [^String input]
@ -95,8 +82,8 @@
DBObject DBObject
(to-db-object [^DBObject input] input) (to-db-object [^DBObject input] input)
com.mongodb.DBRef com.novemberain.monger.DBRef
(to-db-object [^com.mongodb.DBRef dbref] (to-db-object [^com.novemberain.monger.DBRef dbref]
dbref) dbref)
Object Object
@ -105,44 +92,55 @@
(declare associate-pairs)
(defprotocol ConvertFromDBObject (defprotocol ConvertFromDBObject
(from-db-object [input keywordize] "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 (extend-protocol ConvertFromDBObject
nil nil
(from-db-object [_ _] nil) (from-db-object [input keywordize] input)
Object Object
(from-db-object [input _] input) (from-db-object [input keywordize] input)
Decimal128 Map
(from-db-object [^Decimal128 input _] (from-db-object [^Map input keywordize]
(.bigDecimalValue input)) (associate-pairs (.entrySet input) keywordize))
List List
(from-db-object [^List input keywordize] (from-db-object [^List input keywordize]
(mapv #(from-db-object % keywordize) input)) (vec (map #(from-db-object % keywordize) input)))
BasicDBList BasicDBList
(from-db-object [^BasicDBList input keywordize] (from-db-object [^BasicDBList input keywordize]
(mapv #(from-db-object % keywordize) input)) (vec (map #(from-db-object % keywordize) input)))
com.mongodb.DBRef com.mongodb.DBRef
(from-db-object [^com.mongodb.DBRef input _] (from-db-object [^com.mongodb.DBRef input keywordize]
input) (com.novemberain.monger.DBRef. input))
DBObject DBObject
(from-db-object [^DBObject input keywordize] (from-db-object [^DBObject input keywordize]
;; DBObject provides .toMap, but the implementation in ;; DBObject provides .toMap, but the implementation in
;; subclass GridFSFile unhelpfully throws ;; subclass GridFSFile unhelpfully throws
;; UnsupportedOperationException. ;; UnsupportedOperationException. This part is taken from congomongo and
(persistent! ;; may need revisiting at a later point. MK.
(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. Taken from congomongo. MK.
(reduce (if keywordize (reduce (if keywordize
(fn [m ^String k] (fn [m [^String k v]]
(assoc! m (keyword k) (from-db-object (.get input k) true))) (assoc m (keyword k) (from-db-object v true)))
(fn [m ^String k] (fn [m [^String k v]]
(assoc! m k (from-db-object (.get input k) false)))) (assoc m k (from-db-object v false))))
(transient {}) (.keySet input))))) {} (reverse pairs)))
(defprotocol ConvertToObjectId (defprotocol ConvertToObjectId

View file

@ -1,38 +1,14 @@
;; This source code is dual-licensed under the Apache License, version ;; Copyright (c) 2011-2012 Michael S. Klishin
;; 2.0, and the Eclipse Public License, version 1.0.
;; ;;
;; The APL v2.0: ;; The use and distribution terms for this software are covered by the
;; ;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
;; ---------------------------------------------------------------------------------- ;; which can be found in the file epl-v10.html at the root of this distribution.
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team ;; By using this software in any fashion, you are agreeing to be bound by
;; ;; the terms of this license.
;; Licensed under the Apache License, Version 2.0 (the "License"); ;; You must not remove this notice, or any other, from this software.
;; you may not use this file except in compliance with the License.
;; You may obtain a copy of the License at
;;
;; http://www.apache.org/licenses/LICENSE-2.0
;;
;; Unless required by applicable law or agreed to in writing, software
;; distributed under the License is distributed on an "AS IS" BASIS,
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;; See the License for the specific language governing permissions and
;; limitations under the License.
;; ----------------------------------------------------------------------------------
;;
;; The EPL v1.0:
;;
;; ----------------------------------------------------------------------------------
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team.
;; All rights reserved.
;;
;; This program and the accompanying materials are made available under the terms of
;; the Eclipse Public License Version 1.0,
;; which accompanies this distribution and is available at
;; http://www.eclipse.org/legal/epl-v10.html.
;; ----------------------------------------------------------------------------------
(ns monger.core (ns ^{:author "Michael S. Klishin"
"Thin idiomatic wrapper around MongoDB Java client. monger.core includes :doc "Thin idiomatic wrapper around MongoDB Java client. monger.core includes
fundamental functions that perform database/replica set connection, set default write concern, default database, performing commands fundamental functions that perform database/replica set connection, set default write concern, default database, performing commands
and so on. Most of the functionality is in other monger.* namespaces, in particular monger.collection, monger.query and monger.gridfs and so on. Most of the functionality is in other monger.* namespaces, in particular monger.collection, monger.query and monger.gridfs
@ -40,14 +16,13 @@
* http://clojuremongodb.info/articles/connecting.html * http://clojuremongodb.info/articles/connecting.html
* http://clojuremongodb.info/articles/commands.html * http://clojuremongodb.info/articles/commands.html
* http://clojuremongodb.info/articles/gridfs.html" * http://clojuremongodb.info/articles/gridfs.html"}
monger.core
(:refer-clojure :exclude [count]) (:refer-clojure :exclude [count])
(:require [monger.conversion :refer :all] (:use [monger.conversion])
[monger.util :refer [into-array-list]]) (:import [com.mongodb Mongo MongoURI DB WriteConcern DBObject DBCursor Bytes MongoOptions ServerAddress MapReduceOutput]
(:import [com.mongodb MongoClient MongoClientURI MongoCredential DB WriteConcern DBObject DBCursor Bytes
MongoClientOptions MongoClientOptions$Builder ServerAddress MapReduceOutput MongoException]
[com.mongodb.gridfs GridFS] [com.mongodb.gridfs GridFS]
[java.util Map])) [java.util Map ArrayList]))
;; ;;
;; Defaults ;; Defaults
@ -56,78 +31,103 @@
(def ^:dynamic ^String *mongodb-host* "127.0.0.1") (def ^:dynamic ^String *mongodb-host* "127.0.0.1")
(def ^:dynamic ^long *mongodb-port* 27017) (def ^:dynamic ^long *mongodb-port* 27017)
(def ^:dynamic ^WriteConcern *mongodb-write-concern* WriteConcern/ACKNOWLEDGED) (declare ^:dynamic ^Mongo *mongodb-connection*)
(declare ^:dynamic ^DB *mongodb-database*)
(def ^:dynamic ^WriteConcern *mongodb-write-concern* WriteConcern/SAFE)
(declare ^:dynamic ^GridFS *mongodb-gridfs*)
;; ;;
;; API ;; API
;; ;;
(defn ^MongoClient connect (defn ^com.mongodb.Mongo connect
"Connects to MongoDB. When used without arguments, connects to "Connects to MongoDB. When used without arguments, connects to
Arguments: Arguments:
:host (\"127.0.0.1\" by default) :host (*mongodb-host* by default)
:port (27017 by default)" :port (*mongodb-port* by default)
EXAMPLES
(monger.core/connect)
(monger.core/connect { :host \"db3.intranet.local\", :port 27787 })
;; Connecting to a replica set with a couple of seeds
(let [^MongoOptions opts (mg/mongo-options :threads-allowed-to-block-for-connection-multiplier 300)
seeds [[\"192.168.1.1\" 27017] [\"192.168.1.2\" 27017] [\"192.168.1.1\" 27018]]
sas (map #(apply mg/server-address %) seeds)]
(mg/connect! sas opts))
"
{:arglists '([] {:arglists '([]
[server-address options] [server-address options]
[server-address options credentials]
[[server-address & more] options] [[server-address & more] options]
[{:keys [host port uri] :or { host *mongodb-host* port *mongodb-port*}}])} [{ :keys [host port uri] :or { host *mongodb-host* port *mongodb-port* }}])}
([] ([]
(MongoClient.)) (Mongo.))
([server-address ^MongoClientOptions options] ([server-address ^MongoOptions options]
(if (coll? server-address) (if (coll? server-address)
;; connect to a replica set ;; connect to a replica set
(let [server-list (into-array-list server-address)] (let [server-list ^ArrayList (ArrayList. ^java.util.Collection server-address)]
(MongoClient. server-list options)) (Mongo. server-list options))
;; connect to a single instance ;; connect to a single instance
(MongoClient. ^ServerAddress server-address options))) (Mongo. ^ServerAddress server-address options)))
([server-address ^MongoClientOptions options credentials]
(let [creds (into-array-list (if (coll? credentials)
credentials
[credentials]))]
(if (coll? server-address)
(let [server-list (into-array-list server-address)]
(MongoClient. server-list ^java.util.List creds options))
(MongoClient. ^ServerAddress server-address ^java.util.List creds options))))
([{ :keys [host port uri] :or { host *mongodb-host* port *mongodb-port* }}] ([{ :keys [host port uri] :or { host *mongodb-host* port *mongodb-port* }}]
(if uri (Mongo. ^String host ^Long port)))
(MongoClient. (MongoClientURI. uri))
(MongoClient. ^String host ^Long port))))
(defn ^MongoClient connect-with-credentials
"Connect with provided credentials and default options"
([credentials]
(connect-with-credentials *mongodb-host* *mongodb-port* credentials))
([^String hostname credentials]
(connect-with-credentials hostname *mongodb-port* credentials))
([^String hostname ^long port credentials]
(MongoClient. (into-array-list [(ServerAddress. hostname port)])
(into-array-list (if (coll? credentials)
credentials
[credentials])))))
(defn get-db-names (defn get-db-names
"Gets a list of all database names present on the server" "Gets a list of all database names present on the server"
[^MongoClient conn] ([]
(set (.getDatabaseNames conn))) (get-db-names *mongodb-connection*))
([^Mongo connection]
(set (.getDatabaseNames connection))))
(defn ^DB get-db (defn ^com.mongodb.DB get-db
"Get database reference by name." "Get database reference by name.
[^MongoClient conn ^String name]
(.getDB conn name))
(defn drop-db EXAMPLES
"Drops a database"
[^MongoClient conn ^String db] (monger.core/get-db \"myapp_production\")
(.dropDatabase conn db)) (monger.core/get-db connection \"myapp_production\")"
([]
*mongodb-database*)
([^String name]
(.getDB *mongodb-connection* name))
([^Mongo connection ^String name]
(.getDB connection name)))
(defn ^com.mongodb.DB current-db
"Returns currently used database"
[]
*mongodb-database*)
(defn authenticate
([^String db ^String username ^chars password]
(authenticate *mongodb-connection* db username password))
([^Mongo connection ^String db ^String username ^chars password]
(.authenticate (.getDB connection db) username password)))
(defmacro with-connection
[conn & body]
`(binding [*mongodb-connection* ~conn]
(do ~@body)))
(defmacro with-db
[db & body]
`(binding [*mongodb-database* ~db]
(do ~@body)))
(defmacro with-gridfs
[fs & body]
`(binding [*mongodb-gridfs* ~fs]
(do ~@body)))
(defn ^GridFS get-gridfs
"Get GridFS for the given database."
[^MongoClient conn ^String name]
(GridFS. (.getDB conn name)))
(defn server-address (defn server-address
([^String hostname] ([^String hostname]
@ -135,165 +135,158 @@
([^String hostname ^Long port] ([^String hostname ^Long port]
(ServerAddress. hostname port))) (ServerAddress. hostname port)))
(defn ^MongoClientOptions$Builder mongo-options-builder
[{:keys [add-cluster-listener add-cluster-listeners add-command-listener add-command-listeners (defn mongo-options
add-connection-pool-listener add-connection-pool-listeners add-server-listener add-server-listeners [& { :keys [connections-per-host threads-allowed-to-block-for-connection-multiplier
add-server-monitor-listener add-server-monitor-listeners always-use-mbeans application-name max-wait-time connect-timeout socket-timeout socket-keep-alive auto-connect-retry max-auto-connect-retry-time
codec-registry compressor-list connect-timeout connections-per-host cursor-finalizer-enabled safe w w-timeout fsync j] :or [auto-connect-retry true] }]
db-decoder-factory db-encoder-factory description heartbeat-connect-timeout heartbeat-frequency (let [mo (MongoOptions.)]
heartbeat-socket-timeout local-threshold max-connection-idle-time max-connection-life-time
max-wait-time min-connections-per-host min-heartbeat-frequency read-concern read-preference
required-replica-set-name retry-writes server-selection-timeout server-selector socket-keep-alive
socket-factory socket-timeout ssl-context ssl-enabled ssl-invalid-host-name-allowed
threads-allowed-to-block-for-connection-multiplier uuid-representation write-concern]}]
(let [mob (MongoClientOptions$Builder.)]
(when add-cluster-listener
(.addClusterListener mob add-cluster-listener))
(when add-cluster-listeners
(doseq [cluster-listener add-cluster-listeners]
(.addClusterListener mob cluster-listener)))
(when add-command-listener
(.addCommandListener mob add-command-listener))
(when add-command-listeners
(doseq [command-listener add-command-listeners]
(.addCommandListener mob command-listener)))
(when add-connection-pool-listener
(.addConnectionPoolListener mob add-connection-pool-listener))
(when add-connection-pool-listeners
(doseq [connection-pool-listener add-connection-pool-listeners]
(.addConnectionPoolListener mob connection-pool-listener)))
(when add-server-listener
(.addServerListener mob add-server-listener))
(when add-server-listeners
(doseq [server-listener add-server-listeners]
(.addServerListener mob server-listener)))
(when add-server-monitor-listener
(.addServerMonitorListener mob add-server-monitor-listener))
(when add-server-monitor-listeners
(doseq [server-monitor-listener add-server-monitor-listeners]
(.addServerMonitorListener mob server-monitor-listener)))
(when always-use-mbeans
(.alwaysUseMBeans mob always-use-mbeans))
(when application-name
(.applicationName mob application-name))
(when always-use-mbeans
(.alwaysUseMBeans mob always-use-mbeans))
(when codec-registry
(.codecRegistry mob codec-registry))
(when compressor-list
(.compressorList mob compressor-list))
(when connections-per-host (when connections-per-host
(.connectionsPerHost mob connections-per-host)) (set! (. mo connectionsPerHost) connections-per-host))
(when connect-timeout
(.connectTimeout mob connect-timeout))
(when cursor-finalizer-enabled
(.cursorFinalizerEnabled mob cursor-finalizer-enabled))
(when db-decoder-factory
(.dbDecoderFactory mob db-decoder-factory))
(when db-encoder-factory
(.dbEncoderFactory mob db-encoder-factory))
(when description
(.description mob description))
(when heartbeat-connect-timeout
(.heartbeatConnectTimeout mob heartbeat-connect-timeout))
(when heartbeat-frequency
(.heartbeatFrequency mob heartbeat-frequency))
(when heartbeat-socket-timeout
(.heartbeatSocketTimeout mob heartbeat-socket-timeout))
(when ssl-context
(.sslContext mob ssl-context))
(when local-threshold
(.localThreshold mob local-threshold))
(when max-connection-idle-time
(.maxConnectionIdleTime mob max-connection-idle-time))
(when max-wait-time
(.maxWaitTime mob max-wait-time))
(when max-connection-life-time
(.maxConnectionLifeTime mob max-connection-life-time))
(when min-connections-per-host
(.minConnectionsPerHost mob min-connections-per-host))
(when min-heartbeat-frequency
(.minHeartbeatFrequency mob min-heartbeat-frequency))
(when read-concern
(.readConcern mob read-concern))
(when read-preference
(.readPreference mob read-preference))
(when required-replica-set-name
(.requiredReplicaSetName mob required-replica-set-name))
(when retry-writes
(.retryWrites mob retry-writes))
(when server-selection-timeout
(.serverSelectionTimeout mob server-selection-timeout))
(when server-selector
(.serverSelector mob server-selector))
(when socket-keep-alive
(.socketKeepAlive mob socket-keep-alive))
(when socket-factory
(.socketFactory mob socket-factory))
(when socket-timeout
(.socketTimeout mob socket-timeout))
(when ssl-enabled
(.sslEnabled mob ssl-enabled))
(when ssl-invalid-host-name-allowed
(.sslInvalidHostNameAllowed mob ssl-invalid-host-name-allowed))
(when threads-allowed-to-block-for-connection-multiplier (when threads-allowed-to-block-for-connection-multiplier
(.threadsAllowedToBlockForConnectionMultiplier mob threads-allowed-to-block-for-connection-multiplier)) (set! (. mo threadsAllowedToBlockForConnectionMultiplier) threads-allowed-to-block-for-connection-multiplier))
(when uuid-representation (when max-wait-time
(.uuidRepresentation mob uuid-representation)) (set! (. mo maxWaitTime) max-wait-time))
(when write-concern (when connect-timeout
(.writeConcern mob write-concern)) (set! (. mo connectTimeout) connect-timeout))
mob)) (when socket-timeout
(set! (. mo socketTimeout) socket-timeout))
(when socket-keep-alive
(set! (. mo socketKeepAlive) socket-keep-alive))
(when auto-connect-retry
(set! (. mo autoConnectRetry) auto-connect-retry))
(when max-auto-connect-retry-time
(set! (. mo maxAutoConnectRetryTime) max-auto-connect-retry-time))
(when safe
(set! (. mo safe) safe))
(when w
(set! (. mo w) w))
(when w-timeout
(set! (. mo wtimeout) w-timeout))
(when j
(set! (. mo j) j))
(when fsync
(set! (. mo fsync) fsync))
mo))
(defn ^MongoClientOptions mongo-options
[opts]
(let [mob (mongo-options-builder opts)]
(.build mob)))
(defn disconnect (defn set-connection!
"Closes default connection to MongoDB" "Sets given MongoDB connection as default by altering *mongodb-connection* var"
[^MongoClient conn] ^Mongo [^Mongo conn]
(.close conn)) (alter-var-root (var *mongodb-connection*) (constantly conn)))
(def ^:const admin-db-name "admin") (defn connect!
"Connect to MongoDB, store connection in the *mongodb-connection* var"
^Mongo [& args]
(let [c (apply connect args)]
(set-connection! c)))
(defn ^DB admin-db
"Returns admin database"
[^MongoClient conn] (defn set-db!
(get-db conn admin-db-name)) "Sets *mongodb-database* var to given db, updates *mongodb-gridfs* var state. Recommended to be used for
applications that only use one database."
[db]
(alter-var-root (var *mongodb-database*) (constantly db))
(alter-var-root (var *mongodb-gridfs*) (constantly (GridFS. db))))
(def ^{:doc "Combines set-db! and get-db, so (use-db \"mydb\") is the same as (set-db! (get-db \"mydb\"))"}
use-db! (comp set-db! get-db))
(defn set-default-write-concern! (defn set-default-write-concern!
[wc] [wc]
"Sets *mongodb-write-concert*" "Set *mongodb-write-concert* var to :wc
Unlike the official Java driver, Monger uses WriteConcern/SAFE by default. We think defaults should be safe first
and WebScale fast second."
(alter-var-root #'*mongodb-write-concern* (constantly wc))) (alter-var-root #'*mongodb-write-concern* (constantly wc)))
(defn connect-via-uri (defn connect-via-uri!
"Connects to MongoDB using a URI, returns the connection and database as a map with :conn and :db. "Connects to MongoDB using a URI, sets up default connection and database. Commonly used for PaaS-based applications,
Commonly used for PaaS-based applications, for example, running on Heroku. for example, running on Heroku. If username and password are provided, performs authentication."
If username and password are provided, performs authentication." [uri]
[^String uri-string] (let [uri (MongoURI. uri)
(let [uri (MongoClientURI. uri-string) ;; yes, you are not hallucinating. A class named MongoURI has a method called connectDB.
conn (MongoClient. uri)] ;; I call it "college OOP". Or maybe "don't give a shit" OOP.
(if-let [dbName (.getDatabase uri)] db (.connectDB uri)
{:conn conn :db (.getDB conn dbName)} conn (.getMongo db)
(throw (IllegalArgumentException. "No database name specified in URI. Monger requires a database to be explicitly configured."))))) user (.getUsername uri)
pwd (.getPassword uri)]
;; I hope that whoever wrote the MongoDB Java driver connection/authentication parts
;; wasn't sober while at it. MK.
;;
;; First we set connection, then DB, then authentcate
(set-connection! conn)
(when (and user pwd)
(when-not (authenticate (.getName db) user pwd)
(throw (IllegalArgumentException. (format "Could not authenticate with MongoDB. Either database name or credentials are invalid. Database name: %s, username: %s" (.getName db) user)))))
;; only do this *after* we authenticated because set-db! will try to set up a default GridFS instance. MK.
(when db
(set-db! db))
conn))
(defn ^com.mongodb.CommandResult command (defn ^com.mongodb.CommandResult command
"Runs a database command (please check MongoDB documentation for the complete list of commands). "Runs a database command (please check MongoDB documentation for the complete list of commands). Some common commands
are:
Ordering of keys in the command document may matter. Please use sorted maps instead of map literals, for example: { :buildinfo 1 } returns version number and build information about the current MongoDB server, should be executed via admin DB.
(array-map :near 50 :test 430 :num 10)
For commonly used commands (distinct, count, map/reduce, etc), use monger.command and monger.collection functions such as { :collstats collection-name [ :scale scale ] } returns stats about given collection.
/distinct, /count, /drop, /dropIndexes, and /mapReduce respectively."
[^DB database ^Map cmd]
(.command ^DB database ^DBObject (to-db-object cmd)))
(defn ^com.mongodb.CommandResult raw-command { :dbStats 1 } returns the stats of current database
"Like monger.core/command but accepts DBObjects"
[^DB database ^DBObject cmd] { :dropDatabase 1 } deletes the current database
(.command database cmd))
{ :findAndModify find-and-modify-config } runs find, modify and return for the given query.
Takes :query, :sory, :remove, :update, :new, :fields and :upsert arguments.
Please refer MongoDB documentation for details. http://www.mongodb.org/display/DOCS/findAndModify+Command
{ :fsync config } performs a full fsync, that flushes all pending writes to database, provides an optional write lock that will make
backups easier.
Please refer MongoDB documentation for details :http://www.mongodb.org/display/DOCS/fsync+Command
{ :getLastError 1 } returns the status of the last operation on current connection.
{ :group group-config } performs grouping aggregation, docs and support for grouping are TBD in Monger.
{ :listCommands 1 } displays the list of available commands.
{ :profile new-profile-level } sets the database profiler to profile level N.
{ :reIndex coll } performs re-index on a given collection.
{ :renameCollection old-name :to new-name } renames collection from old-name to new-name
{ :repairDatabase 1 } repair and compact the current database (may be very time-consuming, depending on DB size)
Replica set commands
{ :isMaster 1 } checks if this server is a master server.
{ :replSetGetStatus 1 } get the status of a replica set.
{ :replSetInitiate replica-config } initiate a replica set with given config.
{ :replSetReconfig replica-config } set a given config for replica set.
{ :replSetStepDown seconds } manually tell a member to step down as primary. It will become primary again after specified amount of seconds.
{ :replSetFreeze seconds } freeze state of member, call with 0 to unfreeze.
{ :resync 1 } start a full resync of a replica slave
For more information, please refer Mongodb Replica Set Command guide: http://www.mongodb.org/display/DOCS/Replica+Set+Commands
{ :serverStatus 1 } gets administrative statistics about the server.
{ :shutdown 1 } shuts the MongoDB server down.
{ :top 1 } get a breakdown of usage by collection.
{ :validate namespace-name } validate the namespace (collection or index). May be very time-consuming, depending on DB size.
For :distinct, :count, :drop, :dropIndexes, :mapReduce we suggest to use monger/collection #distinct, #count, #drop, #dropIndexes, :mapReduce respectively.
"
([^Map cmd]
(.command ^DB *mongodb-database* ^DBObject (to-db-object cmd)))
([^DB database ^Map cmd]
(.command ^DB database ^DBObject (to-db-object cmd))))
(defprotocol Countable (defprotocol Countable
(count [this] "Returns size of the object")) (count [this] "Returns size of the object"))
@ -308,3 +301,23 @@
;; MongoDB Java driver could use a lot more specific type than Iterable but ;; MongoDB Java driver could use a lot more specific type than Iterable but
;; it always uses DBCollection#find to popular result set. MK. ;; it always uses DBCollection#find to popular result set. MK.
(.count ^DBCursor (.results this)))) (.count ^DBCursor (.results this))))
(defn ^DBObject get-last-error
"Returns the the error (if there is one) from the previous operation on this connection.
The result of this command looks like:
#<CommandResult { \"serverUsed\" : \"127.0.0.1:27017\" , \"n\" : 0 , \"connectionId\" : 66 , \"err\" : null , \"ok\" : 1.0}>\"
The value for err will be null if no error occurred, or a description otherwise.
Important note: when calling this method directly, it is undefined which connection \"getLastError\" is called on.
You may need to explicitly use a \"consistent Request\", see requestStart() For most purposes it is better not to call this method directly but instead use WriteConcern."
([]
(get-last-error *mongodb-database*))
([^DB database]
(.getLastError ^DB database))
([^DB database ^Integer w ^Integer wtimeout ^Boolean fsync]
(.getLastError ^DB database w wtimeout fsync))
([^DB database ^WriteConcern write-concern]
(.getLastError ^DB database write-concern)))

View file

@ -1,56 +0,0 @@
;; This source code is dual-licensed under the Apache License, version
;; 2.0, and the Eclipse Public License, version 1.0.
;;
;; The APL v2.0:
;;
;; ----------------------------------------------------------------------------------
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team
;;
;; Licensed under the Apache License, Version 2.0 (the "License");
;; you may not use this file except in compliance with the License.
;; You may obtain a copy of the License at
;;
;; http://www.apache.org/licenses/LICENSE-2.0
;;
;; Unless required by applicable law or agreed to in writing, software
;; distributed under the License is distributed on an "AS IS" BASIS,
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;; See the License for the specific language governing permissions and
;; limitations under the License.
;; ----------------------------------------------------------------------------------
;;
;; The EPL v1.0:
;;
;; ----------------------------------------------------------------------------------
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team.
;; All rights reserved.
;;
;; This program and the accompanying materials are made available under the terms of
;; the Eclipse Public License Version 1.0,
;; which accompanies this distribution and is available at
;; http://www.eclipse.org/legal/epl-v10.html.
;; ----------------------------------------------------------------------------------
(ns monger.credentials
"Helper functions for instantiating various types
of credentials."
(:require [clojurewerkz.support.chars :refer :all])
(:import [com.mongodb MongoCredential]))
;;
;; API
;;
(defn ^MongoCredential create
"Creates a MongoCredential instance with an unspecified mechanism.
The client will negotiate the best mechanism based on the
version of the server that the client is authenticating to."
[^String username ^String database pwd]
(MongoCredential/createCredential username database (to-char-array pwd)))
(defn ^MongoCredential x509
"Creates a MongoCredential instance for the X509-based authentication
protocol."
[^String username]
(MongoCredential/createMongoX509Credential username))

View file

@ -1,143 +0,0 @@
;; This source code is dual-licensed under the Apache License, version
;; 2.0, and the Eclipse Public License, version 1.0.
;;
;; The APL v2.0:
;;
;; ----------------------------------------------------------------------------------
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team
;;
;; Licensed under the Apache License, Version 2.0 (the "License");
;; you may not use this file except in compliance with the License.
;; You may obtain a copy of the License at
;;
;; http://www.apache.org/licenses/LICENSE-2.0
;;
;; Unless required by applicable law or agreed to in writing, software
;; distributed under the License is distributed on an "AS IS" BASIS,
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;; See the License for the specific language governing permissions and
;; limitations under the License.
;; ----------------------------------------------------------------------------------
;;
;; The EPL v1.0:
;;
;; ----------------------------------------------------------------------------------
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team.
;; All rights reserved.
;;
;; This program and the accompanying materials are made available under the terms of
;; the Eclipse Public License Version 1.0,
;; which accompanies this distribution and is available at
;; http://www.eclipse.org/legal/epl-v10.html.
;; ----------------------------------------------------------------------------------
(ns monger.cursor
"Helper-functions for dbCursor object:
* to initialize new cursor,
* for CRUD functionality of options of dbCursor"
(:import [com.mongodb DB DBCursor Bytes]
[java.util List Map]
[java.lang Integer]
[clojure.lang Keyword])
(:require [monger.core]
[monger.conversion :refer [to-db-object from-db-object as-field-selector]]))
(defn ^DBCursor make-db-cursor
"initializes new db-cursor."
([^DB db ^String coll]
(make-db-cursor db coll {} {}))
([^DB db ^String coll ^Map ref]
(make-db-cursor db coll ref {}))
([^DB db ^String coll ^Map ref fields]
(.find
(.getCollection db (name coll))
(to-db-object ref)
(as-field-selector fields))))
(def cursor-options {:awaitdata Bytes/QUERYOPTION_AWAITDATA
;;:exhaust Bytes/QUERYOPTION_EXHAUST - not human settable
:notimeout Bytes/QUERYOPTION_NOTIMEOUT
:oplogreplay Bytes/QUERYOPTION_OPLOGREPLAY
:partial Bytes/QUERYOPTION_PARTIAL
:slaveok Bytes/QUERYOPTION_SLAVEOK
:tailable Bytes/QUERYOPTION_TAILABLE})
(defn get-options
"Returns map of cursor's options with current state."
[^DBCursor db-cur]
(into {}
(for [[opt option-mask] cursor-options]
[opt (< 0 (bit-and (.getOptions db-cur) option-mask))])))
(defn add-option!
[^DBCursor db-cur ^String opt]
(.addOption db-cur (get cursor-options (keyword opt) 0)))
(defn remove-option!
[^DBCursor db-cur ^String opt]
(.setOptions db-cur (bit-and-not (.getOptions db-cur)
(get cursor-options (keyword opt) 0))))
(defmulti add-options (fn [db-cur opts] (class opts)))
(defmethod add-options Map [^DBCursor db-cur options]
"Changes options by using map of settings, which key specifies name of settings
and boolean value specifies new state of the setting.
usage:
(add-options db-cur {:notimeout true, :tailable false})
returns:
^DBCursor object."
(doseq [[opt value] (seq options)]
(if (= true value)
(add-option! db-cur opt)
(remove-option! db-cur opt)))
db-cur)
(defmethod add-options List [^DBCursor db-cur options]
"Takes list of options and activates these options
usage:
(add-options db-cur [:notimeout :tailable])
returns:
^DBCursor object"
(doseq [opt (seq options)]
(add-option! db-cur opt))
db-cur)
(defmethod add-options Integer [^DBCursor db-cur, option]
"Takes com.mongodb.Byte value and adds it to current settings.
usage:
(add-options db-cur com.mongodb.Bytes/QUERYOPTION_NOTIMEOUT)
returns:
^DBCursor object"
(.addOption db-cur option)
db-cur)
(defmethod add-options Keyword [^DBCursor db-cur, option]
"Takes just one keyword as name of settings and applies it to the db-cursor.
usage:
(add-options db-cur :notimeout)
returns:
^DBCursor object"
(add-option! db-cur option)
db-cur)
(defmethod add-options :default [^DBCursor db-cur, options]
"Using add-options with not supported type of options just passes unchanged cursor"
db-cur)
(defn ^DBCursor reset-options
"Resets cursor options to default value and returns cursor"
[^DBCursor db-cur]
(.resetOptions db-cur)
db-cur)
(defmulti format-as (fn [db-cur as] as))
(defmethod format-as :map [db-cur as]
(map #(from-db-object %1 true) db-cur))
(defmethod format-as :seq [db-cur as]
(seq db-cur))
(defmethod format-as :default [db-cur as]
db-cur)

View file

@ -1,62 +1,39 @@
;; This source code is dual-licensed under the Apache License, version ;; Copyright (c) 2011-2012 Michael S. Klishin
;; 2.0, and the Eclipse Public License, version 1.0.
;;
;; The APL v2.0:
;;
;; ----------------------------------------------------------------------------------
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team
;; Copyright (c) 2012 Toby Hede ;; Copyright (c) 2012 Toby Hede
;; ;;
;; Licensed under the Apache License, Version 2.0 (the "License"); ;; The use and distribution terms for this software are covered by the
;; you may not use this file except in compliance with the License. ;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
;; You may obtain a copy of the License at ;; which can be found in the file epl-v10.html at the root of this distribution.
;; ;; By using this software in any fashion, you are agreeing to be bound by
;; http://www.apache.org/licenses/LICENSE-2.0 ;; the terms of this license.
;; ;; You must not remove this notice, or any other, from this software.
;; Unless required by applicable law or agreed to in writing, software
;; distributed under the License is distributed on an "AS IS" BASIS,
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;; See the License for the specific language governing permissions and
;; limitations under the License.
;; ----------------------------------------------------------------------------------
;;
;; The EPL v1.0:
;;
;; ----------------------------------------------------------------------------------
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team.
;; Copyright (c) 2012 Toby Hede
;; All rights reserved.
;;
;; This program and the accompanying materials are made available under the terms of
;; the Eclipse Public License Version 1.0,
;; which accompanies this distribution and is available at
;; http://www.eclipse.org/legal/epl-v10.html.
;; ----------------------------------------------------------------------------------
(ns monger.db (ns monger.db
"Functions that provide operations on databases"
(:refer-clojure :exclude [find remove count drop distinct empty?]) (:refer-clojure :exclude [find remove count drop distinct empty?])
(:import [com.mongodb Mongo DB DBCollection]) (:import [com.mongodb Mongo DB DBCollection])
(:require monger.core (:require monger.core)
[monger.conversion :refer :all])) (:use monger.conversion))
;;
;; API
;;
(defn add-user (defn add-user
"Adds a new user for this db" "Adds a new user for this db"
[^DB db ^String username ^chars password] ([^String username, ^chars password]
(.addUser db username password)) (.addUser ^DB monger.core/*mongodb-database* username password))
([^DB database ^String username ^chars password]
(.addUser ^DB database username password)))
(defn drop-db (defn drop-db
"Drops the currently set database (via core/set-db) or the specified database." "Drops the currently set database (via core/set-db) or the specified database."
[^DB db] ([]
(.dropDatabase db)) (.dropDatabase ^DB monger.core/*mongodb-database*))
([^DB database]
(.dropDatabase ^DB database)))
(defn get-collection-names (defn get-collection-names
"Returns a set containing the names of all collections in this database." "Returns a set containing the names of all collections in this database."
([^DB db] ([]
(set (.getCollectionNames db)))) (set (.getCollectionNames ^DB monger.core/*mongodb-database*)))
([^DB database]
(set (.getCollectionNames ^DB database))))

View file

@ -1,50 +1,26 @@
;; This source code is dual-licensed under the Apache License, version ;; Copyright (c) 2011-2012 Michael S. Klishin
;; 2.0, and the Eclipse Public License, version 1.0.
;; ;;
;; The APL v2.0: ;; The use and distribution terms for this software are covered by the
;; ;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
;; ---------------------------------------------------------------------------------- ;; which can be found in the file epl-v10.html at the root of this distribution.
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team ;; By using this software in any fashion, you are agreeing to be bound by
;; ;; the terms of this license.
;; Licensed under the Apache License, Version 2.0 (the "License"); ;; You must not remove this notice, or any other, from this software.
;; you may not use this file except in compliance with the License.
;; You may obtain a copy of the License at
;;
;; http://www.apache.org/licenses/LICENSE-2.0
;;
;; Unless required by applicable law or agreed to in writing, software
;; distributed under the License is distributed on an "AS IS" BASIS,
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;; See the License for the specific language governing permissions and
;; limitations under the License.
;; ----------------------------------------------------------------------------------
;;
;; The EPL v1.0:
;;
;; ----------------------------------------------------------------------------------
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team.
;; All rights reserved.
;;
;; This program and the accompanying materials are made available under the terms of
;; the Eclipse Public License Version 1.0,
;; which accompanies this distribution and is available at
;; http://www.eclipse.org/legal/epl-v10.html.
;; ----------------------------------------------------------------------------------
(ns monger.gridfs (ns
"Provides functions and macros for working with GridFS: storing files in GridFS, streaming files from GridFS, ^{:doc "Provides functions and macros for working with GridFS: storing files in GridFS, streaming files from GridFS,
finding stored files. finding stored files.
Related documentation guide: http://clojuremongodb.info/articles/gridfs.html" Related documentation guide: http://clojuremongodb.info/articles/gridfs.html"}
monger.gridfs
(:refer-clojure :exclude [remove find]) (:refer-clojure :exclude [remove find])
(:require monger.core (:require monger.core
[clojure.java.io :as io] [clojure.java.io :as io])
[monger.conversion :refer :all] (:use monger.conversion
[clojurewerkz.support.fn :refer [fpartial]]) [clojurewerkz.support.fn :only [fpartial]])
(:import [com.mongodb DB DBObject] (:import [com.mongodb DB DBObject]
org.bson.types.ObjectId
[com.mongodb.gridfs GridFS GridFSInputFile] [com.mongodb.gridfs GridFS GridFSInputFile]
[java.io InputStream ByteArrayInputStream File])) [java.io InputStream File]))
;; ;;
;; Implementation ;; Implementation
@ -66,16 +42,24 @@
(defn remove (defn remove
[^GridFS fs query] ([]
(.remove fs ^DBObject (to-db-object query))) (remove {}))
([query]
(.remove ^GridFS monger.core/*mongodb-gridfs* ^DBObject (to-db-object query)))
([^GridFS fs query]
(.remove fs ^DBObject (to-db-object query))))
(defn remove-all (defn remove-all
[^GridFS fs] ([]
(remove fs {})) (remove {}))
([^GridFS fs]
(remove fs {})))
(defn all-files (defn all-files
([^GridFS fs] ([]
(.getFileList fs (to-db-object {}))) (.getFileList ^GridFS monger.core/*mongodb-gridfs*))
([query]
(.getFileList ^GridFS monger.core/*mongodb-gridfs* query))
([^GridFS fs query] ([^GridFS fs query]
(.getFileList fs query))) (.getFileList fs query)))
@ -83,8 +67,10 @@
(fpartial from-db-object true)) (fpartial from-db-object true))
(defn files-as-maps (defn files-as-maps
([^GridFS fs] ([]
(files-as-maps fs {})) (map converter (all-files)))
([query]
(map converter (all-files (to-db-object query))))
([^GridFS fs query] ([^GridFS fs query]
(map converter (all-files fs (to-db-object query))))) (map converter (all-files fs (to-db-object query)))))
@ -93,51 +79,27 @@
;; Plumbing (low-level API) ;; Plumbing (low-level API)
;; ;;
(defprotocol InputStreamFactory
(^InputStream to-input-stream [input] "Makes InputStream out of the given input"))
(extend byte-array-type
InputStreamFactory
{:to-input-stream (fn [^bytes input]
(ByteArrayInputStream. input))})
(extend-protocol InputStreamFactory
String
(to-input-stream [^String input]
(io/make-input-stream input {:encoding "UTF-8"}))
File
(to-input-stream [^File input]
(io/make-input-stream input {:encoding "UTF-8"}))
InputStream
(to-input-stream [^InputStream input]
input))
(defprotocol GridFSInputFileFactory (defprotocol GridFSInputFileFactory
(^GridFSInputFile create-gridfs-file [input ^GridFS fs] "Creates a file entry")) (^com.mongodb.gridfs.GridFSInputFile make-input-file [input] "Makes GridFSInputFile out of the given input"))
(extend byte-array-type (extend byte-array-type
GridFSInputFileFactory GridFSInputFileFactory
{:create-gridfs-file (fn [^bytes input ^GridFS fs] {:make-input-file (fn [^bytes input]
(.createFile fs input))}) (.createFile ^GridFS monger.core/*mongodb-gridfs* input))})
(extend-protocol GridFSInputFileFactory (extend-protocol GridFSInputFileFactory
String String
(create-gridfs-file [^String input ^GridFS fs] (make-input-file [^String input]
(.createFile fs (io/file input))) (.createFile ^GridFS monger.core/*mongodb-gridfs* ^InputStream (io/make-input-stream input {:encoding "UTF-8"})))
File File
(create-gridfs-file [^File input ^GridFS fs] (make-input-file [^File input]
(.createFile fs input)) (.createFile ^GridFS monger.core/*mongodb-gridfs* ^InputStream (io/make-input-stream input {:encoding "UTF-8"})))
InputStream InputStream
(create-gridfs-file [^InputStream input ^GridFS fs] (make-input-file [^InputStream input]
(.createFile fs input))) (.createFile ^GridFS monger.core/*mongodb-gridfs* ^InputStream input)))
(defn ^GridFSInputFile make-input-file
[^GridFS fs input]
(create-gridfs-file input fs))
(defmacro store (defmacro store
[^GridFSInputFile input & body] [^GridFSInputFile input & body]
@ -145,8 +107,9 @@
(.save f# GridFS/DEFAULT_CHUNKSIZE) (.save f# GridFS/DEFAULT_CHUNKSIZE)
(from-db-object f# true))) (from-db-object f# true)))
;; ;;
;; Higher-level API ;; "New" DSL, a higher-level API
;; ;;
(defn save (defn save
@ -179,34 +142,48 @@
;; Finders ;; Finders
;; ;;
(defn find (defprotocol Finders
[^GridFS fs query] (find [input] "Finds multiple files using given input (an ObjectId, filename or query)")
(.find fs (to-db-object query))) (find-one [input] "Finds one file using given input (an ObjectId, filename or query)")
(find-maps [input] "Finds multiple files using given input (an ObjectId, filename or query), returning a Clojure map")
(find-one-as-map [input] "Finds one file using given input (an ObjectId, filename or query), returning a Clojure map"))
(defn find-by-filename (extend-protocol Finders
[^GridFS fs ^String filename] String
(.find fs (to-db-object {"filename" filename}))) (find [^String input]
(.find ^GridFS monger.core/*mongodb-gridfs* input))
(find-one [^String input]
(.findOne ^GridFS monger.core/*mongodb-gridfs* input))
(find-maps [^String input]
(map converter (find input)))
(find-one-as-map [^String input]
(converter (find-one input)))
(defn find-by-md5 org.bson.types.ObjectId
[^GridFS fs ^String md5] (find-one [^org.bson.types.ObjectId input]
(.find fs (to-db-object {"md5" md5}))) (.findOne ^GridFS monger.core/*mongodb-gridfs* input))
(find-one-as-map [^org.bson.types.ObjectId input]
(converter (find-one input)))
(defn find-one
[^GridFS fs query]
(.findOne fs (to-db-object query)))
(defn find-maps DBObject
[^GridFS fs query] (find [^DBObject input]
(map converter (find fs query))) (.find ^GridFS monger.core/*mongodb-gridfs* input))
(find-one [^DBObject input]
(.findOne ^GridFS monger.core/*mongodb-gridfs* input))
(find-maps [^DBObject input]
(map converter (find input)))
(find-one-as-map [^DBObject input]
(converter (find-one input)))
(defn find-one-as-map ;; using java.util.Map here results in (occasional) recursion
[^GridFS fs query] clojure.lang.IPersistentMap
(converter (find-one fs query))) (find [^java.util.Map input]
(find (to-db-object input)))
(find-one [^java.util.Map input]
(find-one (to-db-object input)))
(find-maps [^java.util.Map input]
(find-maps (to-db-object input)))
(find-one-as-map [^java.util.Map input]
(find-one-as-map (to-db-object input))))
(defn find-by-id
[^GridFS fs ^ObjectId id]
(.findOne fs id))
(defn find-map-by-id
[^GridFS fs ^ObjectId id]
(converter (find-by-id fs id)))

View file

@ -0,0 +1,73 @@
;; Copyright (c) 2011-2012 Michael S. Klishin
;;
;; The use and distribution terms for this software are covered by the
;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
;; which can be found in the file epl-v10.html at the root of this distribution.
;; By using this software in any fashion, you are agreeing to be bound by
;; the terms of this license.
;; You must not remove this notice, or any other, from this software.
(ns monger.internal.fn)
;;
;; Implementation
;;
(defn- apply-to-values [m f]
"Applies function f to all values in map m"
(into {} (for [[k v] m]
[k (f v)])))
;;
;; API
;;
(defn fpartial
"Like clojure.core/partial but prepopulates last N arguments (first is passed in later)"
[f & args]
(fn [arg & more] (apply f arg (concat args more))))
(defprotocol IFNExpansion
(expand-all [x] "Replaces functions with their invocation results, recursively expands maps, evaluates all other values to themselves")
(expand-all-with [x f] "Replaces functions with their invocation results that function f is applied to, recursively expands maps, evaluates all other values to themselves"))
(extend-protocol IFNExpansion
java.lang.Integer
(expand-all [i] i)
(expand-all-with [i f] i)
java.lang.Long
(expand-all [l] l)
(expand-all-with [l f] l)
java.lang.String
(expand-all [s] s)
(expand-all-with [s f] s)
java.lang.Float
(expand-all [fl] fl)
(expand-all-with [fl f] fl)
java.lang.Double
(expand-all [d] d)
(expand-all-with [d f] d)
;; maps are also functions, so be careful here. MK.
clojure.lang.IPersistentMap
(expand-all [m] (apply-to-values m expand-all))
(expand-all-with [m f] (apply-to-values m (fpartial expand-all-with f)))
clojure.lang.PersistentVector
(expand-all [v] (map expand-all v))
(expand-all-with [v f] (map (fpartial expand-all-with f) v))
;; this distinguishes functions from maps, sets and so on, which are also
;; clojure.lang.AFn subclasses. MK.
clojure.lang.AFunction
(expand-all [f] (f))
(expand-all-with [f expander] (expander f))
Object
(expand-all [x] x)
(expand-all-with [x f] x))

View file

@ -1,35 +1,11 @@
;; This source code is dual-licensed under the Apache License, version ;; Copyright (c) 2011-2012 Michael S. Klishin
;; 2.0, and the Eclipse Public License, version 1.0.
;; ;;
;; The APL v2.0: ;; The use and distribution terms for this software are covered by the
;; ;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
;; ---------------------------------------------------------------------------------- ;; which can be found in the file epl-v10.html at the root of this distribution.
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team ;; By using this software in any fashion, you are agreeing to be bound by
;; ;; the terms of this license.
;; Licensed under the Apache License, Version 2.0 (the "License"); ;; You must not remove this notice, or any other, from this software.
;; you may not use this file except in compliance with the License.
;; You may obtain a copy of the License at
;;
;; http://www.apache.org/licenses/LICENSE-2.0
;;
;; Unless required by applicable law or agreed to in writing, software
;; distributed under the License is distributed on an "AS IS" BASIS,
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;; See the License for the specific language governing permissions and
;; limitations under the License.
;; ----------------------------------------------------------------------------------
;;
;; The EPL v1.0:
;;
;; ----------------------------------------------------------------------------------
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team.
;; All rights reserved.
;;
;; This program and the accompanying materials are made available under the terms of
;; the Eclipse Public License Version 1.0,
;; which accompanies this distribution and is available at
;; http://www.eclipse.org/legal/epl-v10.html.
;; ----------------------------------------------------------------------------------
(ns monger.internal.pagination) (ns monger.internal.pagination)

View file

@ -1,48 +1,23 @@
;; This source code is dual-licensed under the Apache License, version ;; Copyright (c) 2011-2012 Michael S. Klishin
;; 2.0, and the Eclipse Public License, version 1.0.
;; ;;
;; The APL v2.0: ;; The use and distribution terms for this software are covered by the
;; ;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
;; ---------------------------------------------------------------------------------- ;; which can be found in the file epl-v10.html at the root of this distribution.
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team ;; By using this software in any fashion, you are agreeing to be bound by
;; ;; the terms of this license.
;; Licensed under the Apache License, Version 2.0 (the "License"); ;; You must not remove this notice, or any other, from this software.
;; you may not use this file except in compliance with the License.
;; You may obtain a copy of the License at
;;
;; http://www.apache.org/licenses/LICENSE-2.0
;;
;; Unless required by applicable law or agreed to in writing, software
;; distributed under the License is distributed on an "AS IS" BASIS,
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;; See the License for the specific language governing permissions and
;; limitations under the License.
;; ----------------------------------------------------------------------------------
;;
;; The EPL v1.0:
;;
;; ----------------------------------------------------------------------------------
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team.
;; All rights reserved.
;;
;; This program and the accompanying materials are made available under the terms of
;; the Eclipse Public License Version 1.0,
;; which accompanies this distribution and is available at
;; http://www.eclipse.org/legal/epl-v10.html.
;; ----------------------------------------------------------------------------------
(ns monger.joda-time (ns ^{:doc "An optional convenience namespaces for applications that heavily use dates and would prefer use JodaTime types
"An optional convenience namespaces for applications that heavily use dates and would prefer use JodaTime types
transparently when storing and loading them from MongoDB and serializing to JSON and/or with Clojure reader. transparently when storing and loading them from MongoDB and serializing to JSON and/or with Clojure reader.
Enables automatic conversion of JodaTime date/time/instant instances to JDK dates (java.util.Date) when documents Enables automatic conversion of JodaTime date/time/instant instances to JDK dates (java.util.Date) when documents
are serialized and the other way around when documents are loaded. Extends clojure.data.json/Write-JSON protocol for are serialized and the other way around when documents are loaded. Extends clojure.data.json/Write-JSON protocol for
JodaTime types. JodaTime types.
To use it, make sure you add dependencies on clj-time (or JodaTime) and clojure.data.json." To use it, make sure you add dependencies on clj-time (or JodaTime) and clojure.data.json."} monger.joda-time
(:import [org.joda.time DateTime DateTimeZone ReadableInstant] (:import [org.joda.time DateTime DateTimeZone ReadableInstant]
[org.joda.time.format ISODateTimeFormat]) [org.joda.time.format ISODateTimeFormat])
(:require [monger.conversion :refer :all])) (:use [monger.conversion]))
;; ;;
;; API ;; API
@ -51,9 +26,6 @@
(extend-protocol ConvertToDBObject (extend-protocol ConvertToDBObject
org.joda.time.base.AbstractInstant org.joda.time.base.AbstractInstant
(to-db-object [^AbstractInstant input] (to-db-object [^AbstractInstant input]
(to-db-object (.toDate input)))
org.joda.time.base.AbstractPartial
(to-db-object [^AbstractPartial input]
(to-db-object (.toDate input)))) (to-db-object (.toDate input))))
(extend-protocol ConvertFromDBObject (extend-protocol ConvertFromDBObject
@ -67,17 +39,31 @@
;; Reader extensions ;; Reader extensions
;; ;;
(defmethod print-dup java.util.Date
[^java.util.Date d ^java.io.Writer out]
(.write out
(str "#="
`(java.util.Date. ~(.getYear d)
~(.getMonth d)
~(.getDate d)
~(.getHours d)
~(.getMinutes d)
~(.getSeconds d)))))
(defmethod print-dup org.joda.time.base.AbstractInstant (defmethod print-dup org.joda.time.base.AbstractInstant
[^org.joda.time.base.AbstractInstant d out] [^org.joda.time.base.AbstractInstant d out]
(print-dup (.toDate d) out)) (print-dup (.toDate d) out))
(defmethod print-dup org.joda.time.base.AbstractPartial
[^org.joda.time.base.AbstractPartial d out]
(print-dup (.toDate d) out))
;; ;;
;; JSON serialization ;; JSON serialization
;; ;;
(require 'clojurewerkz.support.json) (try
;; try to load clojure.data.json. If available, load CLJW Support
;; extensions.
(require 'clojure.data.json)
(require 'clojurewerkz.support.json)
(catch Throwable _
false))

View file

@ -1,38 +1,13 @@
;; This source code is dual-licensed under the Apache License, version ;; Copyright (c) 2011-2012 Michael S. Klishin
;; 2.0, and the Eclipse Public License, version 1.0.
;; ;;
;; The APL v2.0: ;; The use and distribution terms for this software are covered by the
;; ;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
;; ---------------------------------------------------------------------------------- ;; which can be found in the file epl-v10.html at the root of this distribution.
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team ;; By using this software in any fashion, you are agreeing to be bound by
;; ;; the terms of this license.
;; Licensed under the Apache License, Version 2.0 (the "License"); ;; You must not remove this notice, or any other, from this software.
;; you may not use this file except in compliance with the License.
;; You may obtain a copy of the License at
;;
;; http://www.apache.org/licenses/LICENSE-2.0
;;
;; Unless required by applicable law or agreed to in writing, software
;; distributed under the License is distributed on an "AS IS" BASIS,
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;; See the License for the specific language governing permissions and
;; limitations under the License.
;; ----------------------------------------------------------------------------------
;;
;; The EPL v1.0:
;;
;; ----------------------------------------------------------------------------------
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team.
;; All rights reserved.
;;
;; This program and the accompanying materials are made available under the terms of
;; the Eclipse Public License Version 1.0,
;; which accompanies this distribution and is available at
;; http://www.eclipse.org/legal/epl-v10.html.
;; ----------------------------------------------------------------------------------
(ns monger.js (ns ^{:doc "Kept for backwards compatibility. Please use clojurewerkz.support.js from now on."} monger.js
"Kept for backwards compatibility. Please use clojurewerkz.support.js from now on."
(:require [clojurewerkz.support.js :as js])) (:require [clojurewerkz.support.js :as js]))

View file

@ -1,116 +1,24 @@
;; This source code is dual-licensed under the Apache License, version ;; Copyright (c) 2011-2012 Michael S. Klishin
;; 2.0, and the Eclipse Public License, version 1.0.
;; ;;
;; The APL v2.0: ;; The use and distribution terms for this software are covered by the
;; ;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
;; ---------------------------------------------------------------------------------- ;; which can be found in the file epl-v10.html at the root of this distribution.
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team ;; By using this software in any fashion, you are agreeing to be bound by
;; ;; the terms of this license.
;; Licensed under the Apache License, Version 2.0 (the "License"); ;; You must not remove this notice, or any other, from this software.
;; you may not use this file except in compliance with the License.
;; You may obtain a copy of the License at
;;
;; http://www.apache.org/licenses/LICENSE-2.0
;;
;; Unless required by applicable law or agreed to in writing, software
;; distributed under the License is distributed on an "AS IS" BASIS,
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;; See the License for the specific language governing permissions and
;; limitations under the License.
;; ----------------------------------------------------------------------------------
;;
;; The EPL v1.0:
;;
;; ----------------------------------------------------------------------------------
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team.
;; All rights reserved.
;;
;; This program and the accompanying materials are made available under the terms of
;; the Eclipse Public License Version 1.0,
;; which accompanies this distribution and is available at
;; http://www.eclipse.org/legal/epl-v10.html.
;; ----------------------------------------------------------------------------------
(ns monger.json
"Provides clojure.data.json/Write-JSON protocol extension for MongoDB-specific types, such as
org.bson.types.ObjectId"
(:import org.bson.types.ObjectId
org.bson.types.BSONTimestamp))
;;
;; Implementation
;;
;; copied from clojure.reducers
(defmacro ^:private compile-if
"Evaluate `exp` and if it returns logical true and doesn't error, expand to
`then`. Else expand to `else`.
(compile-if (Class/forName \"java.util.concurrent.ForkJoinTask\")
(do-cool-stuff-with-fork-join)
(fall-back-to-executor-services))"
[exp then else]
(if (try (eval exp)
(catch Throwable _ false))
`(do ~then)
`(do ~else)))
(ns ^{:doc "Provides clojure.data.json/Write-JSON protocol extension for MongoDB-specific types, such as
org.bson.types.ObjectId"}
monger.json
(:import org.bson.types.ObjectId)
(:require [clojure.data.json :as json]
clojurewerkz.support.json))
;; ;;
;; API ;; API
;; ;;
(require 'clojurewerkz.support.json) (extend-protocol json/Write-JSON
;; all this madness would not be necessary if some people cared about backwards
;; compatiblity of the libraries they maintain. Shame on the clojure.data.json maintainer. MK.
(compile-if (and (find-ns 'clojure.data.json)
clojure.data.json/JSONWriter)
(try
(extend-protocol clojure.data.json/JSONWriter
ObjectId
(-write
([^ObjectId object out]
(clojure.data.json/write (.toString object) out))
([^ObjectId object out options]
(clojure.data.json/write (.toString object) out options))))
(extend-protocol clojure.data.json/JSONWriter
BSONTimestamp
(-write
([^BSONTimestamp object out]
(clojure.data.json/write {:time (.getTime object) :inc (.getInc object)} out))
([^BSONTimestamp object out options]
(clojure.data.json/write {:time (.getTime object) :inc (.getInc object)} out options))))
(catch Throwable _
false))
(comment "Nothing to do, clojure.data.json is not available"))
(compile-if (and (find-ns 'clojure.data.json)
clojure.data.json/Write-JSON)
(try
(extend-protocol clojure.data.json/Write-JSON
ObjectId ObjectId
(write-json [^ObjectId object out escape-unicode?] (write-json [^ObjectId object out escape-unicode?]
(clojure.data.json/write-json (.toString object) out escape-unicode?))) (json/write-json (.toString object) out escape-unicode?)))
(catch Throwable _
false))
(comment "Nothing to do, clojure.data.json 0.1.x is not available"))
(try
(require 'cheshire.generate)
(catch Throwable t
false))
(try
(cheshire.generate/add-encoder ObjectId
(fn [^ObjectId oid ^com.fasterxml.jackson.core.json.WriterBasedJsonGenerator generator]
(.writeString generator (.toString oid))))
(cheshire.generate/add-encoder BSONTimestamp
(fn [^BSONTimestamp ts ^com.fasterxml.jackson.core.json.WriterBasedJsonGenerator generator]
(cheshire.generate/encode-map {:time (.getTime ts) :inc (.getInc ts)} generator)))
(catch Throwable t
false))

View file

@ -1,42 +1,18 @@
;; This source code is dual-licensed under the Apache License, version ;; Copyright (c) 2011-2012 Michael S. Klishin
;; 2.0, and the Eclipse Public License, version 1.0.
;; ;;
;; The APL v2.0: ;; The use and distribution terms for this software are covered by the
;; ;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
;; ---------------------------------------------------------------------------------- ;; which can be found in the file epl-v10.html at the root of this distribution.
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team ;; By using this software in any fashion, you are agreeing to be bound by
;; ;; the terms of this license.
;; Licensed under the Apache License, Version 2.0 (the "License"); ;; You must not remove this notice, or any other, from this software.
;; you may not use this file except in compliance with the License.
;; You may obtain a copy of the License at
;;
;; http://www.apache.org/licenses/LICENSE-2.0
;;
;; Unless required by applicable law or agreed to in writing, software
;; distributed under the License is distributed on an "AS IS" BASIS,
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;; See the License for the specific language governing permissions and
;; limitations under the License.
;; ----------------------------------------------------------------------------------
;;
;; The EPL v1.0:
;;
;; ----------------------------------------------------------------------------------
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team.
;; All rights reserved.
;;
;; This program and the accompanying materials are made available under the terms of
;; the Eclipse Public License Version 1.0,
;; which accompanies this distribution and is available at
;; http://www.eclipse.org/legal/epl-v10.html.
;; ----------------------------------------------------------------------------------
(ns monger.operators (ns ^{:doc "Provides vars that represent various MongoDB operators, for example, $gt or $in or $regex.
"Provides vars that represent various MongoDB operators, for example, $gt or $in or $regex.
They can be passed in queries as strings but using vars from this namespace makes the code They can be passed in queries as strings but using vars from this namespace makes the code
a bit cleaner and closer to what you would see in a MongoDB shell query. a bit cleaner and closer to what you would see in a MongoDB shell query.
Related documentation guide: http://clojuremongodb.info/articles/querying.html") Related documentation guide: http://clojuremongodb.info/articles/querying.html"}
monger.operators)
(defmacro ^{:private true} defoperator (defmacro ^{:private true} defoperator
[operator] [operator]
@ -46,8 +22,6 @@
;; QUERY OPERATORS ;; QUERY OPERATORS
;; ;;
(declare $gt $gte $lt $lte $all $in $nin $eq $ne $elemMatch $regex $options)
;; $gt is "greater than" comparator ;; $gt is "greater than" comparator
;; $gte is "greater than or equals" comparator ;; $gte is "greater than or equals" comparator
;; $gt is "less than" comparator ;; $gt is "less than" comparator
@ -82,16 +56,10 @@
;; (mgcol/find-maps "languages" { :tags { $nin [ "functional" ] } } ) ;; (mgcol/find-maps "languages" { :tags { $nin [ "functional" ] } } )
(defoperator $nin) (defoperator $nin)
;; $eq is "equals" comparator
;;
;; EXAMPLES:
;; (monger.collection/find "libraries" { :language { $eq "Clojure" }})
(defoperator $eq)
;; $ne is "non-equals" comparator ;; $ne is "non-equals" comparator
;; ;;
;; EXAMPLES: ;; EXAMPLES:
;; (monger.collection/find "libraries" { :language { $ne "Clojure" }}) ;; (monger.collection/find "libraries" {$ne { :language "Clojure" }})
(defoperator $ne) (defoperator $ne)
;; $elemMatch checks if an element in an array matches the specified expression ;; $elemMatch checks if an element in an array matches the specified expression
@ -104,37 +72,6 @@
(defoperator $regex) (defoperator $regex)
(defoperator $options) (defoperator $options)
;; comment on a query predicate
(declare $comment $explain $hint $maxTimeMS $orderBy $query $returnKey $showDiskLoc $natural)
(defoperator $comment)
(defoperator $explain)
(defoperator $hint)
(defoperator $maxTimeMS)
(defoperator $orderBy)
(defoperator $query)
(defoperator $returnKey)
(defoperator $showDiskLoc)
(defoperator $natural)
;;
;; EVALUATION (QUERY)
;;
(declare $expr $jsonSchema $where $and $or $nor)
(defoperator $expr)
(defoperator $jsonSchema)
;; Matches documents that satisfy a JavaScript expression.
;;
;; EXAMPLES:
;;
;; (monger.collection/find "people" { $where "this.placeOfBirth === this.address.city" })
(defoperator $where)
;; ;;
;; LOGIC OPERATORS ;; LOGIC OPERATORS
;; ;;
@ -167,8 +104,6 @@
;; ATOMIC MODIFIERS ;; ATOMIC MODIFIERS
;; ;;
(declare $inc $mul $set $unset $setOnInsert $rename $push $position $each $addToSet $pop $pull $pullAll $bit $bitsAllClear $bitsAllSet $bitsAnyClear $bitsAnySet $exists $mod $size $type $not)
;; $inc increments one or many fields for the given value, otherwise sets the field to value ;; $inc increments one or many fields for the given value, otherwise sets the field to value
;; ;;
;; EXAMPLES: ;; EXAMPLES:
@ -176,8 +111,6 @@
;; (monger.collection/update "scores" { :_id user-id } { :score 20 :bonus 10 } }) ;; (monger.collection/update "scores" { :_id user-id } { :score 20 :bonus 10 } })
(defoperator $inc) (defoperator $inc)
(defoperator $mul)
;; $set sets an existing (or non-existing) field (or set of fields) to value ;; $set sets an existing (or non-existing) field (or set of fields) to value
;; $set supports all datatypes. ;; $set supports all datatypes.
;; ;;
@ -192,13 +125,6 @@
;; (monger.collection/update "things" { :_id oid } { $unset { :weight 1 } }) ;; (monger.collection/update "things" { :_id oid } { $unset { :weight 1 } })
(defoperator $unset) (defoperator $unset)
;; $setOnInsert assigns values to fields during an upsert only when using the upsert option to the update operation performs an insert.
;; New in version 2.4. http://docs.mongodb.org/manual/reference/operator/setOnInsert/
;;
;; EXAMPLES:
;; (monger.collection/find-and-modify "things" {:_id oid} {$set {:lastseen now} $setOnInsert {:firstseen now}} :upsert true)
(defoperator $setOnInsert)
;; $rename renames a given field ;; $rename renames a given field
;; ;;
;; EXAMPLES: ;; EXAMPLES:
@ -212,17 +138,12 @@
;; (mgcol/update "docs" { :_id oid } { $push { :tags "modifiers" } }) ;; (mgcol/update "docs" { :_id oid } { $push { :tags "modifiers" } })
(defoperator $push) (defoperator $push)
;; $position modifies the behavior of $push per https://docs.mongodb.com/manual/reference/operator/update/position/ ;; $pushAll appends each value in value_array to field, if field is an existing array, otherwise sets field to the array value_array
(defoperator $position) ;; if field is not present. If field is present but is not an array, an error condition is raised.
;; $each is a modifier for the $push and $addToSet operators for appending multiple values to an array field.
;; Without the $each modifier $push and $addToSet will append an array as a single value.
;; MongoDB 2.4 adds support for the $each modifier to the $push operator.
;; In MongoDB 2.2 the $each modifier can only be used with the $addToSet operator.
;; ;;
;; EXAMPLES: ;; EXAMPLES:
;; (mgcol/update coll { :_id oid } { $push { :tags { $each ["mongodb" "docs"] } } }) ;; (mgcol/update coll { :_id oid } { $pushAll { :tags ["mongodb" "docs"] } })
(defoperator $each) (defoperator $pushAll)
;; $addToSet Adds value to the array only if its not in the array already, if field is an existing array, otherwise sets field to the ;; $addToSet Adds value to the array only if its not in the array already, if field is an existing array, otherwise sets field to the
;; array value if field is not present. If field is present but is not an array, an error condition is raised. ;; array value if field is not present. If field is present but is not an array, an error condition is raised.
@ -250,15 +171,11 @@
;; an error condition is raised. ;; an error condition is raised.
;; ;;
;; EXAMPLES: ;; EXAMPLES:
;; (mgcol/update coll { :_id oid } { $pullAll { :measurements 1.2 } }) ;; (mgcol/update coll { :_id oid } { $pull { :measurements 1.2 } })
;; (mgcol/update coll { :_id oid } { $pullAll { :measurements { $gte 1.2 } } }) ;; (mgcol/update coll { :_id oid } { $pull { :measurements { $gte 1.2 } } })
(defoperator $pullAll) (defoperator $pullAll)
(defoperator $bit) (defoperator $bit)
(defoperator $bitsAllClear)
(defoperator $bitsAllSet)
(defoperator $bitsAnyClear)
(defoperator $bitsAnySet)
(defoperator $exists) (defoperator $exists)
(defoperator $mod) (defoperator $mod)
@ -268,132 +185,33 @@
;; ;;
;; Aggregation in 4.2 ;; Aggregation in 2.2
;; ;;
(declare $addFields $bucket $bucketAuto $collStats $facet $geoNear $graphLookup $indexStats $listSessions $lookup $match $merge $out $planCacheStats $project $redact $replaceRoot $replaceWith $sample $limit $skip $unwind $group $sort $sortByCount $currentOp $listLocalSessions $cmp $min $max $avg $stdDevPop $stdDevSamp $sum $let $first $last $abs $add $ceil $divide $exp $floor $ln $log $log10 $multiply $pow $round $sqrt $subtract $trunc $literal $arrayElemAt $arrayToObject $concatArrays $filter $indexOfArray $isArray $map $objectToArray $range $reduce $reverseArray $zip $mergeObjects $allElementsTrue $anyElementsTrue $setDifference $setEquals $setIntersection $setIsSubset $setUnion $strcasecmp $substr $substrBytes $substrCP $toLower $toString $toUpper $concat $indexOfBytes $indexOfCP $ltrim $regexFind $regexFindAll $regexMatch $rtrim $split $strLenBytes $subLenCP $trim $sin $cos $tan $asin $acos $atan $atan2 $asinh $acosh $atanh $radiansToDegrees $degreesToRadians $convert $toBool $toDecimal $toDouble $toInt $toLong $toObjectId $dayOfMonth $dayOfWeek $dayOfYear $hour $minute $month $second $millisecond $week $year $isoDate $dateFromParts $dateFromString $dateToParts $dateToString $isoDayOfWeek $isoWeek $isoWeekYear $toDate $ifNull $cond $switch)
(defoperator $addFields)
(defoperator $bucket)
(defoperator $bucketAuto)
(defoperator $collStats)
(defoperator $facet)
(defoperator $geoNear)
(defoperator $graphLookup)
(defoperator $indexStats)
(defoperator $listSessions)
(defoperator $lookup)
(defoperator $match) (defoperator $match)
(defoperator $merge)
(defoperator $out)
(defoperator $planCacheStats)
(defoperator $project) (defoperator $project)
(defoperator $redact)
(defoperator $replaceRoot)
(defoperator $replaceWith)
(defoperator $sample)
(defoperator $limit) (defoperator $limit)
(defoperator $skip) (defoperator $skip)
(defoperator $unwind) (defoperator $unwind)
(defoperator $group) (defoperator $group)
(defoperator $sort) (defoperator $sort)
(defoperator $sortByCount)
(defoperator $currentOp)
(defoperator $listLocalSessions)
(defoperator $cmp) (defoperator $cmp)
(defoperator $min) (defoperator $min)
(defoperator $max) (defoperator $max)
(defoperator $avg) (defoperator $avg)
(defoperator $stdDevPop)
(defoperator $stdDevSamp)
(defoperator $sum) (defoperator $sum)
(defoperator $let)
(defoperator $first)
(defoperator $last)
(defoperator $abs)
(defoperator $add) (defoperator $add)
(defoperator $ceil)
(defoperator $divide) (defoperator $divide)
(defoperator $exp)
(defoperator $floor)
(defoperator $ln)
(defoperator $log)
(defoperator $log10)
(defoperator $multiply) (defoperator $multiply)
(defoperator $pow) (defoperator $substract)
(defoperator $round)
(defoperator $sqrt)
(defoperator $subtract)
(defoperator $trunc)
(defoperator $literal)
(defoperator $arrayElemAt)
(defoperator $arrayToObject)
(defoperator $concatArrays)
(defoperator $filter)
(defoperator $indexOfArray)
(defoperator $isArray)
(defoperator $map)
(defoperator $objectToArray)
(defoperator $range)
(defoperator $reduce)
(defoperator $reverseArray)
(defoperator $zip)
(defoperator $mergeObjects)
(defoperator $allElementsTrue)
(defoperator $anyElementsTrue)
(defoperator $setDifference)
(defoperator $setEquals)
(defoperator $setIntersection)
(defoperator $setIsSubset)
(defoperator $setUnion)
(defoperator $strcasecmp) (defoperator $strcasecmp)
(defoperator $substr) (defoperator $substr)
(defoperator $substrBytes)
(defoperator $substrCP)
(defoperator $toLower) (defoperator $toLower)
(defoperator $toString)
(defoperator $toUpper) (defoperator $toUpper)
(defoperator $concat)
(defoperator $indexOfBytes)
(defoperator $indexOfCP)
(defoperator $ltrim)
(defoperator $regexFind)
(defoperator $regexFindAll)
(defoperator $regexMatch)
(defoperator $rtrim)
(defoperator $split)
(defoperator $strLenBytes)
(defoperator $subLenCP)
(defoperator $trim)
(defoperator $sin)
(defoperator $cos)
(defoperator $tan)
(defoperator $asin)
(defoperator $acos)
(defoperator $atan)
(defoperator $atan2)
(defoperator $asinh)
(defoperator $acosh)
(defoperator $atanh)
(defoperator $radiansToDegrees)
(defoperator $degreesToRadians)
(defoperator $convert)
(defoperator $toBool)
(defoperator $toDecimal)
(defoperator $toDouble)
(defoperator $toInt)
(defoperator $toLong)
(defoperator $toObjectId)
(defoperator $dayOfMonth) (defoperator $dayOfMonth)
(defoperator $dayOfWeek) (defoperator $dayOfWeek)
@ -402,58 +220,10 @@
(defoperator $minute) (defoperator $minute)
(defoperator $month) (defoperator $month)
(defoperator $second) (defoperator $second)
(defoperator $millisecond)
(defoperator $week) (defoperator $week)
(defoperator $year) (defoperator $year)
(defoperator $isoDate) (defoperator $isoDate)
(defoperator $dateFromParts)
(defoperator $dateFromString)
(defoperator $dateToParts)
(defoperator $dateToString)
(defoperator $isoDayOfWeek)
(defoperator $isoWeek)
(defoperator $isoWeekYear)
(defoperator $toDate)
(defoperator $ifNull) (defoperator $ifNull)
(defoperator $cond) (defoperator $cond)
(defoperator $switch)
;; Geospatial
(declare $geoWithin $geoIntersects $near $nearSphere $geometry $maxDistance $minDistance $center $centerSphere $box $polygon $slice)
(defoperator $geoWithin)
(defoperator $geoIntersects)
(defoperator $near)
(defoperator $nearSphere)
(defoperator $geometry)
(defoperator $maxDistance)
(defoperator $minDistance)
(defoperator $center)
(defoperator $centerSphere)
(defoperator $box)
(defoperator $polygon)
(defoperator $slice)
;; full text search
(declare $text $meta $search $language $natural $currentDate $isolated $count)
(defoperator $text)
(defoperator $meta)
(defoperator $search)
(defoperator $language)
(defoperator $natural)
;; $currentDate operator sets the value of a field to the current date, either as a Date or a timestamp. The default type is Date.
;;
;; EXAMPLES:
;; (mgcol/update coll { :_id oid } { $currentDate { :lastModified true } })
(defoperator $currentDate)
;; Isolates intermediate multi-document updates from other clients.
;;
;; EXAMPLES:
;; (mgcol/update "libraries" { :language "Clojure", $isolated 1 } { $inc { :popularity 1 } } {:multi true})
(defoperator $isolated)
(defoperator $count)

View file

@ -1,56 +1,31 @@
;; This source code is dual-licensed under the Apache License, version ;; Copyright (c) 2011-2012 Michael S. Klishin
;; 2.0, and the Eclipse Public License, version 1.0.
;; ;;
;; The APL v2.0: ;; The use and distribution terms for this software are covered by the
;; ;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
;; ---------------------------------------------------------------------------------- ;; which can be found in the file epl-v10.html at the root of this distribution.
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team ;; By using this software in any fashion, you are agreeing to be bound by
;; ;; the terms of this license.
;; Licensed under the Apache License, Version 2.0 (the "License"); ;; You must not remove this notice, or any other, from this software.
;; you may not use this file except in compliance with the License.
;; You may obtain a copy of the License at
;;
;; http://www.apache.org/licenses/LICENSE-2.0
;;
;; Unless required by applicable law or agreed to in writing, software
;; distributed under the License is distributed on an "AS IS" BASIS,
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;; See the License for the specific language governing permissions and
;; limitations under the License.
;; ----------------------------------------------------------------------------------
;;
;; The EPL v1.0:
;;
;; ----------------------------------------------------------------------------------
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team.
;; All rights reserved.
;;
;; This program and the accompanying materials are made available under the terms of
;; the Eclipse Public License Version 1.0,
;; which accompanies this distribution and is available at
;; http://www.eclipse.org/legal/epl-v10.html.
;; ----------------------------------------------------------------------------------
(ns monger.query (ns ^{:doc "Provides an expressive Query DSL that is very close to that in the Mongo shell (within reason).
"Provides an expressive Query DSL that is very close to that in the Mongo shell (within reason).
This is the most flexible and recommended way to query with Monger. Queries can be composed, like in Korma. This is the most flexible and recommended way to query with Monger. Queries can be composed, like in Korma.
Related documentation guide: http://clojuremongodb.info/articles/querying.html" Related documentation guide: http://clojuremongodb.info/articles/querying.html"}
monger.query
(:refer-clojure :exclude [select find sort]) (:refer-clojure :exclude [select find sort])
(:require [monger.core] (:require [monger.core]
[monger.internal pagination] [monger.internal pagination])
[monger.cursor :as cursor :refer [add-options]]
[monger.conversion :refer :all]
[monger.operators :refer :all])
(:import [com.mongodb DB DBCollection DBObject DBCursor ReadPreference] (:import [com.mongodb DB DBCollection DBObject DBCursor ReadPreference]
[java.util.concurrent TimeUnit] [java.util List])
java.util.List)) (:use [monger conversion operators]))
;; ;;
;; Implementation ;; Implementation
;; ;;
(def ^{:dynamic true} *query-collection*)
;; ;;
;; Cursor/chain methods ;; Cursor/chain methods
;; ;;
@ -66,6 +41,7 @@
;; :skip - Skips the first N results. ;; :skip - Skips the first N results.
;; :limit - Returns a maximum of N results. ;; :limit - Returns a maximum of N results.
;; :batch-size - limits the nubmer of elements returned in one batch. ;; :batch-size - limits the nubmer of elements returned in one batch.
;; :hint - force Mongo to use a specific index for a query in order to improve performance.
;; :snapshot - sses snapshot mode for the query. Snapshot mode assures no duplicates are returned, or objects missed ;; :snapshot - sses snapshot mode for the query. Snapshot mode assures no duplicates are returned, or objects missed
;; which were present at both the start and end of the query's execution (if an object is new during the query, or ;; which were present at both the start and end of the query's execution (if an object is new during the query, or
;; deleted during the query, it may or may not be returned, even with snapshot mode). Note that short query responses ;; deleted during the query, it may or may not be returned, even with snapshot mode). Note that short query responses
@ -79,6 +55,7 @@
:skip 0 :skip 0
:limit 0 :limit 0
:batch-size 256 :batch-size 256
:hint nil
:snapshot false :snapshot false
:keywordize-fields true :keywordize-fields true
}) })
@ -86,35 +63,19 @@
(merge (empty-query) { :collection coll }))) (merge (empty-query) { :collection coll })))
(defn exec (defn exec
[{:keys [^DBCollection collection [{ :keys [^DBCollection collection query fields skip limit sort batch-size hint snapshot read-preference keywordize-fields options] :or { limit 0 batch-size 256 skip 0 } }]
query (let [cursor (doto (.find collection (to-db-object query) (as-field-selector fields))
fields
skip
limit
sort
batch-size
hint
snapshot
read-preference
keywordize-fields
max-time
options]
:or { limit 0 batch-size 256 skip 0 } }]
(with-open [cursor (doto (.find collection (to-db-object query) (as-field-selector fields))
(.limit limit) (.limit limit)
(.skip skip) (.skip skip)
(.sort (to-db-object sort)) (.sort (to-db-object sort))
(.batchSize batch-size))] (.batchSize batch-size)
(.hint (to-db-object hint)))]
(when snapshot (when snapshot
(.snapshot cursor)) (.snapshot cursor))
(when hint
(.hint cursor (to-db-object hint)))
(when read-preference (when read-preference
(.setReadPreference cursor read-preference)) (.setReadPreference cursor read-preference))
(when max-time
(.maxTime cursor max-time TimeUnit/MILLISECONDS))
(when options (when options
(add-options cursor options)) (.setOptions cursor options))
(map (fn [x] (from-db-object x keywordize-fields)) (map (fn [x] (from-db-object x keywordize-fields))
cursor))) cursor)))
@ -158,10 +119,6 @@
[m ^ReadPreference rp] [m ^ReadPreference rp]
(merge m { :read-preference rp })) (merge m { :read-preference rp }))
(defn max-time
[m ^long max-time]
(merge m { :max-time max-time }))
(defn options (defn options
[m opts] [m opts]
(merge m { :options opts })) (merge m { :options opts }))
@ -175,14 +132,12 @@
(merge m { :limit per-page :skip (monger.internal.pagination/offset-for page per-page) })) (merge m { :limit per-page :skip (monger.internal.pagination/offset-for page per-page) }))
(defmacro with-collection (defmacro with-collection
[db coll & body] [^String coll & body]
`(let [coll# ~coll `(binding [*query-collection* (if (string? ~coll)
^DB db# ~db (.getCollection ^DB monger.core/*mongodb-database* ~coll)
db-coll# (if (string? coll#) ~coll)]
(.getCollection db# coll#) (let [query# (-> (empty-query *query-collection*) ~@body)]
coll#) (exec query#))))
query# (-> (empty-query db-coll#) ~@body)]
(exec query#)))
(defmacro partial-query (defmacro partial-query
[& body] [& body]

View file

@ -1,43 +1,9 @@
;; This source code is dual-licensed under the Apache License, version
;; 2.0, and the Eclipse Public License, version 1.0.
;;
;; The APL v2.0:
;;
;; ----------------------------------------------------------------------------------
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team
;;
;; Licensed under the Apache License, Version 2.0 (the "License");
;; you may not use this file except in compliance with the License.
;; You may obtain a copy of the License at
;;
;; http://www.apache.org/licenses/LICENSE-2.0
;;
;; Unless required by applicable law or agreed to in writing, software
;; distributed under the License is distributed on an "AS IS" BASIS,
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;; See the License for the specific language governing permissions and
;; limitations under the License.
;; ----------------------------------------------------------------------------------
;;
;; The EPL v1.0:
;;
;; ----------------------------------------------------------------------------------
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team.
;; All rights reserved.
;;
;; This program and the accompanying materials are made available under the terms of
;; the Eclipse Public License Version 1.0,
;; which accompanies this distribution and is available at
;; http://www.eclipse.org/legal/epl-v10.html.
;; ----------------------------------------------------------------------------------
(ns monger.ragtime (ns monger.ragtime
"Ragtime integration"
(:refer-clojure :exclude [find sort]) (:refer-clojure :exclude [find sort])
(:require [ragtime.protocols :as proto] (:require [ragtime.core :as ragtime]
[monger.core :as mg] [monger.core :as mg]
[monger.collection :as mc] [monger.collection :as mc])
[monger.query :refer [with-collection find sort]]) (:use [monger.query :only [with-collection find sort]])
(:import java.util.Date (:import java.util.Date
[com.mongodb DB WriteConcern])) [com.mongodb DB WriteConcern]))
@ -46,20 +12,23 @@
migrations-collection "meta.migrations") migrations-collection "meta.migrations")
(extend-type com.mongodb.DB (extend-type com.mongodb.DB
proto/DataStore ragtime/Migratable
(add-migration-id [db id] (add-migration-id [db id]
(mc/insert db migrations-collection {:_id id :created_at (Date.)} WriteConcern/FSYNC_SAFE)) (mc/insert db migrations-collection {:_id id :created_at (Date.)} WriteConcern/FSYNC_SAFE))
(remove-migration-id [db id] (remove-migration-id [db id]
(mc/remove-by-id db migrations-collection id)) (mc/remove-by-id db migrations-collection id))
(applied-migration-ids [db] (applied-migration-ids [db]
(let [xs (with-collection db migrations-collection (mg/with-db db
(let [xs (with-collection migrations-collection
(find {}) (find {})
(sort {:created_at 1}))] (sort {:created_at 1}))]
(vec (map :_id xs))))) (set (map :_id xs))))))
(defn flush-migrations! (defn flush-migrations!
"REMOVES all the information about previously performed migrations" "REMOVES all the information about previously performed migrations"
[^DB db] [db]
(mc/remove db migrations-collection)) (mg/with-db db
(mc/remove migrations-collection)))

View file

@ -1,38 +1,13 @@
;; This source code is dual-licensed under the Apache License, version ;; Copyright (c) 2011-2012 Michael S. Klishin
;; 2.0, and the Eclipse Public License, version 1.0.
;; ;;
;; The APL v2.0: ;; The use and distribution terms for this software are covered by the
;; ;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
;; ---------------------------------------------------------------------------------- ;; which can be found in the file epl-v10.html at the root of this distribution.
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team ;; By using this software in any fashion, you are agreeing to be bound by
;; ;; the terms of this license.
;; Licensed under the Apache License, Version 2.0 (the "License"); ;; You must not remove this notice, or any other, from this software.
;; you may not use this file except in compliance with the License.
;; You may obtain a copy of the License at
;;
;; http://www.apache.org/licenses/LICENSE-2.0
;;
;; Unless required by applicable law or agreed to in writing, software
;; distributed under the License is distributed on an "AS IS" BASIS,
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;; See the License for the specific language governing permissions and
;; limitations under the License.
;; ----------------------------------------------------------------------------------
;;
;; The EPL v1.0:
;;
;; ----------------------------------------------------------------------------------
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team.
;; All rights reserved.
;;
;; This program and the accompanying materials are made available under the terms of
;; the Eclipse Public License Version 1.0,
;; which accompanies this distribution and is available at
;; http://www.eclipse.org/legal/epl-v10.html.
;; ----------------------------------------------------------------------------------
(ns monger.result (ns ^{:doc "Provides functions that determine if a query (or other database operation)
"Provides functions that determine if a query (or other database operation)
was successful or not. was successful or not.
Related documentation guides: Related documentation guides:
@ -40,33 +15,64 @@
* http://clojuremongodb.info/articles/inserting.html * http://clojuremongodb.info/articles/inserting.html
* http://clojuremongodb.info/articles/updating.html * http://clojuremongodb.info/articles/updating.html
* http://clojuremongodb.info/articles/commands.html * http://clojuremongodb.info/articles/commands.html
* http://clojuremongodb.info/articles/mapreduce.html" * http://clojuremongodb.info/articles/mapreduce.html"}
(:import [com.mongodb WriteResult CommandResult]) monger.result
(:import [com.mongodb DBObject WriteResult MapReduceOutput]
clojure.lang.IPersistentMap)
(:require monger.conversion)) (:require monger.conversion))
;;
;; Implementation
;;
(defn- okayish?
[value]
(contains? #{true "true" 1 1.0} value))
;; ;;
;; API ;; API
;; ;;
(defprotocol WriteResultPredicates (defprotocol MongoCommandResult
(acknowledged? [input] "Returns true if write result is a success") (ok? [input] "Returns true if command result is a success")
(updated-existing? [input] "Returns true if write result has updated an existing document")) (has-error? [input] "Returns true if command result indicates an error")
(updated-existing? [input] "Returns true if command result has `updatedExisting` field set to true"))
(extend-protocol MongoCommandResult
DBObject
(ok?
[^DBObject result]
(okayish? (.get result "ok")))
(has-error?
[^DBObject result]
;; yes, this is exactly the logic MongoDB Java driver uses.
(> (count (str (.get result "err"))) 0))
(updated-existing?
[^DBObject result]
(let [v ^Boolean (.get result "updatedExisting")]
(and v (Boolean/valueOf v))))
(extend-protocol WriteResultPredicates
WriteResult WriteResult
(acknowledged? (ok?
[^WriteResult result] [^WriteResult result]
(.wasAcknowledged result)) (and (not (nil? result)) (ok? (.getLastError result))))
(has-error?
[^WriteResult result]
(has-error? (.getLastError result)))
(updated-existing? (updated-existing?
[^WriteResult result] [^WriteResult result]
(.isUpdateOfExisting result)) (updated-existing? (.getLastError result)))
CommandResult MapReduceOutput
(acknowledged? (ok?
[^CommandResult result] [^MapReduceOutput result]
(.ok result))) (ok? ^DBObject (.getRaw result)))
(defn affected-count IPersistentMap
"Get the number of documents affected" (ok?
[^WriteResult result] [^IPersistentMap m]
(.getN result)) (okayish? (or (get m :ok)
(get m "ok")))))

View file

@ -1,43 +1,8 @@
;; This source code is dual-licensed under the Apache License, version
;; 2.0, and the Eclipse Public License, version 1.0.
;;
;; The APL v2.0:
;;
;; ----------------------------------------------------------------------------------
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team
;;
;; Licensed under the Apache License, Version 2.0 (the "License");
;; you may not use this file except in compliance with the License.
;; You may obtain a copy of the License at
;;
;; http://www.apache.org/licenses/LICENSE-2.0
;;
;; Unless required by applicable law or agreed to in writing, software
;; distributed under the License is distributed on an "AS IS" BASIS,
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;; See the License for the specific language governing permissions and
;; limitations under the License.
;; ----------------------------------------------------------------------------------
;;
;; The EPL v1.0:
;;
;; ----------------------------------------------------------------------------------
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team.
;; All rights reserved.
;;
;; This program and the accompanying materials are made available under the terms of
;; the Eclipse Public License Version 1.0,
;; which accompanies this distribution and is available at
;; http://www.eclipse.org/legal/epl-v10.html.
;; ----------------------------------------------------------------------------------
(ns monger.ring.session-store (ns monger.ring.session-store
(:require [ring.middleware.session.store :as ringstore] (:require [ring.middleware.session.store :as ringstore]
[monger.collection :as mc] [monger.collection :as mc])
[monger.core :as mg] (:use monger.conversion)
[monger.conversion :refer :all])
(:import [java.util UUID Date] (:import [java.util UUID Date]
[com.mongodb DB]
ring.middleware.session.store.SessionStore)) ring.middleware.session.store.SessionStore))
;; ;;
@ -58,10 +23,10 @@
;; data structure Clojure reader can serialize and read but won't make the data useful to applications ;; data structure Clojure reader can serialize and read but won't make the data useful to applications
;; in other languages. ;; in other languages.
(defrecord ClojureReaderBasedMongoDBSessionStore [^DB db ^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.Writer out] [d out]
(.write out (.write out
(str "#=" (str "#="
`(java.util.Date. ~(.getYear d) `(java.util.Date. ~(.getYear d)
@ -72,7 +37,7 @@
~(.getSeconds d))))) ~(.getSeconds d)))))
(defmethod print-dup org.bson.types.ObjectId (defmethod print-dup org.bson.types.ObjectId
[oid ^java.io.Writer out] [oid out]
(.write out (.write out
(str "#=" (str "#="
`(org.bson.types.ObjectId. ~(str oid))))) `(org.bson.types.ObjectId. ~(str oid)))))
@ -83,7 +48,7 @@
(read-session [store key] (read-session [store key]
(if key (if key
(if-let [m (mc/find-one-as-map (.db store) (.collection-name store) {:_id key})] (if-let [m (mc/find-one-as-map (.collection-name store) {:_id key})]
(read-string (:value m)) (read-string (:value m))
{}) {})
{})) {}))
@ -93,43 +58,47 @@
key (or key (str (UUID/randomUUID))) key (or key (str (UUID/randomUUID)))
value (binding [*print-dup* true] value (binding [*print-dup* true]
(pr-str (assoc data :_id key)))] (pr-str (assoc data :_id key)))]
(mc/save (.db store) (.collection-name store) {:_id key :value value :date date}) (mc/save (.collection-name store) {:_id key :value value :date date})
key)) key))
(delete-session [store key] (delete-session [store key]
(mc/remove-by-id (.db store) (.collection-name store) key) (mc/remove-by-id (.collection-name store) key)
nil)) nil))
(defn session-store (defn session-store
[^DB db ^String s] ([]
(ClojureReaderBasedMongoDBSessionStore. db s)) (ClojureReaderBasedMongoDBSessionStore. default-session-store-collection))
([^String s]
(ClojureReaderBasedMongoDBSessionStore. s)))
;; this session store won't store namespaced keywords correctly but stores results in a way ;; this session store won't store namespaced keywords correctly but stores results in a way
;; that applications in other languages can read. DO NOT use it with Friend. ;; that applications in other languages can read. DO NOT use it with Friend.
(defrecord MongoDBSessionStore [^DB db ^String collection-name]) (defrecord MongoDBSessionStore [^String collection-name])
(extend-protocol ringstore/SessionStore (extend-protocol ringstore/SessionStore
MongoDBSessionStore MongoDBSessionStore
(read-session [store key] (read-session [store key]
(if-let [m (and key (if-let [m (and key
(mc/find-one-as-map (.db store) (.collection-name store) {:_id key}))] (mc/find-one-as-map (.collection-name store) {:_id key}))]
m m
{})) {}))
(write-session [store key data] (write-session [store key data]
(let [key (or key (str (UUID/randomUUID)))] (let [key (or key (str (UUID/randomUUID)))]
(mc/save (.db store) (.collection-name store) (assoc data :date (Date.) :_id key)) (mc/save (.collection-name store) (assoc data :date (Date.) :_id key))
key)) key))
(delete-session [store key] (delete-session [store key]
(mc/remove-by-id (.db store) (.collection-name store) key) (mc/remove-by-id (.collection-name store) key)
nil)) nil))
(defn monger-store (defn monger-store
[^DB db ^String s] ([]
(MongoDBSessionStore. db s)) (MongoDBSessionStore. default-session-store-collection))
([^String s]
(MongoDBSessionStore. s)))

View file

@ -0,0 +1,147 @@
;; Copyright (c) 2011-2012 Michael S. Klishin
;;
;; The use and distribution terms for this software are covered by the
;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
;; which can be found in the file epl-v10.html at the root of this distribution.
;; By using this software in any fashion, you are agreeing to be bound by
;; the terms of this license.
;; You must not remove this notice, or any other, from this software.
(ns ^{:doc "Monger TestKit is an experiment that turned out to be partially successful but partially need to be
rethough, redesigned, integrated with MongoDB DB references and simply reimplemented from the ground up
one more time. For this exact reason, there is no documentation guide on it.
Please keep this in mind if you are considering using it."}
monger.testkit
(:require [monger.collection :as mc]
[monger.result :as mr])
(:use [monger.internal.fn :only (expand-all expand-all-with) :as fntools])
(:import org.bson.types.ObjectId))
;;
;; API
;;
(defmacro defcleaner
"Defines a fixture function that removes all documents from a collection. If collection is not specified,
a conventionally named var will be used. Supposed to be used with clojure.test/use-fixtures but may
be useful on its own.
Examples:
(defcleaner events) ;; collection name will be taken from the events-collection var
(defcleaner people \"accounts\") ;; collection name is given
"
[entities & coll-name]
(let [coll-arg (if coll-name
(str (first coll-name))
(symbol (str entities "-collection")))
fn-name (symbol (str "purge-" entities))]
`(defn ~fn-name
[f#]
(mc/remove ~coll-arg)
(f#)
(mc/remove ~coll-arg))))
(def factories (atom {}))
(def defaults (atom {}))
(def last-oids (atom {}))
(defn defaults-for
[f-group & { :as attributes }]
(swap! defaults (fn [v]
(assoc v (name f-group) attributes))))
(defn factory
[f-group f-name & { :as attributes }]
(swap! factories (fn [a]
(assoc-in a [(name f-group) (name f-name)] attributes))))
(declare build seed remember-oid)
(defn- expand-associate-for-building
[f]
(let [mt (meta f)
[f-group f-name] (f)]
(:_id (build f-group f-name))))
(defn- expand-for-building
"Expands functions, treating those with association metadata (see `parent-id` for example) specially"
[f]
(let [mt (meta f)]
(if (:associate-gen mt)
(expand-associate-for-building f)
(f))))
(defn- expand-associate-for-seeding
[f]
(let [mt (meta f)
[f-group f-name] (f)]
(:_id (seed f-group f-name))))
(defn- expand-for-seeding
"Expands functions, treating those with association metadata (see `parent-id` for example) specially,
making sure parent documents are persisted first"
[f]
(let [mt (meta f)]
(if (:associate-gen mt)
(expand-associate-for-seeding f)
(f))))
(defn build
"Generates a new document and returns it.
Unless _id field is defined by the factory, it is generated."
[f-group f-name & { :as overrides }]
(let [d (@defaults (name f-group))
attributes (get-in @factories [(name f-group) (name f-name)])
merged (merge { :_id (ObjectId.) } d attributes overrides)]
(expand-all-with merged expand-for-building)))
(defn seed
"Generates and inserts a new document, then returns it.
Unless _id field is defined by the factory, it is generated."
[f-group f-name & { :as overrides }]
(io!
(let [d (@defaults (name f-group))
attributes (get-in @factories [(name f-group) (name f-name)])
merged (merge { :_id (ObjectId.) } d attributes overrides)
expanded (expand-all-with merged expand-for-seeding)]
(assert (mr/ok? (mc/insert f-group expanded)))
(remember-oid f-group f-name (:_id expanded))
expanded)))
(defn seed-all
"Seeds all fixtures in the given collection"
[f-group]
(io!
(let [xs (vec (keys (get @factories f-group)))]
(doseq [f-name xs]
(seed f-group f-name)))))
(defn embedded-doc
[f-group f-name & { :as overrides }]
(fn []
(apply build f-group f-name (flatten (vec overrides)))))
(defn parent-id
[f-group f-name]
(with-meta (fn []
[f-group f-name]) { :associate-gen true :parent-gen true }))
(defn- remember-oid
[f-group f-name oid]
(swap! last-oids (fn [a]
(assoc-in a [(name f-group) (name f-name)] oid))))
(defn last-oid-of
"Returns last object id of a document inserted using given factory"
[f-group f-name]
(get-in @last-oids [(name f-group) (name f-name)]))
(def ^{ :doc "Returns a new object id. Generates it if needed, otherwise returns a cached version.
Useful for defining referenced associations between fixture documents." }
memoized-oid (memoize (fn [f-group f-name]
(ObjectId.))))

View file

@ -1,45 +1,19 @@
;; This source code is dual-licensed under the Apache License, version ;; Copyright (c) 2011-2012 Michael S. Klishin
;; 2.0, and the Eclipse Public License, version 1.0.
;; ;;
;; The APL v2.0: ;; The use and distribution terms for this software are covered by the
;; ;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
;; ---------------------------------------------------------------------------------- ;; which can be found in the file epl-v10.html at the root of this distribution.
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team ;; By using this software in any fashion, you are agreeing to be bound by
;; ;; the terms of this license.
;; Licensed under the Apache License, Version 2.0 (the "License"); ;; You must not remove this notice, or any other, from this software.
;; you may not use this file except in compliance with the License.
;; You may obtain a copy of the License at
;;
;; http://www.apache.org/licenses/LICENSE-2.0
;;
;; Unless required by applicable law or agreed to in writing, software
;; distributed under the License is distributed on an "AS IS" BASIS,
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;; See the License for the specific language governing permissions and
;; limitations under the License.
;; ----------------------------------------------------------------------------------
;;
;; The EPL v1.0:
;;
;; ----------------------------------------------------------------------------------
;; Copyright (c) 2011-2018 Michael S. Klishin, Alex Petrov, and the ClojureWerkz Team.
;; All rights reserved.
;;
;; This program and the accompanying materials are made available under the terms of
;; the Eclipse Public License Version 1.0,
;; which accompanies this distribution and is available at
;; http://www.eclipse.org/legal/epl-v10.html.
;; ----------------------------------------------------------------------------------
(ns ^{:doc "Provides various utility functions, primarily for working with document ids."} monger.util (ns ^{:doc "Provides various utility functions, primarily for working with document ids."} monger.util
(:refer-clojure :exclude [random-uuid])
(:import java.security.SecureRandom (:import java.security.SecureRandom
java.math.BigInteger java.math.BigInteger
org.bson.types.ObjectId org.bson.types.ObjectId
com.mongodb.DBObject com.mongodb.DBObject
clojure.lang.IPersistentMap clojure.lang.IPersistentMap
java.util.Map) java.util.Map))
(:refer-clojure :exclude [random-uuid]))
;; ;;
;; API ;; API
@ -56,11 +30,9 @@
(.toString (new BigInteger n (SecureRandom.)) num-base)) (.toString (new BigInteger n (SecureRandom.)) num-base))
(defn ^ObjectId object-id (defn ^ObjectId object-id
"Returns a new BSON object id, or converts str to BSON object id" "Returns a new BSON object id"
([] []
(ObjectId.)) (ObjectId.))
([^String s]
(ObjectId. s)))
(defprotocol GetDocumentId (defprotocol GetDocumentId
(get-id [input] "Returns document id")) (get-id [input] "Returns document id"))
@ -75,8 +47,3 @@
(get-id (get-id
[^IPersistentMap object] [^IPersistentMap object]
(or (:_id object) (object "_id")))) (or (:_id object) (object "_id"))))
(defn into-array-list
"Coerce a j.u.Collection into a j.u.ArrayList."
^java.util.ArrayList [^java.util.Collection coll]
(java.util.ArrayList. coll))

View file

@ -0,0 +1,44 @@
package com.novemberain.monger;
import clojure.lang.IDeref;
import com.mongodb.DB;
import com.mongodb.DBObject;
import org.bson.BSONObject;
/**
* Exactly as com.mongodb.DBRef but also implements Clojure IDeref for @dereferencing
*/
public class DBRef extends com.mongodb.DBRef implements IDeref {
/**
* Creates a DBRef
* @param db the database
* @param o a BSON object representing the reference
*/
public DBRef(DB db, BSONObject o) {
super(db , o.get("$ref").toString(), o.get("$id"));
}
/**
* Creates a DBRef
* @param db the database
* @param ns the namespace where the object is stored
* @param id the object id
*/
public DBRef(DB db, String ns, Object id) {
super(db, ns, id);
}
/**
* Creates a DBRef from a com.mongodb.DBRef instance.
* @param source The original reference MongoDB Java driver uses
*/
public DBRef(com.mongodb.DBRef source) {
this(source.getDB(), source.getRef(), source.getId());
}
@Override
public DBObject deref() {
return this.fetch();
}
}

View file

@ -1,136 +1,69 @@
(ns monger.test.aggregation-framework-test (ns monger.test.aggregation-framework-test
(:require [monger.core :as mg] (:require monger.core [monger.collection :as mc]
[monger.collection :as mc] [monger.test.helper :as helper])
[clojure.test :refer :all] (:use clojure.test
[monger.operators :refer :all])) monger.operators
monger.test.fixtures))
(let [conn (mg/connect)
db (mg/get-db conn "monger-test")
coll "docs"]
(defn purge-collections
[f]
(mc/purge-many db [coll])
(f)
(mc/purge-many db [coll]))
(use-fixtures :each purge-collections) (helper/connect!)
(deftest test-basic-single-stage-$project-aggregation-no-keywordize (use-fixtures :each purge-docs)
(let [batch [{"state" "CA" "quantity" 1 "price" 199.00}
{"state" "NY" "quantity" 2 "price" 199.00}
{"state" "NY" "quantity" 1 "price" 299.00}
{"state" "IL" "quantity" 2 "price" 11.50 }
{"state" "CA" "quantity" 2 "price" 2.95 }
{"state" "IL" "quantity" 3 "price" 5.50 }]
expected #{{"quantity" 1 "state" "CA"}
{"quantity" 2 "state" "NY"}
{"quantity" 1 "state" "NY"}
{"quantity" 2 "state" "IL"}
{"quantity" 2 "state" "CA"}
{"quantity" 3 "state" "IL"}}]
(mc/insert-batch db coll batch)
(is (= 6 (mc/count db coll)))
(let [result (->>
(mc/aggregate db coll [{$project {"state" 1 "quantity" 1}}] :keywordize false)
(map #(select-keys % ["state" "quantity"]))
(set))]
(is (= expected result)))))
(deftest test-basic-single-stage-$project-aggregation (deftest ^{:edge-features true} test-basic-single-stage-$project-aggregation
(let [batch [{:state "CA" :quantity 1 :price 199.00} (let [collection "docs"
{:state "NY" :quantity 2 :price 199.00} batch [{ :state "CA" :quantity 1 :price 199.00 }
{:state "NY" :quantity 1 :price 299.00} { :state "NY" :quantity 2 :price 199.00 }
{:state "IL" :quantity 2 :price 11.50 } { :state "NY" :quantity 1 :price 299.00 }
{:state "CA" :quantity 2 :price 2.95 } { :state "IL" :quantity 2 :price 11.50 }
{:state "IL" :quantity 3 :price 5.50 }] { :state "CA" :quantity 2 :price 2.95 }
{ :state "IL" :quantity 3 :price 5.50 }]
expected #{{:quantity 1 :state "CA"} expected #{{:quantity 1 :state "CA"}
{:quantity 2 :state "NY"} {:quantity 2 :state "NY"}
{:quantity 1 :state "NY"} {:quantity 1 :state "NY"}
{:quantity 2 :state "IL"} {:quantity 2 :state "IL"}
{:quantity 2 :state "CA"} {:quantity 2 :state "CA"}
{:quantity 3 :state "IL"}}] {:quantity 3 :state "IL"}}]
(mc/insert-batch db coll batch) (mc/insert-batch collection batch)
(is (= 6 (mc/count db coll))) (is (= 6 (mc/count collection)))
(let [result (set (map #(select-keys % [:state :quantity]) (let [result (set (map #(select-keys % [:state :quantity])
(mc/aggregate db coll [{$project {:state 1 :quantity 1}}])))] (mc/aggregate "docs" [{$project {:state 1 :quantity 1}}])))]
(is (= expected result))))) (is (= expected result)))))
(deftest test-basic-projection-with-multiplication (deftest ^{:edge-features true} test-basic-projection-with-multiplication
(let [batch [{:state "CA" :quantity 1 :price 199.00} (let [collection "docs"
{:state "NY" :quantity 2 :price 199.00} batch [{ :state "CA" :quantity 1 :price 199.00 }
{:state "NY" :quantity 1 :price 299.00} { :state "NY" :quantity 2 :price 199.00 }
{:state "IL" :quantity 2 :price 11.50 } { :state "NY" :quantity 1 :price 299.00 }
{:state "CA" :quantity 2 :price 2.95 } { :state "IL" :quantity 2 :price 11.50 }
{:state "IL" :quantity 3 :price 5.50 }] { :state "CA" :quantity 2 :price 2.95 }
{ :state "IL" :quantity 3 :price 5.50 }]
expected #{{:_id "NY" :subtotal 398.0} expected #{{:_id "NY" :subtotal 398.0}
{:_id "NY" :subtotal 299.0} {:_id "NY" :subtotal 299.0}
{:_id "IL" :subtotal 23.0} {:_id "IL" :subtotal 23.0}
{:_id "CA" :subtotal 5.9} {:_id "CA" :subtotal 5.9}
{:_id "IL" :subtotal 16.5} {:_id "IL" :subtotal 16.5}
{:_id "CA" :subtotal 199.0}}] {:_id "CA" :subtotal 199.0}}]
(mc/insert-batch db coll batch) (mc/insert-batch collection batch)
(let [result (set (mc/aggregate db coll [{$project {:subtotal {$multiply ["$quantity", "$price"]} (let [result (set (mc/aggregate "docs" [{$project {:subtotal {$multiply ["$quantity", "$price"]}
:_id "$state"}}]))] :_id "$state"}}]))]
(is (= expected result))))) (is (= expected result)))))
(deftest test-basic-total-aggregation (deftest ^{:edge-features true} test-basic-total-aggregation
(let [batch [{:state "CA" :quantity 1 :price 199.00} (let [collection "docs"
{:state "NY" :quantity 2 :price 199.00} batch [{ :state "CA" :quantity 1 :price 199.00 }
{:state "NY" :quantity 1 :price 299.00} { :state "NY" :quantity 2 :price 199.00 }
{:state "IL" :quantity 2 :price 11.50 } { :state "NY" :quantity 1 :price 299.00 }
{:state "CA" :quantity 2 :price 2.95 } { :state "IL" :quantity 2 :price 11.50 }
{:state "IL" :quantity 3 :price 5.50 }] { :state "CA" :quantity 2 :price 2.95 }
{ :state "IL" :quantity 3 :price 5.50 }]
expected #{{:_id "CA" :total 204.9} {:_id "IL" :total 39.5} {:_id "NY" :total 697.0}}] expected #{{:_id "CA" :total 204.9} {:_id "IL" :total 39.5} {:_id "NY" :total 697.0}}]
(mc/insert-batch db coll batch) (mc/insert-batch collection batch)
(let [result (set (mc/aggregate db coll [{$project {:subtotal {$multiply ["$quantity", "$price"]} (let [result (set (mc/aggregate "docs" [{$project {:subtotal {$multiply ["$quantity", "$price"]}
:_id 1 :_id 1
:state 1}} :state 1}}
{$group {:_id "$state" {$group {:_id "$state"
:total {$sum "$subtotal"}}}]))] :total {$sum "$subtotal"}}}]))]
(is (= expected result))))) (is (= expected result)))))
(deftest test-$first-aggregation-operator
(let [batch [{:state "CA"}
{:state "IL"}]
expected "CA"]
(mc/insert-batch db coll batch)
(let [result (:state (first (mc/aggregate db coll [{$group {:_id 1 :state {$first "$state"}}}])))]
(is (= expected result)))))
(deftest test-$last-aggregation-operator
(let [batch [{:state "CA"}
{:state "IL"}]
expected "IL"]
(mc/insert-batch db coll batch)
(let [result (:state (first (mc/aggregate db coll [{$group {:_id 1 :state {$last "$state"}}}])))]
(is (= expected result)))))
(deftest test-cursor-aggregation
(let [batch [{:state "CA" :quantity 1 :price 199.00}
{:state "NY" :quantity 2 :price 199.00}
{:state "NY" :quantity 1 :price 299.00}
{:state "IL" :quantity 2 :price 11.50 }
{:state "CA" :quantity 2 :price 2.95 }
{:state "IL" :quantity 3 :price 5.50 }]
expected #{{:quantity 1 :state "CA"}
{:quantity 2 :state "NY"}
{:quantity 1 :state "NY"}
{:quantity 2 :state "IL"}
{:quantity 2 :state "CA"}
{:quantity 3 :state "IL"}}]
(mc/insert-batch db coll batch)
(is (= 6 (mc/count db coll)))
(let [result (set (map #(select-keys % [:state :quantity])
(mc/aggregate db coll [{$project {:state 1 :quantity 1}}] :cursor {:batch-size 10})))]
(is (= expected result)))))
(deftest test-explain-aggregate
(let [batch [{:state "CA" :price 100}
{:state "CA" :price 10}
{:state "IL" :price 50}]]
(mc/insert-batch db coll batch)
(let [result (mc/explain-aggregate db coll [{$match {:state "CA"}}])]
(is (:ok result))))))

View file

@ -1,461 +1,348 @@
(set! *warn-on-reflection* true)
(ns monger.test.atomic-modifiers-test (ns monger.test.atomic-modifiers-test
(:import [com.mongodb WriteResult WriteConcern DBObject] (:import [com.mongodb WriteResult WriteConcern DBCursor DBObject CommandResult$CommandFailure]
org.bson.types.ObjectId [org.bson.types ObjectId]
java.util.Date) [java.util Date])
(:require [monger.core :as mg] (:require [monger core util]
[monger.collection :as mc] [monger.collection :as mgcol]
[monger.result :refer [acknowledged?]] [monger.result :as mgres]
[clojure.test :refer :all] [monger.test.helper :as helper])
[monger.operators :refer :all])) (:use [clojure.test]
[monger.operators]
[monger.test.fixtures]))
(helper/connect!)
(use-fixtures :each purge-docs purge-things purge-scores)
(let [conn (mg/connect) ;;
db (mg/get-db conn "monger-test")] ;; $inc
;;
(defn purge-collections (deftest increment-a-single-existing-field-using-$inc-modifier
[f]
(mc/remove db "docs")
(mc/remove db "things")
(mc/remove db "scores")
(f)
(mc/remove db "docs")
(mc/remove db "things")
(mc/remove db "scores"))
(use-fixtures :each purge-collections)
;;
;; $inc
;;
(deftest increment-a-single-existing-field-using-$inc-modifier
(let [coll "scores" (let [coll "scores"
oid (ObjectId.)] oid (ObjectId.)]
(mc/insert db coll {:_id oid :username "l33r0y" :score 100}) (mgcol/insert coll { :_id oid :username "l33r0y" :score 100 })
(mc/update db coll {:_id oid} {$inc {:score 20}}) (mgcol/update coll { :_id oid } { $inc { :score 20 } })
(is (= 120 (:score (mc/find-map-by-id db coll oid)))))) (is (= 120 (:score (mgcol/find-map-by-id coll oid))))))
(deftest set-a-single-non-existing-field-using-$inc-modifier (deftest set-a-single-non-existing-field-using-$inc-modifier
(let [coll "scores" (let [coll "scores"
oid (ObjectId.)] oid (ObjectId.)]
(mc/insert db coll {:_id oid :username "l33r0y"}) (mgcol/insert coll { :_id oid :username "l33r0y" })
(mc/update db coll {:_id oid} {$inc {:score 30}}) (mgcol/update coll { :_id oid } { $inc { :score 30 } })
(is (= 30 (:score (mc/find-map-by-id db coll oid)))))) (is (= 30 (:score (mgcol/find-map-by-id coll oid))))))
(deftest increment-multiple-existing-fields-using-$inc-modifier (deftest increment-multiple-existing-fields-using-$inc-modifier
(let [coll "scores" (let [coll "scores"
oid (ObjectId.)] oid (ObjectId.)]
(mc/insert db coll {:_id oid :username "l33r0y" :score 100 :bonus 0}) (mgcol/insert coll { :_id oid :username "l33r0y" :score 100 :bonus 0 })
(mc/update db coll {:_id oid} {$inc {:score 20 :bonus 10}}) (mgcol/update coll { :_id oid } {$inc { :score 20 :bonus 10 } })
(is (= {:_id oid :score 120 :bonus 10 :username "l33r0y"} (is (= { :_id oid :score 120 :bonus 10 :username "l33r0y" } (mgcol/find-map-by-id coll oid)))))
(mc/find-map-by-id db coll oid)))))
(deftest increment-and-set-multiple-existing-fields-using-$inc-modifier (deftest increment-and-set-multiple-existing-fields-using-$inc-modifier
(let [coll "scores" (let [coll "scores"
oid (ObjectId.)] oid (ObjectId.)]
(mc/insert db coll {:_id oid :username "l33r0y" :score 100}) (mgcol/insert coll { :_id oid :username "l33r0y" :score 100 })
(mc/update db coll {:_id oid} {$inc {:score 20 :bonus 10}}) (mgcol/update coll { :_id oid } { $inc { :score 20 :bonus 10 } })
(is (= {:_id oid :score 120 :bonus 10 :username "l33r0y"} (is (= { :_id oid :score 120 :bonus 10 :username "l33r0y" } (mgcol/find-map-by-id coll oid)))))
(mc/find-map-by-id db coll oid)))))
;; ;;
;; $set ;; $set
;; ;;
(deftest update-a-single-existing-field-using-$set-modifier (deftest update-a-single-existing-field-using-$set-modifier
(let [coll "things" (let [coll "things"
oid (ObjectId.)] oid (ObjectId.)]
(mc/insert db coll {:_id oid :weight 10.0}) (mgcol/insert coll { :_id oid :weight 10.0 })
(mc/update db coll {:_id oid} {$set {:weight 20.5}}) (mgcol/update coll { :_id oid } { $set { :weight 20.5 } })
(is (= 20.5 (:weight (mc/find-map-by-id db coll oid [:weight])))))) (is (= 20.5 (:weight (mgcol/find-map-by-id coll oid [:weight]))))))
(deftest set-a-single-non-existing-field-using-$set-modifier (deftest set-a-single-non-existing-field-using-$set-modifier
(let [coll "things" (let [coll "things"
oid (ObjectId.)] oid (ObjectId.)]
(mc/insert db coll {:_id oid :weight 10.0}) (mgcol/insert coll { :_id oid :weight 10.0 })
(mc/update db coll {:_id oid} {$set {:height 17.2}}) (mgcol/update coll { :_id oid } { $set { :height 17.2 } })
(is (= 17.2 (:height (mc/find-map-by-id db coll oid [:height])))))) (is (= 17.2 (:height (mgcol/find-map-by-id coll oid [:height]))))))
(deftest update-multiple-existing-fields-using-$set-modifier (deftest update-multiple-existing-fields-using-$set-modifier
(let [coll "things" (let [coll "things"
oid (ObjectId.)] oid (ObjectId.)]
(mc/insert db coll {:_id oid :weight 10.0 :height 15.2}) (mgcol/insert coll { :_id oid :weight 10.0 :height 15.2 })
(mc/update db coll {:_id oid} {$set {:weight 20.5 :height 25.6}}) (mgcol/update coll { :_id oid } { $set { :weight 20.5 :height 25.6 } })
(is (= {:_id oid :weight 20.5 :height 25.6} (is (= { :_id oid :weight 20.5 :height 25.6 } (mgcol/find-map-by-id coll oid [:weight :height])))))
(mc/find-map-by-id db coll oid [:weight :height])))))
(deftest update-and-set-multiple-fields-using-$set-modifier (deftest update-and-set-multiple-fields-using-$set-modifier
(let [coll "things" (let [coll "things"
oid (ObjectId.)] oid (ObjectId.)]
(mc/insert db coll {:_id oid :weight 10.0}) (mgcol/insert coll { :_id oid :weight 10.0 })
(mc/update db coll {:_id oid} {$set {:weight 20.5 :height 25.6}}) (mgcol/update coll { :_id oid } {$set { :weight 20.5 :height 25.6 } })
(is (= {:_id oid :weight 20.5 :height 25.6} (is (= { :_id oid :weight 20.5 :height 25.6 } (mgcol/find-map-by-id coll oid [:weight :height])))))
(mc/find-map-by-id db coll oid [:weight :height])))))
;; ;;
;; $unset ;; $unset
;; ;;
(deftest unset-a-single-existing-field-using-$unset-modifier (deftest unset-a-single-existing-field-using-$unset-modifier
(let [coll "docs" (let [coll "docs"
oid (ObjectId.)] oid (ObjectId.)]
(mc/insert db coll {:_id oid :title "Document 1" :published true}) (mgcol/insert coll { :_id oid :title "Document 1" :published true })
(mc/update db coll {:_id oid} {$unset {:published 1}}) (mgcol/update coll { :_id oid } { $unset { :published 1 } })
(is (= {:_id oid :title "Document 1"} (is (= { :_id oid :title "Document 1" } (mgcol/find-map-by-id coll oid)))))
(mc/find-map-by-id db coll oid)))))
(deftest unset-multiple-existing-fields-using-$unset-modifier (deftest unset-multiple-existing-fields-using-$unset-modifier
(let [coll "docs" (let [coll "docs"
oid (ObjectId.)] oid (ObjectId.)]
(mc/insert db coll {:_id oid :title "Document 1" :published true :featured true}) (mgcol/insert coll { :_id oid :title "Document 1" :published true :featured true })
(mc/update db coll {:_id oid} {$unset {:published 1 :featured true}}) (mgcol/update coll { :_id oid } { $unset { :published 1 :featured true } })
(is (= {:_id oid :title "Document 1"} (is (= { :_id oid :title "Document 1" } (mgcol/find-map-by-id coll oid)))))
(mc/find-map-by-id db coll oid)))))
(deftest unsetting-an-unexisting-field-using-$unset-modifier-is-not-considered-an-issue (deftest unsetting-an-unexisting-field-using-$unset-modifier-is-not-considered-an-issue
(let [coll "docs" (let [coll "docs"
oid (ObjectId.)] oid (ObjectId.)]
(mc/insert db coll {:_id oid :title "Document 1" :published true}) (mgcol/insert coll { :_id oid :title "Document 1" :published true })
(is (acknowledged? (mc/update db coll {:_id oid} {$unset {:published 1 :featured true}}))) (is (mgres/ok? (mgcol/update coll { :_id oid } { $unset { :published 1 :featured true } })))
(is (= {:_id oid :title "Document 1"} (is (= { :_id oid :title "Document 1" } (mgcol/find-map-by-id coll oid)))))
(mc/find-map-by-id db coll oid)))))
;;
;; $setOnInsert
;;
(deftest setOnInsert-in-upsert-for-non-existing-document ;;
(let [coll "docs" ;; $push
now 456 ;;
oid (ObjectId.)]
(mc/find-and-modify db coll {:_id oid} {$set {:lastseen now} $setOnInsert {:firstseen now}} {:upsert true})
(is (= {:_id oid :lastseen now :firstseen now}
(mc/find-map-by-id db coll oid)))))
(deftest setOnInsert-in-upsert-for-existing-document (deftest initialize-an-array-using-$push-modifier
(let [coll "docs"
before 123
now 456
oid (ObjectId.)]
(mc/insert db coll {:_id oid :firstseen before :lastseen before})
(mc/find-and-modify db coll {:_id oid} {$set {:lastseen now} $setOnInsert {:firstseen now}} {:upsert true})
(is (= {:_id oid :lastseen now :firstseen before}
(mc/find-map-by-id db coll oid)))))
;;
;; $push
;;
(deftest initialize-an-array-using-$push-modifier
(let [coll "docs" (let [coll "docs"
oid (ObjectId.) oid (ObjectId.)
title "$push modifier appends value to field"] title "$push modifier appends value to field"]
(mc/insert db coll {:_id oid :title title}) (mgcol/insert coll { :_id oid :title title })
(mc/update db coll {:_id oid} {$push {:tags "modifiers"}}) (mgcol/update coll { :_id oid } { $push { :tags "modifiers" } })
(is (= {:_id oid :title title :tags ["modifiers"]} (is (= { :_id oid :title title :tags ["modifiers"] } (mgcol/find-map-by-id coll oid)))))
(mc/find-map-by-id db coll oid)))))
(deftest add-value-to-an-existing-array-using-$push-modifier (deftest add-value-to-an-existing-array-using-$push-modifier
(let [coll "docs" (let [coll "docs"
oid (ObjectId.) oid (ObjectId.)
title "$push modifier appends value to field"] title "$push modifier appends value to field"]
(mc/insert db coll {:_id oid :title title :tags ["mongodb"]}) (mgcol/insert coll { :_id oid :title title :tags ["mongodb"] })
(mc/update db coll {:_id oid} {$push {:tags "modifiers"}}) (mgcol/update coll { :_id oid } { $push { :tags "modifiers" } })
(is (= {:_id oid :title title :tags ["mongodb" "modifiers"]} (is (= { :_id oid :title title :tags ["mongodb" "modifiers"] } (mgcol/find-map-by-id coll oid)))))
(mc/find-map-by-id db coll oid)))))
;; this is a common mistake, I leave it here to demonstrate it. You almost never ;; this is a common mistake, I leave it here to demonstrate it. You almost never
;; actually want to do this! What you really want is to use $push with $each instead of $push. MK. ;; actually want to do this! What you really want is to use $pushAll instead of $push. MK.
(deftest add-array-value-to-an-existing-array-using-$push-modifier (deftest add-array-value-to-an-existing-array-using-$push-modifier
(let [coll "docs" (let [coll "docs"
oid (ObjectId.) oid (ObjectId.)
title "$push modifier appends value to field"] title "$push modifier appends value to field"]
(mc/insert db coll {:_id oid :title title :tags ["mongodb"]}) (mgcol/insert coll { :_id oid :title title :tags ["mongodb"] })
(mc/update db coll {:_id oid} {$push {:tags ["modifiers" "operators"]}}) (mgcol/update coll { :_id oid } { $push { :tags ["modifiers" "operators"] } })
(is (= {:_id oid :title title :tags ["mongodb" ["modifiers" "operators"]]} (is (= { :_id oid :title title :tags ["mongodb" ["modifiers" "operators"]] } (mgcol/find-map-by-id coll oid)))))
(mc/find-map-by-id db coll oid)))))
(deftest double-add-value-to-an-existing-array-using-$push-modifier (deftest double-add-value-to-an-existing-array-using-$push-modifier
(let [coll "docs" (let [coll "docs"
oid (ObjectId.) oid (ObjectId.)
title "$push modifier appends value to field"] title "$push modifier appends value to field"]
(mc/insert db coll {:_id oid :title title :tags ["mongodb"]}) (mgcol/insert coll { :_id oid :title title :tags ["mongodb"] })
(mc/update db coll {:_id oid} {$push {:tags "modifiers"}}) (mgcol/update coll { :_id oid } { $push { :tags "modifiers" } })
(mc/update db coll {:_id oid} {$push {:tags "modifiers"}}) (mgcol/update coll { :_id oid } { $push { :tags "modifiers" } })
(is (= {:_id oid :title title :tags ["mongodb" "modifiers" "modifiers"]} (is (= { :_id oid :title title :tags ["mongodb" "modifiers" "modifiers"] } (mgcol/find-map-by-id coll oid)))))
(mc/find-map-by-id db coll oid)))))
;; ;;
;; $push $each ;; $pushAll
;; ;;
(deftest initialize-an-array-using-$push-$each-modifier (deftest initialize-an-array-using-$pushAll-modifier
(let [coll "docs"
oid (ObjectId.)
title "$push with $each modifier appends multiple values to field"]
(mc/insert db coll {:_id oid :title title})
(mc/update db coll {:_id oid} {$push {:tags {$each ["mongodb" "docs"]}}})
(is (= {:_id oid :title title :tags ["mongodb" "docs"]}
(mc/find-map-by-id db coll oid)))))
(deftest add-values-to-an-existing-array-using-$push-$each-modifier
(let [coll "docs"
oid (ObjectId.)
title "$push with $each modifier appends multiple values to field"]
(mc/insert db coll {:_id oid :title title :tags ["mongodb"]})
(mc/update db coll {:_id oid} {$push {:tags {$each ["modifiers" "docs"]}}})
(is (= {:_id oid :title title :tags ["mongodb" "modifiers" "docs"]}
(mc/find-map-by-id db coll oid)))))
(deftest double-add-value-to-an-existing-array-using-$push-$each-modifier
(let [coll "docs"
oid (ObjectId.)
title "$push with $each modifier appends multiple values to field"]
(mc/insert db coll {:_id oid :title title :tags ["mongodb" "docs"]})
(mc/update db coll {:_id oid} {$push {:tags {$each ["modifiers" "docs"]}}})
(is (= {:_id oid :title title :tags ["mongodb" "docs" "modifiers" "docs"]}
(mc/find-map-by-id db coll oid)))))
;;
;; $push + $each (formerly $pushAll)
;;
(deftest initialize-an-array-using-$push-and-$each-modifiers
(let [coll "docs" (let [coll "docs"
oid (ObjectId.) oid (ObjectId.)
title "$pushAll modifier appends multiple values to field"] title "$pushAll modifier appends multiple values to field"]
(mc/insert db coll {:_id oid :title title}) (mgcol/insert coll { :_id oid :title title })
(mc/update db coll {:_id oid} {$push {:tags {$each ["mongodb" "docs"]}}}) (mgcol/update coll { :_id oid } { $pushAll { :tags ["mongodb" "docs"] } })
(is (= {:_id oid :title title :tags ["mongodb" "docs"]} (is (= { :_id oid :title title :tags ["mongodb" "docs"] } (mgcol/find-map-by-id coll oid)))))
(mc/find-map-by-id db coll oid)))))
(deftest add-value-to-an-existing-array-using-$push-and-$each-modifier (deftest add-value-to-an-existing-array-using-$pushAll-modifier
(let [coll "docs" (let [coll "docs"
oid (ObjectId.) oid (ObjectId.)
title "$pushAll modifier appends multiple values to field"] title "$pushAll modifier appends multiple values to field"]
(mc/insert db coll {:_id oid :title title :tags ["mongodb"]}) (mgcol/insert coll { :_id oid :title title :tags ["mongodb"] })
(mc/update db coll {:_id oid} {$push {:tags {$each ["modifiers" "docs"]}}}) (mgcol/update coll { :_id oid } { $pushAll { :tags ["modifiers" "docs"] } })
(is (= {:_id oid :title title :tags ["mongodb" "modifiers" "docs"]} (is (= { :_id oid :title title :tags ["mongodb" "modifiers" "docs"] } (mgcol/find-map-by-id coll oid)))))
(mc/find-map-by-id db coll oid)))))
(deftest double-add-value-to-an-existing-array-using-$push-and-$each-modifier (deftest double-add-value-to-an-existing-array-using-$pushAll-modifier
(let [coll "docs" (let [coll "docs"
oid (ObjectId.) oid (ObjectId.)
title "$pushAll modifier appends multiple values to field"] title "$pushAll modifier appends multiple values to field"]
(mc/insert db coll {:_id oid :title title :tags ["mongodb" "docs"]}) (mgcol/insert coll { :_id oid :title title :tags ["mongodb" "docs"] })
(mc/update db coll {:_id oid} {$push {:tags {$each ["modifiers" "docs"]}}}) (mgcol/update coll { :_id oid } { $pushAll { :tags ["modifiers" "docs"] } })
(is (= {:_id oid :title title :tags ["mongodb" "docs" "modifiers" "docs"]} (is (= { :_id oid :title title :tags ["mongodb" "docs" "modifiers" "docs"] } (mgcol/find-map-by-id coll oid)))))
(mc/find-map-by-id db coll oid)))))
;; ;;
;; $addToSet ;; $addToSet
;; ;;
(deftest initialize-an-array-using-$addToSet-modifier (deftest initialize-an-array-using-$addToSet-modifier
(let [coll "docs" (let [coll "docs"
oid (ObjectId.) oid (ObjectId.)
title "$addToSet modifier appends value to field unless it is already there"] title "$addToSet modifier appends value to field unless it is already there"]
(mc/insert db coll {:_id oid :title title}) (mgcol/insert coll { :_id oid :title title })
(mc/update db coll {:_id oid} {$addToSet {:tags "modifiers"}}) (mgcol/update coll { :_id oid } { $addToSet { :tags "modifiers" } })
(is (= {:_id oid :title title :tags ["modifiers"]} (is (= { :_id oid :title title :tags ["modifiers"] } (mgcol/find-map-by-id coll oid)))))
(mc/find-map-by-id db coll oid)))))
(deftest add-value-to-an-existing-array-using-$addToSet-modifier (deftest add-value-to-an-existing-array-using-$addToSet-modifier
(let [coll "docs" (let [coll "docs"
oid (ObjectId.) oid (ObjectId.)
title "$addToSet modifier appends value to field unless it is already there"] title "$addToSet modifier appends value to field unless it is already there"]
(mc/insert db coll {:_id oid :title title :tags ["mongodb"]}) (mgcol/insert coll { :_id oid :title title :tags ["mongodb"] })
(mc/update db coll {:_id oid} {$addToSet {:tags "modifiers"}}) (mgcol/update coll { :_id oid } { $addToSet { :tags "modifiers" } })
(is (= {:_id oid :title title :tags ["mongodb" "modifiers"]} (is (= { :_id oid :title title :tags ["mongodb" "modifiers"] } (mgcol/find-map-by-id coll oid)))))
(mc/find-map-by-id db coll oid)))))
(deftest double-add-value-to-an-existing-array-using-$addToSet-modifier (deftest double-add-value-to-an-existing-array-using-$addToSet-modifier
(let [coll "docs" (let [coll "docs"
oid (ObjectId.) oid (ObjectId.)
title "$addToSet modifier appends value to field unless it is already there"] title "$addToSet modifier appends value to field unless it is already there"]
(mc/insert db coll {:_id oid :title title :tags ["mongodb"]}) (mgcol/insert coll { :_id oid :title title :tags ["mongodb"] })
(mc/update db coll {:_id oid} {$addToSet {:tags "modifiers"}}) (mgcol/update coll { :_id oid } { $addToSet { :tags "modifiers" } })
(mc/update db coll {:_id oid} {$addToSet {:tags "modifiers"}}) (mgcol/update coll { :_id oid } { $addToSet { :tags "modifiers" } })
(is (= {:_id oid :title title :tags ["mongodb" "modifiers"]} (is (= { :_id oid :title title :tags ["mongodb" "modifiers"] } (mgcol/find-map-by-id coll oid)))))
(mc/find-map-by-id db coll oid)))))
;;
;; $addToSet $each
;;
(deftest initialize-an-array-using-$addToSet-$each-modifier ;;
(let [coll "docs" ;; $pop
oid (ObjectId.) ;;
title "$addToSet with $each modifier appends multiple values to field unless they are already there"]
(mc/insert db coll {:_id oid :title title})
(mc/update db coll {:_id oid} {$addToSet {:tags {$each ["mongodb" "docs"]}}})
(is (= {:_id oid :title title :tags ["mongodb" "docs"]}
(mc/find-map-by-id db coll oid)))))
(deftest add-values-to-an-existing-array-using-$addToSet-$each-modifier (deftest pop-last-value-in-the-array-using-$pop-modifier
(let [coll "docs"
oid (ObjectId.)
title "$addToSet with $each modifier appends multiple values to field unless they are already there"]
(mc/insert db coll {:_id oid :title title :tags ["mongodb"]})
(mc/update db coll {:_id oid} {$addToSet {:tags {$each ["modifiers" "docs"]}}})
(is (= {:_id oid :title title :tags ["mongodb" "modifiers" "docs"]}
(mc/find-map-by-id db coll oid)))))
(deftest double-add-value-to-an-existing-array-using-$addToSet-$each-modifier
(let [coll "docs"
oid (ObjectId.)
title "$addToSet with $each modifier appends multiple values to field unless they are already there"]
(mc/insert db coll {:_id oid :title title :tags ["mongodb" "docs"]})
(mc/update db coll {:_id oid} {$addToSet {:tags {$each ["modifiers" "docs" "operators"]}}})
(is (= {:_id oid :title title :tags ["mongodb" "docs" "modifiers" "operators"]}
(mc/find-map-by-id db coll oid)))))
;;
;; $pop
;;
(deftest pop-last-value-in-the-array-using-$pop-modifier
(let [coll "docs" (let [coll "docs"
oid (ObjectId.) oid (ObjectId.)
title "$pop modifier removes last or first value in the array"] title "$pop modifier removes last or first value in the array"]
(mc/insert db coll {:_id oid :title title :tags ["products" "apple" "reviews"]}) (mgcol/insert coll { :_id oid :title title :tags ["products" "apple" "reviews"] })
(mc/update db coll {:_id oid} {$pop {:tags 1}}) (mgcol/update coll { :_id oid } { $pop { :tags 1 } })
(is (= {:_id oid :title title :tags ["products" "apple"]} (is (= { :_id oid :title title :tags ["products" "apple"] } (mgcol/find-map-by-id coll oid)))))
(mc/find-map-by-id db coll oid)))))
(deftest unshift-first-value-in-the-array-using-$pop-modifier (deftest unshift-first-value-in-the-array-using-$pop-modifier
(let [coll "docs" (let [coll "docs"
oid (ObjectId.) oid (ObjectId.)
title "$pop modifier removes last or first value in the array"] title "$pop modifier removes last or first value in the array"]
(mc/insert db coll {:_id oid :title title :tags ["products" "apple" "reviews"]}) (mgcol/insert coll { :_id oid :title title :tags ["products" "apple" "reviews"] })
(mc/update db coll {:_id oid} {$pop {:tags -1}}) (mgcol/update coll { :_id oid } { $pop { :tags -1 } })
(is (= {:_id oid :title title :tags ["apple" "reviews"]} (is (= { :_id oid :title title :tags ["apple" "reviews"] } (mgcol/find-map-by-id coll oid)))))
(mc/find-map-by-id db coll oid)))))
(deftest pop-last-values-from-multiple-arrays-using-$pop-modifier (deftest pop-last-values-from-multiple-arrays-using-$pop-modifier
(let [coll "docs" (let [coll "docs"
oid (ObjectId.) oid (ObjectId.)
title "$pop modifier removes last or first value in the array"] title "$pop modifier removes last or first value in the array"]
(mc/insert db coll {:_id oid :title title :tags ["products" "apple" "reviews"] :categories ["apple" "reviews" "drafts"]}) (mgcol/insert coll { :_id oid :title title :tags ["products" "apple" "reviews"] :categories ["apple" "reviews" "drafts"] })
(mc/update db coll {:_id oid} {$pop {:tags 1 :categories 1}}) (mgcol/update coll { :_id oid } { $pop { :tags 1 :categories 1 } })
(is (= {:_id oid :title title :tags ["products" "apple"] :categories ["apple" "reviews"]} (is (= { :_id oid :title title :tags ["products" "apple"] :categories ["apple" "reviews"] } (mgcol/find-map-by-id coll oid)))))
(mc/find-map-by-id db coll oid)))))
;; ;;
;; $pull ;; $pull
;; ;;
(deftest remove-all-value-entries-from-array-using-$pull-modifier (deftest remove-all-value-entries-from-array-using-$pull-modifier
(let [coll "docs" (let [coll "docs"
oid (ObjectId.) oid (ObjectId.)
title "$pull modifier removes all value entries in the array"] title "$pull modifier removes all value entries in the array"]
(mc/insert db coll {:_id oid :title title :measurements [1.0 1.2 1.2 1.2 1.1 1.1 1.2 1.3 1.0]}) (mgcol/insert coll { :_id oid :title title :measurements [1.0 1.2 1.2 1.2 1.1 1.1 1.2 1.3 1.0] })
(mc/update db coll {:_id oid} {$pull {:measurements 1.2}}) (mgcol/update coll { :_id oid } { $pull { :measurements 1.2 } })
(is (= {:_id oid :title title :measurements [1.0 1.1 1.1 1.3 1.0]} (is (= { :_id oid :title title :measurements [1.0 1.1 1.1 1.3 1.0] } (mgcol/find-map-by-id coll oid)))))
(mc/find-map-by-id db coll oid)))))
(deftest remove-all-value-entries-from-array-using-$pull-modifier-based-on-a-condition (deftest remove-all-value-entries-from-array-using-$pull-modifier-based-on-a-condition
(let [coll "docs" (let [coll "docs"
oid (ObjectId.) oid (ObjectId.)
title "$pull modifier removes all value entries in the array"] title "$pull modifier removes all value entries in the array"]
(mc/insert db coll {:_id oid :title title :measurements [1.0 1.2 1.2 1.2 1.1 1.1 1.2 1.3 1.0]}) (mgcol/insert coll { :_id oid :title title :measurements [1.0 1.2 1.2 1.2 1.1 1.1 1.2 1.3 1.0] })
(mc/update db coll {:_id oid} {$pull {:measurements {$gte 1.2}}}) (mgcol/update coll { :_id oid } { $pull { :measurements { $gte 1.2 } } })
(is (= {:_id oid :title title :measurements [1.0 1.1 1.1 1.0]} (is (= { :_id oid :title title :measurements [1.0 1.1 1.1 1.0] } (mgcol/find-map-by-id coll oid)))))
(mc/find-map-by-id db coll oid))))) ;;
;; ;; $pullAll
;; $pullAll ;;
;;
(deftest remove-all-value-entries-from-array-using-$pullAll-modifier (deftest remove-all-value-entries-from-array-using-$pullAll-modifier
(let [coll "docs" (let [coll "docs"
oid (ObjectId.) oid (ObjectId.)
title "$pullAll modifier removes entries of multiple values in the array"] title "$pullAll modifier removes entries of multiple values in the array"]
(mc/insert db coll {:_id oid :title title :measurements [1.0 1.2 1.2 1.2 1.1 1.1 1.2 1.3 1.0]}) (mgcol/insert coll { :_id oid :title title :measurements [1.0 1.2 1.2 1.2 1.1 1.1 1.2 1.3 1.0] })
(mc/update db coll {:_id oid} {$pullAll {:measurements [1.0 1.1 1.2]}}) (mgcol/update coll { :_id oid } { $pullAll { :measurements [1.0 1.1 1.2] } })
(is (= {:_id oid :title title :measurements [1.3]} (is (= { :_id oid :title title :measurements [1.3] } (mgcol/find-map-by-id coll oid)))))
(mc/find-map-by-id db coll oid)))))
;; ;;
;; $rename ;; $rename
;; ;;
(deftest rename-a-single-field-using-$rename-modifier (deftest rename-a-single-field-using-$rename-modifier
(let [coll "docs" (let [coll "docs"
oid (ObjectId.) oid (ObjectId.)
title "$rename renames fields" title "$rename renames fields"
v [1.0 1.2 1.2 1.2 1.1 1.1 1.2 1.3 1.0]] v [1.0 1.2 1.2 1.2 1.1 1.1 1.2 1.3 1.0]]
(mc/insert db coll {:_id oid :title title :measurements v}) (mgcol/insert coll { :_id oid :title title :measurements v })
(mc/update db coll {:_id oid} {$rename {:measurements "results"}}) (mgcol/update coll { :_id oid } { $rename { :measurements "results" } })
(is (= {:_id oid :title title :results v} (is (= { :_id oid :title title :results v } (mgcol/find-map-by-id coll oid)))))
(mc/find-map-by-id db coll oid)))))
;; ;;
;; find-and-modify ;; find-and-modify
;; ;;
(deftest find-and-modify-a-single-document (deftest find-and-modify-a-single-document
(let [coll "docs" (let [coll "docs"
oid (ObjectId.) oid (ObjectId.)
doc {:_id oid :name "Sophie Bangs" :level 42} doc {:_id oid :name "Sophie Bangs" :level 42}
conditions {:name "Sophie Bangs"} conditions {:name "Sophie Bangs"}
update {$inc {:level 1}}] update {$inc {:level 1}}]
(mc/insert db coll doc) (mgcol/insert coll doc)
(let [res (mc/find-and-modify db coll conditions update {:return-new true})] (let [res (mgcol/find-and-modify coll conditions update :return-new true)]
(is (= (select-keys res [:name :level]) {:name "Sophie Bangs" :level 43}))))) (is (= (select-keys res [:name :level]) {:name "Sophie Bangs" :level 43})))))
(deftest find-and-modify-remove-a-document (deftest find-and-modify-remove-a-document
(let [coll "docs" (let [coll "docs"
oid (ObjectId.) oid (ObjectId.)
doc {:_id oid :name "Sophie Bangs" :level 42} doc {:_id oid :name "Sophie Bangs" :level 42}
conditions {:name "Sophie Bangs"}] conditions {:name "Sophie Bangs"}]
(mc/insert db coll doc) (mgcol/insert coll doc)
(let [res (mc/find-and-modify db coll conditions {} {:remove true})] (let [res (mgcol/find-and-modify coll conditions {} :remove true)]
(is (= (select-keys res [:name :level]) {:name "Sophie Bangs" :level 42})) (is (= (select-keys res [:name :level]) {:name "Sophie Bangs" :level 42}))
(is (empty? (mc/find-maps db coll conditions)))))) (is (empty? (mgcol/find-maps coll conditions))))))
(deftest find-and-modify-upsert-a-document (deftest find-and-modify-upsert-a-document
(testing "case 1" (testing "case 1"
(let [coll "docs" (let [coll "docs"
oid (ObjectId.) oid (ObjectId.)
doc {:_id oid :name "Sophie Bangs" :level 42}] doc {:_id oid :name "Sophie Bangs" :level 42}]
(let [res (mc/find-and-modify db coll doc doc {:upsert true})] (let [res (mgcol/find-and-modify coll doc doc :upsert true)]
(is (empty? res)) (is (empty? res))
(is (select-keys (mc/find-map-by-id db coll oid) [:name :level]) (dissoc doc :_id))))) (is (select-keys (mgcol/find-map-by-id coll oid) [:name :level]) (dissoc doc :_id)))))
(testing "case 2" (testing "case 2"
(let [coll "docs" (let [coll "docs"
query {:name "Sophie Bangs"} query {:name "Sophie Bangs"}
doc (merge query {:level 42})] doc (merge query {:level 42})]
(let [res (mc/find-and-modify db coll query doc {:upsert true :return-new true})] (let [res (mgcol/find-and-modify coll query doc :upsert true :return-new true)]
(is (:_id res)) (is (:_id res))
(is (select-keys (mc/find-map-by-id db coll (:_id res)) [:name :level]) doc))))) (is (select-keys (mgcol/find-map-by-id coll (:_id res)) [:name :level]) doc)))))
(deftest find-and-modify-after-sort (deftest find-and-modify-after-sort
(let [coll "docs" (let [coll "docs"
oid (ObjectId.) oid (ObjectId.)
oid2 (ObjectId.) oid2 (ObjectId.)
doc {:name "Sophie Bangs"} doc {:name "Sophie Bangs"}
doc1 (assoc doc :_id oid :level 42) doc1 (assoc doc :_id oid :level 42)
doc2 (assoc doc :_id oid2 :level 0)] doc2 (assoc doc :_id oid2 :level 0)]
(mc/insert-batch db coll [doc1 doc2]) (mgcol/insert-batch coll [doc1 doc2])
(let [res (mc/find-and-modify db coll doc {$inc {:level 1}} {:sort {:level -1}})] (let [res (mgcol/find-and-modify coll doc {$inc {:level 1}} :sort {:level -1})]
(is (= (select-keys res [:name :level]) {:name "Sophie Bangs" :level 42})))))) (is (= (select-keys res [:name :level]) {:name "Sophie Bangs" :level 42})))))

View file

@ -1,42 +1,19 @@
(ns monger.test.authentication-test (ns monger.test.authentication-test
(:require [monger util db] (:require [monger core util db]
[monger.credentials :as mcr] [monger.test.helper :as helper])
[monger.core :as mg] (:use [clojure.test]))
[monger.collection :as mc]
[clojure.test :refer :all]))
;; (helper/connect!)
;; Connection via URI
;;
(when-not (System/getenv "CI")
(deftest ^{:authentication true} connect-to-mongo-via-uri-without-credentials
(let [{:keys [conn db]} (mg/connect-via-uri "mongodb://127.0.0.1/monger-test4")]
(is (-> conn .getAddress (.sameHost "127.0.0.1")))))
(deftest ^{:authentication true} connect-to-mongo-via-uri-with-valid-credentials
(let [{:keys [conn db]} (mg/connect-via-uri "mongodb://clojurewerkz%2Fmonger:monger@127.0.0.1/monger-test4")]
(is (= "monger-test4" (.getName db)))
(is (-> conn .getAddress (.sameHost "127.0.0.1")))
(mc/remove db "documents")
;; make sure that the database is selected
;; and operations get through.
(mc/insert db "documents" {:field "value"})
(is (= 1 (mc/count db "documents" {}))))))
(if-let [uri (System/getenv "MONGOHQ_URL")]
(deftest ^{:external true :authentication true} connect-to-mongo-via-uri-with-valid-credentials
(let [{:keys [conn db]} (mg/connect-via-uri uri)]
(is (-> conn .getAddress (.sameHost "127.0.0.1"))))))
;;
;; Regular connecton
;;
(deftest ^{:authentication true} test-authentication-with-valid-credentials (deftest test-authentication-with-valid-credentials
;; see ./bin/ci/before_script.sh. MK. ;; see ./bin/ci/before_script.sh. MK.
(doseq [s ["monger-test" "monger-test2" "monger-test3" "monger-test4"]] (let [username "clojurewerkz/monger"
(let [creds (mcr/create "clojurewerkz/monger" "monger-test" "monger") pwd "monger"]
conn (mg/connect-with-credentials "127.0.0.1" creds)] (is (monger.core/authenticate "monger-test" username (.toCharArray pwd)))))
(mc/remove (mg/get-db conn "monger-test") "documents"))))
(deftest test-authentication-with-invalid-credentials
(let [username "monger"
^String pwd (monger.util/random-str 128 32)]
(is (not (monger.core/authenticate "monger-test2" username (.toCharArray pwd))))))

View file

@ -0,0 +1,123 @@
(ns monger.test.cache-test
(:require [monger.test.helper :as helper]
[monger.collection :as mc])
(:use clojure.core.cache clojure.test monger.cache)
(:import [clojure.core.cache BasicCache FIFOCache LRUCache TTLCache]
java.util.UUID))
;;
;; Playground/Tests. These were necessary because clojure.core.cache has
;; little documentation, incomplete test suite and
;; slightly non-standard (although necessary to support all those cache variations)
;; cache operations protocol.
;;
;; This is by no means clear or complete either but it did the job of helping me
;; explore the API.
(deftest ^{:cache true}
test-has?-with-basic-cache
(testing "that has? returns false for misses"
(let [c (BasicCache. {})]
(are [v] (is (false? (has? c v)))
:missing-key
"missing-key"
(gensym "missing-key"))))
(testing "that has? returns true for hits"
(let [c (BasicCache. {:skey "Value" :lkey (Long/valueOf 10000) "kkey" :keyword})]
(are [v] (is (has? c v))
:skey
:lkey
"kkey"))))
(deftest ^{:cache true}
test-lookup-with-basic-cache
(testing "that lookup returns nil for misses"
(let [c (BasicCache. {})]
(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)
c (BasicCache. {:skey "Value" :lkey l "kkey" :keyword})]
(are [v k] (is (= v (lookup c k)))
"Value" :skey
l :lkey
:keyword "kkey"))))
(deftest ^{:cache true}
test-evict-with-basic-cache
(testing "that evict has no effect for keys that do not exist"
(let [c (atom (BasicCache. {:a 1 :b 2}))]
(swap! c evict :missing-key)
(is (has? @c :a))
(is (has? @c :b))))
(testing "that evict removes keys that did exist"
(let [c (atom (BasicCache. {:skey "Value" "kkey" :keyword}))]
(is (has? @c :skey))
(is (= "Value" (lookup @c :skey)))
(swap! c evict :skey)
(is (not (has? @c :skey)))
(is (= nil (lookup @c :skey)))
(is (has? @c "kkey"))
(is (= :keyword (lookup @c "kkey"))))))
(deftest ^{:cache true}
test-seed-with-basic-cache
(testing "that seed returns a new value"
(let [c (atom (BasicCache. {}))]
(swap! c seed {:a 1 :b "b" "c" :d})
(are [k v] (do
(is (has? @c k))
(is (= v (lookup @c k))))
:a 1
:b "b"
"c" :d))))
;;
;; Tests
;;
(helper/connect!)
(use-fixtures :each (fn [f]
(mc/remove "basic_monger_cache_entries")
(f)
(mc/remove "basic_monger_cache_entries")))
(deftest ^{:cache true}
test-has?-with-basic-monger-cache
(testing "that has? returns false for misses"
(let [coll "basic_monger_cache_entries"
c (basic-monger-cache-factory coll)]
(is (not (has? c (str (UUID/randomUUID)))))
(is (not (has? c (str (UUID/randomUUID)))))))
(testing "that has? returns true for hits"
(let [coll "basic_monger_cache_entries"
c (basic-monger-cache-factory 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 [coll "basic_monger_cache_entries"
c (basic-monger-cache-factory 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)
coll "basic_monger_cache_entries"
c (basic-monger-cache-factory coll {:skey "Value" :lkey l "kkey" :keyword})]
(are [v k] (is (= v (lookup c k)))
"Value" :skey
l :lkey
"keyword" "kkey"))))

View file

@ -1,25 +1,38 @@
(set! *warn-on-reflection* true)
(ns monger.test.capped-collections-test (ns monger.test.capped-collections-test
(:require [monger.core :as mg] (:require [monger core util]
[monger.collection :as mc] [monger.collection :as mc]
[clojure.test :refer :all] [monger.result :as mres]
[monger.operators :refer :all])) [monger.test.helper :as helper])
(:use clojure.test
monger.operators
monger.test.fixtures))
(helper/connect!)
(use-fixtures :each purge-cached)
(defn- megabytes (defn- megabytes
[^long n] [^long n]
(* n 1024 1024)) (* n 1024 1024))
(let [conn (mg/connect)
db (mg/get-db conn "monger-test")] ;;
(deftest test-inserting-into-capped-collection ;; Tests
;;
(deftest test-inserting-into-capped-collection
(let [n 1000 (let [n 1000
cname "cached" cname "cached"
_ (mc/drop db cname) _ (mc/drop cname)
coll (mc/create db cname {:capped true :size (-> 16 megabytes) :max n})] coll (mc/create cname {:capped true :size (-> 16 megabytes) :max n})]
(is (= cname (.getName coll))) (is (= cname (.getName coll)))
(mc/insert-batch db cname (for [i (range 0 (+ n 100))] {:i i})) (mc/insert-batch cname (for [i (range 0 (+ n 100))] {:i i}))
(is (= n (mc/count db cname))) (is (= n (mc/count cname)))
;; older elements get replaced by newer ones ;; older elements get replaced by newer ones
(is (not (mc/any? db cname {:i 1}))) (is (not (mc/any? cname {:i 1})))
(is (not (mc/any? db cname {:i 5}))) (is (not (mc/any? cname {:i 5})))
(is (not (mc/any? db cname {:i 9}))) (is (not (mc/any? cname {:i 9})))
(is (mc/any? db cname {:i (+ n 80)}))))) (is (mc/any? cname {:i (+ n 80)}))))

View file

@ -1,131 +1,127 @@
(set! *warn-on-reflection* true)
(ns monger.test.collection-test (ns monger.test.collection-test
(:import org.bson.types.ObjectId (:import [com.mongodb WriteResult WriteConcern DBCursor DBObject CommandResult$CommandFailure MapReduceOutput MapReduceCommand MapReduceCommand$OutputType]
org.bson.types.ObjectId
java.util.Date) java.util.Date)
(:require [monger.core :as mg] (:require [monger.core :as mg]
[monger.collection :as mc] [monger.collection :as mc]
[clojure.test :refer :all] [monger.result :as mgres]
[monger.operators :refer :all])) [monger.test.helper :as helper])
(:use clojure.test
monger.operators
monger.test.fixtures))
(let [conn (mg/connect) (helper/connect!)
db (mg/get-db conn "monger-test")]
(defn purge-collections (use-fixtures :each purge-people purge-docs purge-things purge-libraries)
[f]
(mc/remove db "people")
(mc/remove db "docs")
(mc/remove db "things")
(mc/remove db "libraries")
(f)
(mc/remove db "people")
(mc/remove db "docs")
(mc/remove db "things")
(mc/remove db "libraries"))
(use-fixtures :each purge-collections)
;; ;;
;; count, remove ;; count, remove
;; ;;
(deftest get-collection-size (deftest get-collection-size
(let [collection "things"] (let [collection "things"]
(is (= 0 (mc/count db collection))) (is (= 0 (mc/count collection)))
(mc/insert-batch db collection [{:language "Clojure" :name "langohr"} (mc/insert-batch collection [{:language "Clojure" :name "langohr"}
{:language "Clojure" :name "monger"} {:language "Clojure" :name "monger"}
{:language "Clojure" :name "incanter"} {:language "Clojure" :name "incanter"}
{:language "Scala" :name "akka"}]) {:language "Scala" :name "akka"}])
(is (= 4 (mc/count db collection))) (is (= 4 (mc/count collection)))
(is (mc/any? db collection)) (is (mc/any? collection))
(is (= 3 (mc/count db collection {:language "Clojure"}))) (is (= 3 (mc/count mg/*mongodb-database* collection {:language "Clojure"})))
(is (mc/any? db collection {:language "Clojure"})) (is (mc/any? mg/*mongodb-database* collection {:language "Clojure"}))
(is (= 1 (mc/count db collection {:language "Scala" }))) (is (= 1 (mc/count collection {:language "Scala" })))
(is (mc/any? db collection {:language "Scala"})) (is (mc/any? collection {:language "Scala"}))
(is (= 0 (mc/count db collection {:language "Python" }))) (is (= 0 (mc/count mg/*mongodb-database* collection {:language "Python" })))
(is (not (mc/any? db collection {:language "Python"}))))) (is (not (mc/any? mg/*mongodb-database* collection {:language "Python"})))))
(deftest remove-all-documents-from-collection (deftest remove-all-documents-from-collection
(let [collection "libraries"] (let [collection "libraries"]
(mc/insert-batch db collection [{:language "Clojure" :name "monger"} (mc/insert-batch collection [{:language "Clojure" :name "monger"}
{:language "Clojure" :name "langohr"} {:language "Clojure" :name "langohr"}
{:language "Clojure" :name "incanter"} {:language "Clojure" :name "incanter"}
{:language "Scala" :name "akka"}]) {:language "Scala" :name "akka"}])
(is (= 4 (mc/count db collection))) (is (= 4 (mc/count collection)))
(mc/remove db collection) (mc/remove collection)
(is (= 0 (mc/count db collection))))) (is (= 0 (mc/count collection)))))
(deftest remove-some-documents-from-collection (deftest remove-some-documents-from-collection
(let [collection "libraries"] (let [collection "libraries"]
(mc/insert-batch db collection [{:language "Clojure" :name "monger"} (mc/insert-batch collection [{:language "Clojure" :name "monger"}
{:language "Clojure" :name "langohr"} {:language "Clojure" :name "langohr"}
{:language "Clojure" :name "incanter"} {:language "Clojure" :name "incanter"}
{:language "Scala" :name "akka"}]) {:language "Scala" :name "akka"}])
(is (= 4 (mc/count db collection))) (is (= 4 (mc/count collection)))
(mc/remove db collection {:language "Clojure"}) (mc/remove collection {:language "Clojure"})
(is (= 1 (mc/count db collection))))) (is (= 1 (mc/count collection)))))
(deftest remove-a-single-document-from-collection (deftest remove-a-single-document-from-collection
(let [collection "libraries" (let [collection "libraries"
oid (ObjectId.)] oid (ObjectId.)]
(mc/insert-batch db collection [{:language "Clojure" :name "monger" :_id oid}]) (mc/insert-batch collection [{:language "Clojure" :name "monger" :_id oid}])
(mc/remove-by-id db collection oid) (mc/remove-by-id collection oid)
(is (= 0 (mc/count db collection))) (is (= 0 (mc/count collection)))
(is (nil? (mc/find-by-id db collection oid))))) (is (nil? (mc/find-by-id collection oid)))))
;; ;;
;; exists?, drop, create ;; exists?, drop, create
;; ;;
(deftest checking-for-collection-existence-when-it-does-not-exist (deftest checking-for-collection-existence-when-it-does-not-exist
(let [collection "widgets"] (let [collection "widgets"]
(mc/drop db collection) (mc/drop collection)
(is (false? (mc/exists? db collection))))) (is (false? (mc/exists? collection)))))
(deftest checking-for-collection-existence-when-it-does-exist (deftest checking-for-collection-existence-when-it-does-exist
(let [collection "widgets"] (let [collection "widgets"]
(mc/drop db collection) (mc/drop collection)
(mc/insert-batch db collection [{:name "widget1"} (mc/insert-batch collection [{:name "widget1"}
{:name "widget2"}]) {:name "widget2"}])
(is (mc/exists? db collection)) (is (mc/exists? collection))
(mc/drop db collection) (mc/drop collection)
(is (false? (mc/exists? db collection))) (is (false? (mc/exists? collection)))
(mc/create db "widgets" {:capped true :size 100000 :max 10}) (mc/create "widgets" {:capped true :size 100000 :max 10})
(is (mc/exists? db collection)) (is (mc/exists? collection))
(mc/rename db collection "gadgets") (mc/rename collection "gadgets")
(is (not (mc/exists? db collection))) (is (not (mc/exists? collection)))
(is (mc/exists? db "gadgets")) (is (mc/exists? "gadgets"))
(mc/drop db "gadgets"))) (mc/drop "gadgets")))
;; ;;
;; any?, empty? ;; any?, empty?
;; ;;
(deftest test-any-on-empty-collection (deftest test-any-on-empty-collection
(let [collection "things"] (let [collection "things"]
(is (not (mc/any? db collection))))) (is (not (mc/any? collection)))))
(deftest test-any-on-non-empty-collection (deftest test-any-on-non-empty-collection
(let [collection "things" (let [collection "things"
_ (mc/insert db collection {:language "Clojure" :name "langohr"})] _ (mc/insert collection {:language "Clojure" :name "langohr"})]
(is (mc/any? db "things" {:language "Clojure"})))) (is (mc/any? "things"))
(is (mc/any? mg/*mongodb-database* "things" {:language "Clojure"}))))
(deftest test-empty-on-empty-collection (deftest test-empty-on-empty-collection
(let [collection "things"] (let [collection "things"]
(is (mc/empty? db collection)))) (is (mc/empty? collection))
(is (mc/empty? mg/*mongodb-database* collection))))
(deftest test-empty-on-non-empty-collection (deftest test-empty-on-non-empty-collection
(let [collection "things" (let [collection "things"
_ (mc/insert db collection {:language "Clojure" :name "langohr"})] _ (mc/insert collection {:language "Clojure" :name "langohr"})]
(is (not (mc/empty? db "things"))))) (is (not (mc/empty? "things")))))
;; ;;
;; distinct ;; distinct
;; ;;
(deftest test-distinct-values (deftest test-distinct-values
(let [collection "widgets" (let [collection "widgets"
batch [{:state "CA" :quantity 1 :price 199.00} batch [{:state "CA" :quantity 1 :price 199.00}
{:state "NY" :quantity 2 :price 199.00} {:state "NY" :quantity 2 :price 199.00}
@ -133,52 +129,16 @@
{:state "IL" :quantity 2 :price 11.50 } {:state "IL" :quantity 2 :price 11.50 }
{:state "CA" :quantity 2 :price 2.95 } {:state "CA" :quantity 2 :price 2.95 }
{:state "IL" :quantity 3 :price 5.50 }]] {:state "IL" :quantity 3 :price 5.50 }]]
(mc/insert-batch db collection batch) (mc/insert-batch collection batch)
(is (= ["CA" "IL" "NY"] (sort (mc/distinct db collection :state)))) (is (= ["CA" "IL" "NY"] (sort (mc/distinct mg/*mongodb-database* collection :state {}))))
(is (= ["CA" "IL" "NY"] (sort (mc/distinct db collection :state {})))) (is (= ["CA" "NY"] (sort (mc/distinct collection :state {:price {$gt 100.00}}))))))
(is (= ["CA" "NY"] (sort (mc/distinct db collection :state {:price {$gt 100.00}}))))))
;;
;; update
;;
(let [coll "things" ;;
batch [{:_id 1 :type "rock" :size "small"} ;; miscellenous
{:_id 2 :type "bed" :size "bed-sized"} ;;
{:_id 3 :type "bottle" :size "1.5 liters"}]]
(deftest test-update (deftest test-system-collection-predicate
(mc/insert-batch db coll batch)
(is (= "small" (:size (mc/find-one-as-map db coll {:type "rock"}))))
(mc/update db coll {:type "rock"} {"$set" {:size "huge"}})
(is (= "huge" (:size (mc/find-one-as-map db coll {:type "rock"})))))
(deftest test-upsert
(is (mc/empty? db coll))
(mc/upsert db coll {:_id 4} {"$set" {:size "tiny"}})
(is (not (mc/empty? db coll)))
(mc/upsert db coll {:_id 4} {"$set" {:size "big"}})
(is (= [{:_id 4 :size "big"}] (mc/find-maps db coll {:_id 4}))))
(deftest test-update-by-id
(mc/insert-batch db coll batch)
(is (= "bed" (:type (mc/find-one-as-map db coll {:_id 2}))))
(mc/update-by-id db coll 2 {"$set" {:type "living room"}})
(is (= "living room" (:type (mc/find-one-as-map db coll {:_id 2})))))
(deftest test-update-by-ids
(mc/insert-batch db coll batch)
(is (= "bed" (:type (mc/find-one-as-map db coll {:_id 2}))))
(is (= "bottle" (:type (mc/find-one-as-map db coll {:_id 3}))))
(mc/update-by-ids db coll [2 3] {"$set" {:type "dog"}})
(is (= "dog" (:type (mc/find-one-as-map db coll {:_id 2}))))
(is (= "dog" (:type (mc/find-one-as-map db coll {:_id 3}))))))
;;
;; miscellenous
;;
(deftest test-system-collection-predicate
(are [name] (is (mc/system-collection? name)) (are [name] (is (mc/system-collection? name))
"system.indexes" "system.indexes"
"system" "system"
@ -190,4 +150,4 @@
"events" "events"
"accounts" "accounts"
"megacorp_account" "megacorp_account"
"myapp_development"))) "myapp_development"))

View file

@ -1,29 +1,50 @@
(ns monger.test.command-test (ns monger.test.command-test
(:require [monger.core :as mg] (:require [monger.core :as mg]
[monger.command :as mcom] [monger.command :as mcom]
[monger.collection :as mc] [monger.test.helper :as helper]
[clojure.test :refer :all] [monger.collection :as mc])
[monger.result :refer [acknowledged?]] (:use clojure.test
[monger.conversion :refer [from-db-object]])) [monger.result :only [ok?]]
[monger.conversion :only [from-db-object]]))
(let [conn (mg/connect) (helper/connect!)
db (mg/get-db conn "monger-test")]
(deftest ^{:command true} test-reindex-collection
(let [_ (mc/insert db "test" {:name "Clojure"})
result (mcom/reindex-collection db "test")]
(is (acknowledged? result))))
(deftest ^{:command true} test-server-status
(let [status (mcom/server-status db)]
(is (acknowledged? status))
(is (not-empty status))))
(deftest ^{:command true} test-top (deftest ^{:command true} test-db-stats
(let [result (mcom/top conn)] (let [stats (mcom/db-stats)]
(is (acknowledged? result)) (is (ok? stats))
(is (not-empty result)))) (is (= "monger-test" (get stats "db")))))
(deftest ^{:command true} test-running-is-master-as-an-arbitrary-command (deftest ^{:command true} test-collection-stats
(let [raw (mg/command db {:isMaster 1}) (let [collection "stat_test"
_ (mc/insert collection {:name "Clojure"})
check (mc/count collection)
stats (mcom/collection-stats collection)]
(is (ok? stats))
(is (= "monger-test.stat_test" (get stats "ns")))
(is (= check (get stats "count")))))
(deftest ^{:command true} test-reindex-collection
(let [_ (mc/insert "test" {:name "Clojure"})
result (mcom/reindex-collection "test")]
(is (ok? result))
(is (get result "indexes"))))
(deftest ^{:command true} test-server-status
(let [status (mcom/server-status)]
(is (ok? status))
(is (not-empty status))
(is (get status "serverUsed"))))
(deftest ^{:command true} test-top
(let [result (mcom/top)]
(is (ok? result))
(is (not-empty result))
(is (get result "serverUsed"))))
(deftest ^{:command true} test-running-is-master-as-an-arbitrary-command
(let [raw (mg/command {:isMaster 1})
result (from-db-object raw true)] result (from-db-object raw true)]
(is (acknowledged? raw))))) (is (ok? result))
(is (ok? raw))
(is (:ismaster result))))

View file

@ -1,11 +1,9 @@
(ns monger.test.conversion-test (ns monger.test.conversion-test
(:require [monger core collection] (:require [monger core collection])
[clojure.test :refer :all]
[monger.conversion :refer :all])
(:import [com.mongodb DBObject BasicDBObject BasicDBList] (:import [com.mongodb DBObject BasicDBObject BasicDBList]
[java.util Date Calendar List ArrayList] [java.util Date Calendar List ArrayList]
org.bson.types.ObjectId org.bson.types.ObjectId)
(org.bson.types Decimal128))) (:use clojure.test monger.conversion))
;; ;;
@ -102,13 +100,6 @@
(is (= 2 (from-db-object 2 false))) (is (= 2 (from-db-object 2 false)))
(is (= 2 (from-db-object 2 true)))) (is (= 2 (from-db-object 2 true))))
(deftest convert-decimal-from-dbobject
(is (= 2.3M (from-db-object (Decimal128. 2.3M) false)))
(is (= 2.3M (from-db-object (Decimal128. 2.3M) true)))
(is (= 2.3M (from-db-object (Decimal128/parse "2.3") true)))
(is (not= 2.32M (from-db-object (Decimal128/parse "2.3") true)))
)
(deftest convert-float-from-dbobject (deftest convert-float-from-dbobject
(is (= 3.3 (from-db-object 3.3 false))) (is (= 3.3 (from-db-object 3.3 false)))
(is (= 3.3 (from-db-object 3.3 true)))) (is (= 3.3 (from-db-object 3.3 true))))
@ -120,20 +111,20 @@
(.put "name" name) (.put "name" name)
(.put "age" age)) (.put "age" age))
output (from-db-object input false)] output (from-db-object input false)]
(is (= output { "name" name, "age" age })) (is (= (output { "name" name, "age" age })))
(is (= (output "name") name)) (is (= (output "name") name))
(is (nil? (output :name))) (is (nil? (output :name)))
(is (= (output "age") age)) (is (= (output "age") age))
(is (nil? (output "points"))))) (is (nil? (output "points")))))
(deftest convert-flat-db-object-to-map-with-keywordizing (deftest convert-flat-db-object-to-map-without-keywordizing
(let [name "Michael" (let [name "Michael"
age 26 age 26
input (doto (BasicDBObject.) input (doto (BasicDBObject.)
(.put "name" name) (.put "name" name)
(.put "age" age)) (.put "age" age))
output (from-db-object input true)] output (from-db-object input true)]
(is (= output { :name name, :age age })) (is (= (output { :name name, :age age })))
(is (= (output :name) name)) (is (= (output :name) name))
(is (nil? (output "name"))) (is (nil? (output "name")))
(is (= (output :age) age)) (is (= (output :age) age))

View file

@ -1,28 +1,79 @@
(ns monger.test.core-test (ns monger.test.core-test
(:require [monger util result] (:require [monger core collection util result]
[monger.core :as mg :refer [server-address mongo-options]] [monger.test.helper :as helper]
[monger.collection :as mc] [monger.collection :as mc])
[clojure.test :refer :all]) (:import [com.mongodb Mongo DB WriteConcern MongoOptions ServerAddress])
(:import [com.mongodb MongoClient DB WriteConcern MongoClientOptions ServerAddress])) (:use clojure.test
[monger.core :only [server-address mongo-options]]))
(println (str "Using Clojure version " *clojure-version*)) (println (str "Using Clojure version " *clojure-version*))
(helper/connect!)
(deftest connect-to-mongo-with-default-host-and-port (deftest connect-to-mongo-with-default-host-and-port
(let [connection (mg/connect)] (let [connection (monger.core/connect)]
(is (instance? com.mongodb.MongoClient connection)))) (is (instance? com.mongodb.Mongo connection))))
(deftest connect-and-disconnect
(let [conn (mg/connect)]
(mg/disconnect conn)))
(deftest connect-to-mongo-with-default-host-and-explicit-port (deftest connect-to-mongo-with-default-host-and-explicit-port
(let [connection (mg/connect {:port 27017})] (let [connection (monger.core/connect { :port 27017 })]
(is (instance? com.mongodb.MongoClient connection)))) (is (instance? com.mongodb.Mongo connection))))
(deftest connect-to-mongo-with-default-port-and-explicit-host (deftest connect-to-mongo-with-default-port-and-explicit-host
(let [connection (mg/connect {:host "127.0.0.1"})] (let [connection (monger.core/connect { :host "127.0.0.1" })]
(is (instance? com.mongodb.MongoClient connection)))) (is (instance? com.mongodb.Mongo connection))))
(when-not (System/getenv "CI")
(deftest connect-to-mongo-via-uri-without-credentials
(let [connection (monger.core/connect-via-uri! "mongodb://127.0.0.1/monger-test4")]
(is (= (-> connection .getAddress ^InetAddress (.sameHost "127.0.0.1")))))
;; reconnect using regular host
(helper/connect!))
(deftest connect-to-mongo-via-uri-with-valid-credentials
(let [connection (monger.core/connect-via-uri! "mongodb://clojurewerkz/monger!:monger!@127.0.0.1/monger-test4")]
(is (= "monger-test4" (.getName (monger.core/current-db))))
(is (= (-> connection .getAddress ^InetAddress (.sameHost "127.0.0.1"))))
(mc/remove "documents")
;; make sure that the database is selected
;; and operations get through.
(mc/insert "documents" {:field "value"})
(is (= 1 (mc/count "documents" {}))))
;; reconnect using regular host
(helper/connect!)))
(if-let [uri (System/getenv "MONGOHQ_URL")]
(deftest ^{:external true} connect-to-mongo-via-uri-with-valid-credentials
(let [connection (monger.core/connect-via-uri! uri)]
(is (= (-> connection .getAddress ^InetAddress (.sameHost "127.0.0.1")))))
;; reconnect using regular host
(helper/connect!)))
(deftest connect-to-mongo-via-uri-with-invalid-credentials
(is (thrown? IllegalArgumentException
(monger.core/connect-via-uri! "mongodb://clojurewerkz/monger!:ahsidaysd78jahsdi8@127.0.0.1/monger-test4"))))
(deftest test-mongo-options-builder
(let [max-wait-time (* 1000 60 2)
^MongoOptions result (monger.core/mongo-options :connections-per-host 3 :threads-allowed-to-block-for-connection-multiplier 50
:max-wait-time max-wait-time :connect-timeout 10 :socket-timeout 10 :socket-keep-alive true
:auto-connect-retry true :max-auto-connect-retry-time 0 :safe true
:w 1 :w-timeout 20 :fsync true :j true)]
(is (= 3 (. result connectionsPerHost)))
(is (= 50 (. result threadsAllowedToBlockForConnectionMultiplier)))
(is (= max-wait-time (.maxWaitTime result)))
(is (= 10 (.connectTimeout result)))
(is (= 10 (.socketTimeout result)))
(is (.socketKeepAlive result))
(is (.autoConnectRetry result))
(is (= 0 (.maxAutoConnectRetryTime result)))
(is (.safe result))
(is (= 1 (.w result)))
(is (= 20 (.wtimeout result)))
(is (.fsync result))
(is (.j result))))
(deftest test-server-address (deftest test-server-address
(let [host "127.0.0.1" (let [host "127.0.0.1"
@ -32,58 +83,32 @@
(is (= port (.getPort sa))))) (is (= port (.getPort sa)))))
(deftest use-existing-mongo-connection (deftest use-existing-mongo-connection
(let [^MongoClientOptions opts (mongo-options {:threads-allowed-to-block-for-connection-multiplier 300}) (let [^MongoOptions opts (mongo-options :threads-allowed-to-block-for-connection-multiplier 300)
connection (MongoClient. "127.0.0.1" opts) connection (Mongo. "127.0.0.1" opts)]
db (mg/get-db connection "monger-test")] (monger.core/set-connection! connection)
(mg/disconnect connection))) (is (= monger.core/*mongodb-connection* connection))))
(deftest connect-to-mongo-with-extra-options (deftest connect-to-mongo-with-extra-options
(let [^MongoClientOptions opts (mongo-options {:threads-allowed-to-block-for-connection-multiplier 300}) (let [^MongoOptions opts (mongo-options :threads-allowed-to-block-for-connection-multiplier 300)
^ServerAddress sa (server-address "127.0.0.1" 27017) ^ServerAddress sa (server-address "127.0.0.1" 27017)]
conn (mg/connect sa opts)] (monger.core/connect! sa opts)))
(mg/disconnect conn)))
(deftest get-database (deftest get-database
(let [connection (mg/connect) (let [connection (monger.core/connect)
db (mg/get-db connection "monger-test")] db (monger.core/get-db connection "monger-test")]
(is (instance? com.mongodb.DB db)))) (is (instance? com.mongodb.DB db))))
(deftest test-get-db-names (deftest test-get-db-names
(let [conn (mg/connect) (let [dbs (monger.core/get-db-names)]
dbs (mg/get-db-names conn)]
(is (not (empty? dbs))) (is (not (empty? dbs)))
(is (dbs "monger-test")))) (is (dbs "monger-test"))))
(deftest monger-options-test (deftest get-last-error
(let [opts {:always-use-mbeans true (let [connection (monger.core/connect)
:application-name "app" db (monger.core/get-db connection "monger-test")]
:connect-timeout 1 (is (monger.result/ok? (monger.core/get-last-error)))
:connections-per-host 1 (is (monger.result/ok? (monger.core/get-last-error db)))
:cursor-finalizer-enabled true (is (monger.result/ok? (monger.core/get-last-error db WriteConcern/NORMAL)))
:description "Description" (is (monger.result/ok? (monger.core/get-last-error db 1 100 true)))))
:heartbeat-connect-timeout 1
:heartbeat-frequency 1
:heartbeat-socket-timeout 1
:local-threshold 1
:max-connection-idle-time 1
:max-connection-life-time 1
:max-wait-time 1
:min-connections-per-host 1
:min-heartbeat-frequency 1
:required-replica-set-name "rs"
:retry-writes true
:server-selection-timeout 1
:socket-keep-alive true
:socket-timeout 1
:ssl-enabled true
:ssl-invalid-host-name-allowed true
:threads-allowed-to-block-for-connection-multiplier 1
:uuid-representation org.bson.UuidRepresentation/STANDARD
:write-concern com.mongodb.WriteConcern/JOURNAL_SAFE}]
(is (instance? com.mongodb.MongoClientOptions$Builder (mg/mongo-options-builder opts)))))
(deftest connect-to-uri-without-db-name
(let [uri "mongodb://localhost:27017"]
(is (thrown? IllegalArgumentException (mg/connect-via-uri uri)))))

View file

@ -1,107 +0,0 @@
(ns monger.test.cursor-test
(:import [com.mongodb DBCursor DBObject Bytes]
[java.util List Map])
(:require [monger.core :as mg]
[clojure.test :refer :all]
[monger.cursor :refer :all]))
(let [conn (mg/connect)
db (mg/get-db conn "monger-test")]
(deftest make-db-cursor-for-collection
(is (= DBCursor
(class (make-db-cursor db :docs)))))
(deftest getting-cursor-options-value
(let [db-cur (make-db-cursor db :docs)
opts (get-options db-cur)]
(is (= true (isa? (class opts) Map)))
(is (= 0 (.getOptions db-cur))) ;;test default value
(is (= false (:notimeout opts)))
(is (= false (:partial opts)))
(is (= false (:awaitdata opts)))
(is (= false (:oplogreplay opts)))
(is (= false (:slaveok opts)))
(is (= false (:tailable opts)))))
(deftest adding-option-to-cursor
(let [db-cur (make-db-cursor db :docs)]
(add-option! db-cur :notimeout)
(is (= (:notimeout cursor-options)
(.getOptions db-cur)))
(add-option! db-cur :tailable)
(is (= (.getOptions db-cur)
(bit-or (:notimeout cursor-options)
(:tailable cursor-options))))))
(deftest remove-option-from-cursor
(let [db-cur (make-db-cursor db :docs)]
(add-option! db-cur :partial)
(add-option! db-cur :awaitdata)
;; removing not-set option should not affect result
(remove-option! db-cur :notimeout)
(is (= (.getOptions db-cur)
(bit-or (:partial cursor-options)
(:awaitdata cursor-options))))
;; removing active option should remove correct value
(remove-option! db-cur :awaitdata)
(is (= (.getOptions db-cur)
(:partial cursor-options)))))
(deftest test-reset-options
(let [db-cur (make-db-cursor db :docs)]
(add-option! db-cur :partial)
(is (= (.getOptions db-cur)
(:partial cursor-options)))
(is (= 0
(int (.getOptions (reset-options db-cur)))))))
(deftest add-options-with-hashmap
(let [db-cur (make-db-cursor db :docs)
_ (add-options db-cur {:notimeout true :slaveok true})
opts (get-options db-cur)]
(is (= true (:notimeout opts)))
(is (= true (:slaveok opts)))
(is (= false (:tailable opts)))
(is (= false (:oplogreplay opts)))))
(deftest add-options-with-hashmap-and-remove-option
(let [db-cur (make-db-cursor db :docs)
_ (add-options db-cur {:notimeout true :slaveok true})
opts (get-options db-cur)]
(is (= true (:notimeout opts)))
(is (= true (:slaveok opts)))
;;remove key and add another option
(add-options db-cur {:partial true :slaveok false})
(let [opts (get-options db-cur)]
(is (= true (:notimeout opts)))
(is (= true (:partial opts)))
(is (= false (:slaveok opts)))
(is (= false (:tailable opts))))))
(deftest add-options-with-list
(let [db-cur (make-db-cursor db :docs)
_ (add-options db-cur [:notimeout :slaveok])
opts (get-options db-cur)]
(is (= true (:notimeout opts)))
(is (= true (:slaveok opts)))
(is (= false (:tailable opts)))
(is (= false (:oplogreplay opts)))))
(deftest add-options-with-Bytes
(let [db-cur (make-db-cursor db :docs)
_ (add-options db-cur Bytes/QUERYOPTION_NOTIMEOUT)
opts (get-options db-cur)]
(is (= true (:notimeout opts)))
(is (= false (:slaveok opts)))
(is (= false (:tailable opts)))
(is (= false (:oplogreplay opts)))))
(deftest add-options-with-one-keyword
(let [db-cur (make-db-cursor db :docs)
_ (add-options db-cur :notimeout)
opts (get-options db-cur)]
(is (= true (:notimeout opts)))
(is (= false (:slaveok opts)))
(is (= false (:tailable opts)))
(is (= false (:oplogreplay opts))))))

View file

@ -1,31 +1,54 @@
(ns monger.test.db-test (ns monger.test.db-test
(:require [monger.db :as mdb] (:require [monger core db]
[monger.core :as mg] [monger.test.helper :as helper]
[monger.collection :as mc] [monger.collection :as mc])
[clojure.test :refer :all])
(:import [com.mongodb Mongo DB] (:import [com.mongodb Mongo DB]
java.util.Set)) java.util.Set)
(:use clojure.test))
(helper/connect!)
(deftest test-add-user
(let [username "clojurewerkz/monger!"
pwd (.toCharArray "monger!")
db-name "monger-test4"]
;; use a secondary database here. MK.
(monger.core/with-db (monger.core/get-db db-name)
(monger.db/add-user username pwd)
(is (monger.core/authenticate db-name username pwd)))))
;; do not run this test for CI, it complicates matters by messing up ;; do not run this test for CI, it complicates matters by messing up
;; authentication for some other tests :( MK. ;; authentication for some other tests :( MK.
(let [conn (mg/connect)] (when-not (System/getenv "CI")
(when-not (System/getenv "CI")
(deftest test-drop-database (deftest test-drop-database
;; drop a secondary database here. MK. ;; drop a secondary database here. MK.
(let [db (mg/get-db conn "monger-test3") (monger.core/with-db (monger.core/get-db "monger-test3")
collection "test" (let [collection "test"
_ (mc/insert db collection {:name "Clojure"}) _ (mc/insert collection {:name "Clojure"})
check (mc/count db collection) check (mc/count collection)
_ (mdb/drop-db db)] _ (monger.db/drop-db)]
(is (= 1 check)) (is (= 1 check))
(is (not (mc/exists? db collection))) (is (not (mc/exists? collection)))
(is (= 0 (mc/count db collection)))))) (is (= 0 (mc/count collection))))))
(deftest test-get-collection-names (deftest test-use-database
(let [db (mg/get-db conn "monger-test")] (monger.core/use-db! "monger-test5")
(mc/insert db "test-1" {:name "Clojure"}) (is (= "monger-test5" (.getName (monger.core/current-db))))
(mc/insert db "test-2" {:name "Clojure"}) (let [collection "test"
(let [^Set xs (mdb/get-collection-names db)] _ (mc/insert collection {:name "Clojure"})
(is (.contains xs "test-1")) check (mc/count collection)
(is (.contains xs "test-2")))))) _ (monger.db/drop-db)]
(is (= 1 check))
(is (not (mc/exists? collection)))
(is (= 0 (mc/count collection))))))
(deftest test-get-collection-names
(mc/insert "test-1" {:name "Clojure"})
(mc/insert "test-2" {:name "Clojure"})
(let [^Set collections (monger.db/get-collection-names)]
(is (.contains collections "test-1"))
(is (.contains collections "test-2"))))

View file

@ -0,0 +1,155 @@
(ns monger.test.factory-dsl-test
(:use clojure.test
[monger testkit joda-time]
monger.test.fixtures
[clj-time.core :only [days ago weeks now]])
(:require [monger.collection :as mc]
[monger.test.helper :as helper])
(:import org.bson.types.ObjectId
org.joda.time.DateTime))
(helper/connect!)
(use-fixtures :each purge-domains purge-pages)
(defaults-for "domains"
:ipv6-enabled false)
(let [coll "domains"]
(factory coll "clojure"
:name "clojure.org"
:created-at (-> 2 days ago)
:embedded [(embedded-doc "pages" "http://clojure.org/lisp")
(embedded-doc "pages" "http://clojure.org/jvm_hosted")
(embedded-doc "pages" "http://clojure.org/runtime_polymorphism")])
(factory coll "elixir"
:_id (memoized-oid coll "elixir")
:name "elixir-lang.org"
:created-at (fn [] (now))
:topics (fn [] ["programming" "erlang" "beam" "ruby"])
:related {
:terms (fn [] ["erlang" "python" "ruby"])
}))
(let [coll "pages"]
(factory coll "http://clojure.org/rationale"
:name "/rationale"
:domain-id (parent-id "domains" "clojure"))
(factory coll "http://clojure.org/jvm_hosted"
:name "/jvm_hosted")
(factory coll "http://clojure.org/runtime_polymorphism"
:name "/runtime_polymorphism")
(factory coll "http://clojure.org/lisp"
:name "/lisp")
(factory coll "http://elixir-lang.org/getting_started"
:name "/getting_started/1.html"
:domain-id (memoized-oid "domains" "elixir")))
(deftest test-building-documents-from-a-factory-case-1
(let [t (-> 2 weeks ago)
doc (build "domains" "clojure" :created-at t)]
(is (:_id doc))
(is (= t (:created-at doc)))
(is (= "clojure.org" (:name doc)))
(is (false? (:ipv6-enabled doc)))))
(deftest test-building-documents-from-a-factory-case-2
(let [oid (ObjectId.)
doc (build "domains" "clojure" :_id oid)]
(is (= oid (:_id doc)))
(is (= "clojure.org" (:name doc)))
(is (false? (:ipv6-enabled doc)))))
(deftest test-building-documents-from-a-factory-case-3
(let [oid (ObjectId.)
t (-> 3 weeks ago)
doc (build "domains" "clojure" :_id oid :created-at t :name "clojurewerkz.org" :ipv6-enabled true)]
(is (= oid (:_id doc)))
(is (= t (:created-at doc)))
(is (= "clojurewerkz.org" (:name doc)))
(is (:ipv6-enabled doc))
(is (= ["/lisp" "/jvm_hosted" "/runtime_polymorphism"]
(vec (map :name (:embedded doc)))))))
(deftest test-building-documents-from-a-factory-case-4
(let [doc (build "domains" "elixir")]
(is (:_id doc))
(is (= (:_id doc) (memoized-oid "domains" "elixir")))
(is (instance? DateTime (:created-at doc)))
(is (= ["erlang" "python" "ruby"] (get-in doc [:related :terms])))
(is (= "elixir-lang.org" (:name doc)))
(is (not (:ipv6-enabled doc)))))
(deftest test-building-child-documents-with-a-parent-ref-case-1
(let [doc (build "pages" "http://clojure.org/rationale")]
(is (:domain-id doc))))
(deftest test-building-child-documents-that-use-memoized-oids-for-parents
(let [doc (build "pages" "http://elixir-lang.org/getting_started")]
(is (= (:domain-id doc) (memoized-oid "domains" "elixir")))))
(deftest test-seeding-documents-using-a-factory-case-1
(is (mc/empty? "domains"))
(let [t (-> 2 weeks ago)
doc (seed "domains" "clojure" :created-at t)]
(is (= 1 (mc/count "domains")))
(is (:_id doc))
(is (= (:_id doc) (last-oid-of "domains" "clojure")))
(is (= t (:created-at doc)))
(is (= "clojure.org" (:name doc)))
(is (false? (:ipv6-enabled doc)))))
(deftest test-seeding-documents-using-a-factory-case-2
(is (mc/empty? "domains"))
(let [doc (seed "domains" "elixir")
loaded (first (mc/find-maps "domains"))]
(is (= 1 (mc/count "domains")))
(is (:_id doc))
(is (= (:_id doc) (:_id loaded)))
(is (instance? DateTime (:created-at loaded)))
(is (= ["erlang" "python" "ruby"] (get-in loaded [:related :terms])))
(is (= "elixir-lang.org" (:name loaded)))
(is (not (:ipv6-enabled loaded)))))
(deftest test-seeding-child-documents-with-a-parent-ref-case-1
(is (mc/empty? "domains"))
(is (mc/empty? "pages"))
(let [page (seed "pages" "http://clojure.org/rationale")
domain (mc/find-map-by-id "domains" (:domain-id page))]
(is (= 1 (mc/count "domains")))
(is (= 1 (mc/count "pages")))
(is domain)
(is (:domain-id page))
(is (= "clojure.org" (:name domain)))
(is (= "/rationale" (:name page)))))
(deftest test-seeding-all-factories-in-a-group
(is (mc/empty? "domains"))
(is (mc/empty? "pages"))
(seed-all "pages")
(is (>= (mc/count "domains") 1))
(is (>= (mc/count "pages") 4)))
(deftest test-named-memoized-object-ids
(let [oid1 (memoized-oid "domains" "clojure.org")
oid2 (memoized-oid "domains" "python.org")]
(is (= oid1 (memoized-oid "domains" "clojure.org")))
(is (= oid1 (memoized-oid "domains" "clojure.org")))
(is (= oid1 (memoized-oid "domains" "clojure.org")))
(is (= oid1 (memoized-oid "domains" "clojure.org")))
(is (not (= oid1 oid2)))
(is (= oid2 (memoized-oid "domains" "python.org")))
(is (= oid2 (memoized-oid "domains" "python.org")))
(is (= oid2 (memoized-oid "domains" "python.org")))))

View file

@ -0,0 +1,20 @@
(ns monger.test.fixtures
(:require [monger.collection :as mgcol])
(:use monger.testkit))
;;
;; fixture functions
;;
(defcleaner people "people")
(defcleaner docs "docs")
(defcleaner things "things")
(defcleaner libraries "libraries")
(defcleaner scores "scores")
(defcleaner locations "locations")
(defcleaner domains "domains")
(defcleaner pages "pages")
(defcleaner cached "cached")
(defcleaner migrations "meta.migrations")

View file

@ -1,28 +0,0 @@
(ns monger.test.full-text-search-test
(:require [monger.core :as mg]
[monger.collection :as mc]
[monger.command :as cmd]
[monger.operators :refer :all]
[clojure.test :refer [deftest is use-fixtures]]
[monger.result :refer [acknowledged?]])
(:import com.mongodb.BasicDBObjectBuilder))
(let [conn (mg/connect)
db (mg/get-db conn "monger-test")
coll "search-docs"]
(defn purge-collections
[f]
(mc/purge-many db [coll])
(f)
(mc/purge-many db [coll]))
(use-fixtures :each purge-collections)
(deftest ^{:search true} test-basic-full-text-search-query
(mc/ensure-index db coll (array-map :subject "text" :content "text"))
(mc/insert db coll {:subject "hello there" :content "this should be searchable"})
(mc/insert db coll {:subject "untitled" :content "this is just noize"})
(let [xs (mc/find-maps db coll {$text {$search "hello"}})]
(is (= 1 (count xs)))
(is (= "hello there" (-> xs first :subject))))))

View file

@ -1,114 +1,106 @@
(ns monger.test.gridfs-test (ns monger.test.gridfs-test
(:refer-clojure :exclude [count remove find]) (:refer-clojure :exclude [count remove find])
(:use clojure.test
[monger.core :only [count]]
monger.test.fixtures
[monger operators conversion]
[monger.gridfs :only (store make-input-file store-file filename content-type metadata)])
(:require [monger.gridfs :as gridfs] (:require [monger.gridfs :as gridfs]
[clojure.java.io :as io] [monger.test.helper :as helper]
[clojure.test :refer :all] [clojure.java.io :as io])
[monger.core :as mg :refer [count]]
[monger.operators :refer :all]
[monger.conversion :refer :all]
[monger.gridfs :refer [store make-input-file store-file filename content-type metadata]])
(:import [java.io InputStream File FileInputStream] (:import [java.io InputStream File FileInputStream]
[com.mongodb.gridfs GridFS GridFSInputFile GridFSDBFile])) [com.mongodb.gridfs GridFS GridFSInputFile GridFSDBFile]))
(let [conn (mg/connect) (defn purge-gridfs*
db (mg/get-db conn "monger-test")
fs (mg/get-gridfs conn "monger-test")]
(defn purge-gridfs*
[] []
(gridfs/remove-all fs)) (gridfs/remove-all))
(defn purge-gridfs (defn purge-gridfs
[f] [f]
(gridfs/remove-all fs) (gridfs/remove-all)
(f) (f)
(gridfs/remove-all fs)) (gridfs/remove-all))
(use-fixtures :each purge-gridfs) (use-fixtures :each purge-gridfs)
(deftest ^{:gridfs true} test-storing-files-to-gridfs-using-relative-fs-paths (helper/connect!)
(deftest ^{:gridfs true} test-storing-files-to-gridfs-using-relative-fs-paths
(let [input "./test/resources/mongo/js/mapfun1.js"] (let [input "./test/resources/mongo/js/mapfun1.js"]
(is (= 0 (count (gridfs/all-files fs)))) (is (= 0 (count (gridfs/all-files))))
(store (make-input-file fs input) (store (make-input-file input)
(.setFilename "monger.test.gridfs.file1") (.setFilename "monger.test.gridfs.file1")
(.setContentType "application/octet-stream")) (.setContentType "application/octet-stream"))
(is (= 1 (count (gridfs/all-files fs)))))) (is (= 1 (count (gridfs/all-files))))))
(deftest ^{:gridfs true} test-storing-files-to-gridfs-using-file-instances (deftest ^{:gridfs true} test-storing-files-to-gridfs-using-file-instances
(let [input (io/as-file "./test/resources/mongo/js/mapfun1.js")] (let [input (io/as-file "./test/resources/mongo/js/mapfun1.js")]
(is (= 0 (count (gridfs/all-files fs)))) (is (= 0 (count (gridfs/all-files))))
(store-file (make-input-file fs input) (store-file (make-input-file input)
(filename "monger.test.gridfs.file2") (filename "monger.test.gridfs.file2")
(content-type "application/octet-stream")) (content-type "application/octet-stream"))
(is (= 1 (count (gridfs/all-files fs)))))) (is (= 1 (count (gridfs/all-files))))))
(deftest ^{:gridfs true} test-storing-bytes-to-gridfs (deftest ^{:gridfs true} test-storing-bytes-to-gridfs
(let [input (.getBytes "A string") (let [input (.getBytes "A string")
md {:format "raw" :source "AwesomeCamera D95"} md {:format "raw" :source "AwesomeCamera D95"}
fname "monger.test.gridfs.file3" fname "monger.test.gridfs.file3"
ct "application/octet-stream"] ct "application/octet-stream"]
(is (= 0 (count (gridfs/all-files fs)))) (is (= 0 (count (gridfs/all-files))))
(store-file (make-input-file fs input) (store-file (make-input-file input)
(filename fname) (filename fname)
(metadata md) (metadata md)
(content-type "application/octet-stream")) (content-type "application/octet-stream"))
(let [f (first (gridfs/files-as-maps fs))] (let [f (first (gridfs/files-as-maps))]
(is (= ct (:contentType f))) (is (= ct (:contentType f)))
(is (= fname (:filename f))) (is (= fname (:filename f)))
(is (= md (:metadata f)))) (is (= md (:metadata f))))
(is (= 1 (count (gridfs/all-files fs)))))) (is (= 1 (count (gridfs/all-files))))))
(deftest ^{:gridfs true} test-storing-files-to-gridfs-using-absolute-fs-paths (deftest ^{:gridfs true} test-storing-files-to-gridfs-using-absolute-fs-paths
(let [tmp-file (File/createTempFile "monger.test.gridfs" "test-storing-files-to-gridfs-using-absolute-fs-paths") (let [tmp-file (File/createTempFile "monger.test.gridfs" "test-storing-files-to-gridfs-using-absolute-fs-paths")
_ (spit tmp-file "Some content") _ (spit tmp-file "Some content")
input (.getAbsolutePath tmp-file)] input (.getAbsolutePath tmp-file)]
(is (= 0 (count (gridfs/all-files fs)))) (is (= 0 (count (gridfs/all-files))))
(store-file (make-input-file fs input) (store-file (make-input-file input)
(filename "monger.test.gridfs.file4") (filename "monger.test.gridfs.file4")
(content-type "application/octet-stream")) (content-type "application/octet-stream"))
(is (= 1 (count (gridfs/all-files fs)))))) (is (= 1 (count (gridfs/all-files))))))
(deftest ^{:gridfs true} test-storing-files-to-gridfs-using-input-stream (deftest ^{:gridfs true} test-storing-files-to-gridfs-using-input-stream
(let [tmp-file (File/createTempFile "monger.test.gridfs" "test-storing-files-to-gridfs-using-input-stream") (let [tmp-file (File/createTempFile "monger.test.gridfs" "test-storing-files-to-gridfs-using-input-stream")
_ (spit tmp-file "Some other content")] _ (spit tmp-file "Some other content")]
(is (= 0 (count (gridfs/all-files fs)))) (is (= 0 (count (gridfs/all-files))))
(store-file fs (store-file (make-input-file (FileInputStream. tmp-file))
(make-input-file (FileInputStream. tmp-file))
(filename "monger.test.gridfs.file4b") (filename "monger.test.gridfs.file4b")
(content-type "application/octet-stream")) (content-type "application/octet-stream"))
(is (= 1 (count (gridfs/all-files fs)))))) (is (= 1 (count (gridfs/all-files))))))
(deftest ^{:gridfs true} test-deleting-file-instance-on-disk-after-storing
(let [tmp-file (File/createTempFile "monger.test.gridfs" "test-deleting-file-instance-on-disk-after-storing")
_ (spit tmp-file "to be deleted")]
(is (= 0 (count (gridfs/all-files fs))))
(store-file (make-input-file fs tmp-file)
(filename "test-deleting-file-instance-on-disk-after-storing")
(content-type "application/octet-stream"))
(is (= 1 (count (gridfs/all-files fs))))
(is (.delete tmp-file))))
(deftest ^{:gridfs true} test-finding-individual-files-on-gridfs (deftest ^{:gridfs true} test-finding-individual-files-on-gridfs
(testing "gridfs/find-one" (testing "gridfs/find-one"
(purge-gridfs*) (purge-gridfs*)
(let [input "./test/resources/mongo/js/mapfun1.js" (let [input "./test/resources/mongo/js/mapfun1.js"
ct "binary/octet-stream" ct "binary/octet-stream"
fname "monger.test.gridfs.file5" fname "monger.test.gridfs.file5"
md5 "14a09deabb50925a3381315149017bbd" md5 "14a09deabb50925a3381315149017bbd"
stored (store-file (make-input-file fs input) stored (store-file (make-input-file input)
(filename fname) (filename fname)
(content-type ct))] (content-type ct))]
(is (= 1 (count (gridfs/all-files fs)))) (is (= 1 (count (gridfs/all-files))))
(is (:_id stored)) (is (:_id stored))
(is (:uploadDate stored)) (is (:uploadDate stored))
(is (= 62 (:length stored))) (is (= 62 (:length stored)))
(is (= md5 (:md5 stored))) (is (= md5 (:md5 stored)))
(is (= fname (:filename stored))) (is (= fname (:filename stored)))
(is (= ct (:contentType stored))) (is (= ct (:contentType stored)))
(are [a b] (is (= a (:md5 (from-db-object (gridfs/find-one fs b) true)))) (are [a b] (is (= a (:md5 (from-db-object (gridfs/find-one b) true))))
md5 {:_id (:_id stored)} md5 (:_id stored)
md5 fname
md5 (to-db-object {:md5 md5})))) md5 (to-db-object {:md5 md5}))))
(testing "gridfs/find-one-as-map" (testing "gridfs/find-one-as-map"
(purge-gridfs*) (purge-gridfs*)
@ -116,77 +108,39 @@
ct "binary/octet-stream" ct "binary/octet-stream"
fname "monger.test.gridfs.file6" fname "monger.test.gridfs.file6"
md5 "14a09deabb50925a3381315149017bbd" md5 "14a09deabb50925a3381315149017bbd"
stored (store-file (make-input-file fs input) stored (store-file (make-input-file input)
(filename fname) (filename fname)
(metadata (to-db-object {:meta "data"})) (metadata (to-db-object {:meta "data"}))
(content-type ct))] (content-type ct))]
(is (= 1 (count (gridfs/all-files fs)))) (is (= 1 (count (gridfs/all-files))))
(is (:_id stored)) (is (:_id stored))
(is (:uploadDate stored)) (is (:uploadDate stored))
(is (= 62 (:length stored))) (is (= 62 (:length stored)))
(is (= md5 (:md5 stored))) (is (= md5 (:md5 stored)))
(is (= fname (:filename stored))) (is (= fname (:filename stored)))
(is (= ct (:contentType stored))) (is (= ct (:contentType stored)))
(let [m (gridfs/find-one-as-map fs {:filename fname})] (let [m (gridfs/find-one-as-map {:filename fname})]
(is (= {:meta "data"} (:metadata m)))) (is (= {:meta "data"} (:metadata m))))
(are [a query] (is (= a (:md5 (gridfs/find-one-as-map fs query)))) (are [a query] (is (= a (:md5 (gridfs/find-one-as-map query))))
md5 {:_id (:_id stored)} md5 (:_id stored)
md5 {:md5 md5}))) md5 fname
(testing "gridfs/find-by-id" md5 {:md5 md5}))))
(purge-gridfs*)
(let [input "./test/resources/mongo/js/mapfun1.js"
ct "binary/octet-stream"
fname "monger.test.gridfs.file5"
md5 "14a09deabb50925a3381315149017bbd"
stored (store-file (make-input-file fs input)
(filename fname)
(content-type ct))]
(is (= 1 (count (gridfs/all-files fs))))
(is (:_id stored))
(is (:uploadDate stored))
(is (= 62 (:length stored)))
(is (= md5 (:md5 stored)))
(is (= fname (:filename stored)))
(is (= ct (:contentType stored)))
(are [a id] (is (= a (:md5 (from-db-object (gridfs/find-by-id fs id) true))))
md5 (:_id stored))))
(testing "gridfs/find-map-by-id"
(purge-gridfs*)
(let [input "./test/resources/mongo/js/mapfun1.js"
ct "binary/octet-stream"
fname "monger.test.gridfs.file6"
md5 "14a09deabb50925a3381315149017bbd"
stored (store-file (make-input-file fs input)
(filename fname)
(metadata (to-db-object {:meta "data"}))
(content-type ct))]
(is (= 1 (count (gridfs/all-files fs))))
(is (:_id stored))
(is (:uploadDate stored))
(is (= 62 (:length stored)))
(is (= md5 (:md5 stored)))
(is (= fname (:filename stored)))
(is (= ct (:contentType stored)))
(let [m (gridfs/find-map-by-id fs (:_id stored))]
(is (= {:meta "data"} (:metadata m))))
(are [a id] (is (= a (:md5 (gridfs/find-map-by-id fs id))))
md5 (:_id stored)))))
(deftest ^{:gridfs true} test-finding-multiple-files-on-gridfs (deftest ^{:gridfs true} test-finding-multiple-files-on-gridfs
(let [input "./test/resources/mongo/js/mapfun1.js" (let [input "./test/resources/mongo/js/mapfun1.js"
ct "binary/octet-stream" ct "binary/octet-stream"
md5 "14a09deabb50925a3381315149017bbd" md5 "14a09deabb50925a3381315149017bbd"
stored1 (store-file (make-input-file fs input) stored1 (store-file (make-input-file input)
(filename "monger.test.gridfs.file6") (filename "monger.test.gridfs.file6")
(content-type ct)) (content-type ct))
stored2 (store-file (make-input-file fs input) stored2 (store-file (make-input-file input)
(filename "monger.test.gridfs.file7") (filename "monger.test.gridfs.file7")
(content-type ct)) (content-type ct))
list1 (gridfs/find-by-filename fs "monger.test.gridfs.file6") list1 (gridfs/find "monger.test.gridfs.file6")
list2 (gridfs/find-by-filename fs "monger.test.gridfs.file7") list2 (gridfs/find "monger.test.gridfs.file7")
list3 (gridfs/find-by-filename fs "888000___.monger.test.gridfs.file") list3 (gridfs/find "888000___.monger.test.gridfs.file")
list4 (gridfs/find-by-md5 fs md5)] list4 (gridfs/find { :md5 md5 })]
(is (= 2 (count (gridfs/all-files fs)))) (is (= 2 (count (gridfs/all-files))))
(are [a b] (is (= (map #(.get ^GridFSDBFile % "_id") a) (are [a b] (is (= (map #(.get ^GridFSDBFile % "_id") a)
(map :_id b))) (map :_id b)))
list1 [stored1] list1 [stored1]
@ -195,18 +149,18 @@
list4 [stored1 stored2]))) list4 [stored1 stored2])))
(deftest ^{:gridfs true} test-removing-multiple-files-from-gridfs (deftest ^{:gridfs true} test-removing-multiple-files-from-gridfs
(let [input "./test/resources/mongo/js/mapfun1.js" (let [input "./test/resources/mongo/js/mapfun1.js"
ct "binary/octet-stream" ct "binary/octet-stream"
md5 "14a09deabb50925a3381315149017bbd" md5 "14a09deabb50925a3381315149017bbd"
stored1 (store-file (make-input-file fs input) stored1 (store-file (make-input-file input)
(filename "monger.test.gridfs.file8") (filename "monger.test.gridfs.file8")
(content-type ct)) (content-type ct))
stored2 (store-file (make-input-file fs input) stored2 (store-file (make-input-file input)
(filename "monger.test.gridfs.file9") (filename "monger.test.gridfs.file9")
(content-type ct))] (content-type ct))]
(is (= 2 (count (gridfs/all-files fs)))) (is (= 2 (count (gridfs/all-files))))
(gridfs/remove fs { :filename "monger.test.gridfs.file8" }) (gridfs/remove { :filename "monger.test.gridfs.file8" })
(is (= 1 (count (gridfs/all-files fs)))) (is (= 1 (count (gridfs/all-files))))
(gridfs/remove fs { :md5 md5 }) (gridfs/remove { :md5 md5 })
(is (= 0 (count (gridfs/all-files fs))))))) (is (= 0 (count (gridfs/all-files))))))

View file

@ -0,0 +1,17 @@
(ns monger.test.helper
(:require [monger core util])
(:import [com.mongodb WriteConcern]))
(def connected (atom false))
(defn connected?
[]
@connected)
(defn connect!
[]
(when-not (connected?)
(do
(monger.core/connect!)
(monger.core/set-db! (monger.core/get-db "monger-test"))
(monger.core/set-default-write-concern! WriteConcern/SAFE)
(reset! connected true))))

View file

@ -1,49 +1,53 @@
(ns monger.test.indexing-test (ns monger.test.indexing-test
(:import org.bson.types.ObjectId (:import org.bson.types.ObjectId
java.util.Date) java.util.Date)
(:require [monger.core :as mg] (:require [monger core util]
[monger.collection :as mc] [monger.collection :as mc]
monger.joda-time [monger.test.helper :as helper]
[clojure.test :refer :all] monger.joda-time)
[clj-time.core :refer [now seconds ago from-now]])) (:use clojure.test
monger.test.fixtures
[clj-time.core :only [now secs ago from-now]]))
(let [conn (mg/connect) (helper/connect!)
db (mg/get-db conn "monger-test")]
(deftest ^{:indexing true} test-creating-and-dropping-indexes
;;
;; indexes
;;
(deftest ^{:indexing true} test-creating-and-dropping-indexes
(let [collection "libraries"] (let [collection "libraries"]
(mc/drop-indexes db collection) (mc/drop-indexes collection)
(mc/create-index db collection {"language" 1}) (mc/create-index collection { "language" 1 })
(is (= "language_1" (is (= "language_1"
(:name (second (mc/indexes-on db collection))))) (:name (second (mc/indexes-on collection)))))
(mc/drop-indexes db collection) (mc/drop-index collection "language_1")
(is (nil? (second (mc/indexes-on db collection)))) (mc/create-index collection ["language"])
(mc/ensure-index db collection (array-map "language" 1) {:unique true}) (mc/drop-index collection "language_1")
(is (nil? (second (mc/indexes-on collection))))
(mc/ensure-index collection { "language" 1 } {:unique true})
(is (= "language_1" (is (= "language_1"
(:name (second (mc/indexes-on db collection))))) (:name (second (mc/indexes-on collection)))))
(mc/drop-indexes db collection) (mc/ensure-index collection { "language" 1 })
(mc/ensure-index db collection (array-map "language" 1)) (mc/ensure-index collection { "language" 1 } { :unique true })
(mc/drop-indexes db collection) (mc/drop-indexes collection)))
(mc/ensure-index db collection (array-map "language" 1) {:unique true})
(mc/drop-indexes db collection)
(mc/ensure-index db collection (array-map "language" 1) "index-name" true)
(mc/drop-indexes db collection)))
(deftest ^{:indexing true :time-consuming true} test-ttl-collections (deftest ^{:indexing true :edge-features true :time-consuming true} test-ttl-collections
(let [coll "recent_events" (let [coll "recent_events"
ttl 15 ttl 30
sleep 65] sleep 120]
(mc/remove db coll) (mc/remove coll)
(mc/drop-indexes db coll) (mc/ensure-index coll {:created-at 1} {:expireAfterSeconds ttl})
(mc/ensure-index db coll (array-map :created-at 1) {:expireAfterSeconds ttl})
(dotimes [i 100] (dotimes [i 100]
(mc/insert db coll {:type "signup" :created-at (-> i seconds ago) :i i})) (mc/insert coll {:type "signup" :created-at (-> i secs ago) :i i}))
(dotimes [i 100] (dotimes [i 100]
(mc/insert db coll {:type "signup" :created-at (-> i seconds from-now) :i i})) (mc/insert coll {:type "signup" :created-at (-> i secs from-now) :i i}))
(is (= 200 (mc/count db coll {:type "signup"}))) (is (= 200 (mc/count coll {:type "signup"})))
;; sleep for > 60 seconds. MongoDB seems to run TTLMonitor once per minute, according to ;; sleep for 65 seconds. MongoDB 2.1.2 seems to run TTLMonitor once per minute, according to
;; the log. ;; the log. MK.
(println (format "Now sleeping for %d seconds to test TTL collections!" sleep)) (println (format "Now sleeping for %d seconds to test TTL collections!" sleep))
(Thread/sleep (* sleep 1000)) (Thread/sleep (* sleep 1000))
(println (format "Documents in the TTL collection: %d" (mc/count db coll {:type "signup"}))) (println (format "Documents in the TTL collection: %d" (mc/count coll {:type "signup"})))
(is (< (mc/count db coll {:type "signup"}) 100)) (is (< (mc/count coll {:type "signup"}) 100))
(mc/remove db coll)))) (mc/remove coll)))

View file

@ -1,3 +1,5 @@
(set! *warn-on-reflection* true)
(ns monger.test.inserting-test (ns monger.test.inserting-test
(:import [com.mongodb WriteResult WriteConcern DBCursor DBObject DBRef] (:import [com.mongodb WriteResult WriteConcern DBCursor DBObject DBRef]
org.bson.types.ObjectId org.bson.types.ObjectId
@ -5,131 +7,118 @@
(:require [monger.core :as mg] (:require [monger.core :as mg]
[monger.util :as mu] [monger.util :as mu]
[monger.collection :as mc] [monger.collection :as mc]
[clojure.test :refer :all] [monger.test.helper :as helper])
[monger.operators :refer :all] (:use clojure.test
[monger.conversion :refer :all])) monger.operators
monger.conversion
monger.test.fixtures))
(helper/connect!)
(use-fixtures :each purge-people purge-docs purge-things purge-libraries)
;;
;; insert
;;
(deftest insert-a-basic-document-without-id-and-with-default-write-concern
(let [collection "people"
doc {:name "Joe" :age 30}]
(is (monger.result/ok? (mc/insert "people" doc)))
(is (= 1 (mc/count collection)))))
(deftest insert-a-basic-document-with-explicitly-passed-database-without-id-and-with-default-write-concern
(let [collection "people"
doc {:name "Joe" :age 30}]
(dotimes [n 5]
(is (monger.result/ok? (mc/insert monger.core/*mongodb-database* "people" doc WriteConcern/SAFE))))
(is (= 5 (mc/count collection)))))
(deftest insert-a-basic-document-without-id-and-with-explicit-write-concern
(let [collection "people"
doc {:name "Joe" :age 30}]
(is (monger.result/ok? (mc/insert "people" doc WriteConcern/SAFE)))
(is (= 1 (mc/count collection)))))
(deftest insert-a-basic-db-object-without-id-and-with-default-write-concern
(let [collection "people"
doc (to-db-object {:name "Joe" :age 30})]
(is (nil? (.get ^DBObject doc "_id")))
(mc/insert "people" doc)
(is (not (nil? (monger.util/get-id doc))))))
(deftest insert-a-map-with-id-and-with-default-write-concern
(let [collection "people"
id (ObjectId.)
doc {:name "Joe" :age 30 "_id" id}
result (mc/insert "people" doc)]
(is (= id (monger.util/get-id doc)))))
(deftest insert-a-document-with-clojure-ratio-in-it
(let [collection "widgets"
id (ObjectId.)
doc {:ratio 11/2 "_id" id}
result (mc/insert "widgets" doc)]
(is (= 5.5 (:ratio (mc/find-map-by-id collection id))))))
(deftest insert-a-document-with-clojure-keyword-in-it
(let [collection "widgets"
id (ObjectId.)
doc {:keyword :kwd "_id" id}
result (mc/insert "widgets" doc)]
(is (= (name :kwd) (:keyword (mc/find-map-by-id collection id))))))
(deftest insert-a-document-with-clojure-keyword-in-a-set-in-it
(let [collection "widgets"
id (ObjectId.)
doc {:keyword1 {:keyword2 #{:kw1 :kw2}} "_id" id}
result (mc/insert "widgets" doc)]
(is (= (sort ["kw1" "kw2"])
(sort (get-in (mc/find-map-by-id collection id) [:keyword1 :keyword2]))))))
(defrecord Metrics (defrecord Metrics
[rps eps]) [rps eps])
(let [conn (mg/connect) (deftest insert-a-document-with-clojure-record-in-it
db (mg/get-db conn "monger-test")]
(defn purge-collections
[f]
(mc/remove db "people")
(mc/remove db "docs")
(mc/remove db "things")
(mc/remove db "widgets")
(f)
(mc/remove db "people")
(mc/remove db "docs")
(mc/remove db "things")
(mc/remove db "widgets"))
(use-fixtures :each purge-collections)
;;
;; insert
;;
(deftest insert-a-basic-document-without-id-and-with-default-write-concern
(let [collection "people"
doc {:name "Joe" :age 30}]
(is (mc/insert db collection doc))
(is (= 1 (mc/count db collection)))))
(deftest insert-a-basic-document-with-explicitly-passed-database-without-id-and-with-default-write-concern
(let [collection "people"
doc {:name "Joe" :age 30}]
(dotimes [n 5]
(mc/insert db collection doc WriteConcern/SAFE))
(is (= 5 (mc/count db collection)))))
(deftest insert-a-basic-document-without-id-and-with-explicit-write-concern
(let [collection "people"
doc {:name "Joe" :age 30}]
(is (mc/insert db collection doc WriteConcern/SAFE))
(is (= 1 (mc/count db collection)))))
(deftest insert-a-basic-db-object-without-id-and-with-default-write-concern
(let [collection "people"
doc (to-db-object {:name "Joe" :age 30})]
(is (nil? (.get ^DBObject doc "_id")))
(mc/insert db collection doc)
(is (not (nil? (monger.util/get-id doc))))))
(deftest insert-a-map-with-id-and-with-default-write-concern
(let [collection "people"
id (ObjectId.)
doc {:name "Joe" :age 30 "_id" id}
result (mc/insert db collection doc)]
(is (= id (monger.util/get-id doc)))))
(deftest insert-a-document-with-clojure-ratio-in-it
(let [collection "widgets"
id (ObjectId.)
doc {:ratio 11/2 "_id" id}
result (mc/insert db collection doc)]
(is (= 5.5 (:ratio (mc/find-map-by-id db collection id))))))
(deftest insert-a-document-with-clojure-keyword-in-it
(let [collection "widgets"
id (ObjectId.)
doc {:keyword :kwd "_id" id}
result (mc/insert db collection doc)]
(is (= (name :kwd) (:keyword (mc/find-map-by-id db collection id))))))
(deftest insert-a-document-with-clojure-keyword-in-a-set-in-it
(let [collection "widgets"
id (ObjectId.)
doc {:keyword1 {:keyword2 #{:kw1 :kw2}} "_id" id}
result (mc/insert db collection doc)]
(is (= (sort ["kw1" "kw2"])
(sort (get-in (mc/find-map-by-id db collection id) [:keyword1 :keyword2]))))))
(deftest insert-a-document-with-clojure-record-in-it
(let [collection "widgets" (let [collection "widgets"
id (ObjectId.) id (ObjectId.)
doc {:record (Metrics. 10 20) "_id" id} doc {:record (Metrics. 10 20) "_id" id}
result (mc/insert db collection doc)] result (mc/insert "widgets" doc)]
(is (= {:rps 10 :eps 20} (:record (mc/find-map-by-id db collection id)))))) (is (= {:rps 10 :eps 20} (:record (mc/find-map-by-id collection id))))))
;; TODO: disabled until we figure out how to implement dereferencing of DBRefs (deftest test-insert-a-document-with-dbref
;; in 3.0 in a compatible way (and if that's possible at all). MK.
#_ (deftest test-insert-a-document-with-dbref
(mc/remove db "widgets")
(mc/remove db "owners")
(let [coll1 "widgets" (let [coll1 "widgets"
coll2 "owners" coll2 "owners"
oid (ObjectId.) oid (ObjectId.)
joe (mc/insert db coll2 {:name "Joe" :_id oid}) joe (mc/insert "owners" {:name "Joe" :_id oid})
dbref (DBRef. coll2 oid)] dbref (DBRef. (mg/current-db) coll2 oid)]
(mc/insert db coll1 {:type "pentagon" :owner dbref}) (mc/insert coll1 {:type "pentagon" :owner dbref})
(let [fetched (mc/find-one-as-map db coll1 {:type "pentagon"}) (let [fetched (mc/find-one-as-map coll1 {:type "pentagon"})
fo (:owner fetched)] fo (:owner fetched)]
(is (= {:_id oid :name "Joe"} (from-db-object @fo true)))))) (is (= {:_id oid :name "Joe"} (from-db-object @fo true))))))
;; ;;
;; insert-and-return ;; insert-and-return
;; ;;
(deftest insert-and-return-a-basic-document-without-id-and-with-default-write-concern (deftest insert-and-return-a-basic-document-without-id-and-with-default-write-concern
(let [collection "people" (let [collection "people"
doc {:name "Joe" :age 30} doc {:name "Joe" :age 30}
result (mc/insert-and-return db collection doc)] result (mc/insert-and-return :people doc)]
(is (= (:name doc) (is (= (:name doc)
(:name result))) (:name result)))
(is (= (:age doc) (is (= (:age doc)
(:age result))) (:age result)))
(is (:_id result)) (is (:_id result))
(is (= 1 (mc/count db collection))))) (is (= 1 (mc/count collection)))))
(deftest insert-and-return-a-basic-document-without-id-but-with-a-write-concern (deftest insert-and-return-a-basic-document-without-id-but-with-a-write-concern
(let [collection "people" (let [collection "people"
doc {:name "Joe" :age 30 :ratio 3/4} doc {:name "Joe" :age 30 :ratio 3/4}
result (mc/insert-and-return db collection doc WriteConcern/FSYNC_SAFE)] result (mc/insert-and-return "people" doc WriteConcern/FSYNC_SAFE)]
(is (= (:name doc) (is (= (:name doc)
(:name result))) (:name result)))
(is (= (:age doc) (is (= (:age doc)
@ -137,44 +126,36 @@
(is (= (:ratio doc) (is (= (:ratio doc)
(:ratio result))) (:ratio result)))
(is (:_id result)) (is (:_id result))
(is (= 1 (mc/count db collection))))) (is (= 1 (mc/count collection)))))
(deftest insert-and-return-with-a-provided-id (deftest insert-and-return-with-a-provided-id
(let [collection "people" (let [collection "people"
oid (ObjectId.) oid (ObjectId.)
doc {:name "Joe" :age 30 :_id oid} doc {:name "Joe" :age 30 :_id oid}
result (mc/insert-and-return db collection doc)] result (mc/insert-and-return :people doc)]
(is (= (:_id result) (:_id doc) oid)) (is (= (:_id result) (:_id doc) oid))
(is (= 1 (mc/count db collection))))) (is (= 1 (mc/count collection)))))
;; ;;
;; insert-batch ;; insert-batch
;; ;;
(deftest insert-a-batch-of-basic-documents-without-ids-and-with-default-write-concern (deftest insert-a-batch-of-basic-documents-without-ids-and-with-default-write-concern
(let [collection "people" (let [collection "people"
docs [{:name "Joe" :age 30} {:name "Paul" :age 27}]] docs [{:name "Joe" :age 30} {:name "Paul" :age 27}]]
(is (mc/insert-batch db collection docs)) (is (monger.result/ok? (mc/insert-batch "people" docs)))
(is (= 2 (mc/count db collection))))) (is (= 2 (mc/count collection)))))
(deftest insert-a-batch-of-basic-documents-without-ids-and-with-explicit-write-concern (deftest insert-a-batch-of-basic-documents-without-ids-and-with-explicit-write-concern
(let [collection "people" (let [collection "people"
docs [{:name "Joe" :age 30} {:name "Paul" :age 27}]] docs [{:name "Joe" :age 30} {:name "Paul" :age 27}]]
(is (mc/insert-batch db collection docs WriteConcern/FSYNCED)) (is (monger.result/ok? (mc/insert-batch "people" docs WriteConcern/NORMAL)))
(is (= 2 (mc/count db collection))))) (is (= 2 (mc/count collection)))))
(deftest insert-a-batch-of-basic-documents-with-explicit-database-without-ids-and-with-explicit-write-concern (deftest insert-a-batch-of-basic-documents-with-explicit-database-without-ids-and-with-explicit-write-concern
(let [collection "people" (let [collection "people"
docs [{:name "Joe" :age 30} {:name "Paul" :age 27}]] docs [{:name "Joe" :age 30} {:name "Paul" :age 27}]]
(dotimes [n 44] (dotimes [n 44]
(is (mc/insert-batch db collection docs WriteConcern/FSYNCED))) (is (monger.result/ok? (mc/insert-batch monger.core/*mongodb-database* "people" docs WriteConcern/NORMAL))))
(is (= 88 (mc/count db collection))))) (is (= 88 (mc/count collection)))))
(deftest insert-a-batch-of-basic-documents-from-a-lazy-sequence
(let [collection "people"
numbers (range 0 1000)]
(is (mc/insert-batch db collection (map (fn [^long l]
{:n l})
numbers)))
(is (= (count numbers) (mc/count db collection))))))

View file

@ -0,0 +1,45 @@
(ns monger.test.internal.fn-test
(:use clojure.test
monger.internal.fn))
(deftest test-expand-all
(are [i o] (is (= (expand-all i) o))
{ :int (fn [] 1) :str "Clojure" :float (Float/valueOf 11.0) } { :int 1 :str "Clojure" :float (Float/valueOf 11.0 )}
{ :long (fn [] (Long/valueOf 11)) } { :long (Long/valueOf 11) }
{
:i 1
:l (Long/valueOf 1111)
:s "Clojure"
:d (Double/valueOf 11.1)
:f (Float/valueOf 2.5)
:v [1 2 3]
:dyn-v [(fn [] 10) (fn [] 20) (fn [] 30)]
:dyn-i (fn [] 1)
:dyn-s (fn [] "Clojure (expanded)")
:m { :nested "String" }
:dyn-m { :abc (fn [] :abc) :nested { :a { :b { :c (fn [] "d") } } } }
}
{
:i 1
:l (Long/valueOf 1111)
:s "Clojure"
:d (Double/valueOf 11.1)
:f (Float/valueOf 2.5)
:v [1 2 3]
:dyn-v [10 20 30]
:dyn-i 1
:dyn-s "Clojure (expanded)"
:m { :nested "String" }
:dyn-m {
:abc :abc
:nested { :a { :b { :c "d" } } }
}
}))
(deftest test-expand-all-with
(let [expander-fn (fn [f]
(* 3 (f)))]
(are [i o] (is (= (expand-all-with i expander-fn) o))
{ :a 1 :int (fn [] 3) } { :a 1 :int 9 }
{ :v [(fn [] 1) (fn [] 11)] :m { :inner (fn [] 3) } :s "Clojure" } { :v [3 33] :m { :inner 9 } :s "Clojure" })))

View file

@ -1,6 +1,6 @@
(ns monger.test.internal.pagination-test (ns monger.test.internal.pagination-test
(:require [clojure.test :refer :all] (:use clojure.test
[monger.internal.pagination :refer :all])) monger.internal.pagination))
(deftest test-pagination-offset (deftest test-pagination-offset
(are [a b] (= a b) (are [a b] (= a b)

View file

@ -1,6 +1,9 @@
(ns monger.test.js-test (ns monger.test.js-test
(:require monger.js (:require monger.js
[clojure.test :refer :all])) [monger.test.helper :as helper])
(:use clojure.test))
(helper/connect!)
(deftest load-js-resource-using-path-on-the-classpath (deftest load-js-resource-using-path-on-the-classpath
(are [c path] (= c (count (monger.js/load-resource path))) (are [c path] (= c (count (monger.js/load-resource path)))

View file

@ -1,16 +0,0 @@
(ns monger.test.json-cheshire-test
(:require [clojure.test :refer :all]
[monger.json]
[cheshire.core :refer :all])
(:import org.bson.types.ObjectId
org.bson.types.BSONTimestamp))
(deftest convert-dbobject-to-json
(let [input (ObjectId.)
output (generate-string input)]
(is (= (str "\"" input "\"") output))))
(deftest convert-bson-timestamp-to-json
(let [input (BSONTimestamp. 123 4)
output (generate-string input)]
(is (= "{\"time\":123,\"inc\":4}" output))))

View file

@ -1,16 +0,0 @@
(ns monger.test.json-test
(:require [clojure.test :refer :all]
[monger.json]
[clojure.data.json :as json])
(:import org.bson.types.ObjectId
org.bson.types.BSONTimestamp))
(deftest convert-dbobject-to-json
(let [input (ObjectId.)
output (json/write-str input)]
(is (= (str "\"" input "\"") output))))
(deftest convert-bson-timestamp-to-json
(let [input (BSONTimestamp. 123 4)
output (json/write-str input)]
(is (= "{\"time\":123,\"inc\":4}" output))))

View file

@ -1,55 +1,42 @@
(ns monger.test.lib-integration-test (ns monger.test.lib-integration-test
(:import [org.joda.time DateTime DateMidnight LocalDate] (:use clojure.test
monger.json
monger.joda-time
monger.conversion)
(:import [org.joda.time DateTime DateMidnight]
org.bson.types.ObjectId org.bson.types.ObjectId
com.mongodb.DBObject) com.mongodb.DBObject)
(:require monger.json (:require [clojure.data.json :as json]
monger.joda-time [clj-time.core :as t]))
[clj-time.core :as t]
[cheshire.core :as json]
[clojure.test :refer :all]
[monger.conversion :refer :all]))
(deftest ^{:integration true} serialization-of-joda-datetime-to-json (deftest serialization-of-joda-datetime-to-json-with-clojure-data-json
(let [dt (t/date-time 2011 10 13 23 55 0)] (is (= "\"2011-10-13T23:55:00.000Z\"" (json/json-str (t/date-time 2011 10 13 23 55 0)))))
(is (= "\"2011-10-13T23:55:00.000Z\""
(json/encode dt)))))
(deftest ^{:integration true} serialization-of-joda-date-to-json (deftest serialization-of-object-id-to-json-with-clojure-data-json
(let [d (.toDate (t/date-time 2011 10 13 23 55 0))] (is (= "\"4ec2d1a6b55634a935ea4ac8\"" (json/json-str (ObjectId. "4ec2d1a6b55634a935ea4ac8")))))
(is (= "\"2011-10-13T23:55:00Z\""
(json/encode d)))))
(deftest ^{:integration true} conversion-of-joda-datetime-to-db-object
(deftest conversion-of-joda-datetime-to-db-object
(let [d (to-db-object (t/date-time 2011 10 13 23 55 0))] (let [d (to-db-object (t/date-time 2011 10 13 23 55 0))]
(is (instance? java.util.Date d)) (is (instance? java.util.Date d))
(is (= 1318550100000 (.getTime ^java.util.Date d))))) (is (= 1318550100000 (.getTime ^java.util.Date d)))))
(deftest ^{:integration true} conversion-of-joda-datemidnight-to-db-object (deftest conversion-of-joda-datemidnight-to-db-object
(let [d (to-db-object (DateMidnight. (t/date-time 2011 10 13)))] (let [d (to-db-object (DateMidnight. (t/date-time 2011 10 13)))]
(is (instance? java.util.Date d)) (is (instance? java.util.Date d))
(is (= 1318464000000 (.getTime ^java.util.Date d))))) (is (= 1318464000000 (.getTime ^java.util.Date d)))))
(deftest ^{:integration true} conversion-of-joda-localdate-to-db-object
(let [d (to-db-object (LocalDate. 2011 10 13))]
(is (instance? java.util.Date d))
(is (= 111 (.getYear ^java.util.Date d))) ;; how many years since 1900
(is (= 9 (.getMonth ^java.util.Date d))) ;; java.util.Date counts from 0
(is (= 13 (.getDate ^java.util.Date d)))))
(deftest ^{:integration true} conversion-of-java-util-date-to-joda-datetime (deftest conversion-of-java-util-date-to-joda-datetime
(let [input (.toDate ^DateTime (t/date-time 2011 10 13 23 55 0)) (let [input (.toDate ^DateTime (t/date-time 2011 10 13 23 55 0))
output (from-db-object input false)] output (from-db-object input false)]
(is (instance? org.joda.time.DateTime output)) (is (instance? org.joda.time.DateTime output))
(is (= input (.toDate ^DateTime output))))) (is (= input (.toDate ^DateTime output)))))
(deftest ^{:integration true} test-reader-extensions
(deftest test-reader-extensions
(let [^DateTime d (t/date-time 2011 10 13 23 55 0)] (let [^DateTime d (t/date-time 2011 10 13 23 55 0)]
(binding [*print-dup* true] (binding [*print-dup* true]
(pr-str d)))) (pr-str d))))
(deftest ^{:integration true} test-reader-extensions-for-localdate
(let [^DateTime d (t/today)]
(binding [*print-dup* true]
(pr-str d))))

View file

@ -0,0 +1,69 @@
(ns monger.test.map-reduce-test
(:import [com.mongodb WriteResult WriteConcern DBCursor DBObject CommandResult$CommandFailure MapReduceOutput MapReduceCommand MapReduceCommand$OutputType]
org.bson.types.ObjectId
java.util.Date)
(:require [monger core util]
[monger.collection :as mc]
[monger.result :as mgres]
[clojurewerkz.support.js :as js]
[monger.test.helper :as helper])
(:use clojure.test
[monger operators conversion]
monger.test.fixtures))
(helper/connect!)
(use-fixtures :each purge-people purge-docs purge-things purge-libraries)
;;
;; Map/Reduce
;;
(let [collection "widgets"
mapper (js/load-resource "resources/mongo/js/mapfun1.js")
reducer "function(key, values) {
var result = 0;
values.forEach(function(v) { result += v });
return result;
}"
batch [{ :state "CA" :quantity 1 :price 199.00 }
{ :state "NY" :quantity 2 :price 199.00 }
{ :state "NY" :quantity 1 :price 299.00 }
{ :state "IL" :quantity 2 :price 11.50 }
{ :state "CA" :quantity 2 :price 2.95 }
{ :state "IL" :quantity 3 :price 5.50 }]
expected [{:_id "CA", :value 204.9} {:_id "IL", :value 39.5} {:_id "NY", :value 697.0}]]
(deftest test-basic-inline-map-reduce-example
(mc/remove monger.core/*mongodb-database* collection {})
(is (mgres/ok? (mc/insert-batch collection batch)))
(let [output (mc/map-reduce collection mapper reducer nil MapReduceCommand$OutputType/INLINE {})
results (from-db-object ^DBObject (.results ^MapReduceOutput output) true)]
(mgres/ok? output)
(is (= expected results))))
(deftest test-basic-map-reduce-example-that-replaces-named-collection
(mc/remove monger.core/*mongodb-database* collection {})
(is (mgres/ok? (mc/insert-batch collection batch)))
(let [output (mc/map-reduce collection mapper reducer "mr_outputs" {})
results (from-db-object ^DBObject (.results ^MapReduceOutput output) true)]
(mgres/ok? output)
(is (= 3 (monger.core/count results)))
(is (= expected
(map #(from-db-object % true) (seq results))))
(is (= expected
(map #(from-db-object % true) (mc/find "mr_outputs"))))
(.drop ^MapReduceOutput output)))
(deftest test-basic-map-reduce-example-that-merged-results-into-named-collection
(mc/remove monger.core/*mongodb-database* collection {})
(is (mgres/ok? (mc/insert-batch collection batch)))
(mc/map-reduce collection mapper reducer "merged_mr_outputs" MapReduceCommand$OutputType/MERGE {})
(is (mgres/ok? (mc/insert collection { :state "OR" :price 17.95 :quantity 4 })))
(let [^MapReduceOutput output (mc/map-reduce collection mapper reducer "merged_mr_outputs" MapReduceCommand$OutputType/MERGE {})]
(mgres/ok? output)
(is (= 4 (monger.core/count output)))
(is (= ["CA" "IL" "NY" "OR"]
(map :_id (mc/find-maps "merged_mr_outputs"))))
(.drop ^MapReduceOutput output))))

View file

@ -1,146 +1,111 @@
(set! *warn-on-reflection* true)
(ns monger.test.query-operators-test (ns monger.test.query-operators-test
(:require [monger.core :as mg] (:import [com.mongodb WriteResult WriteConcern DBCursor DBObject CommandResult$CommandFailure MapReduceOutput MapReduceCommand MapReduceCommand$OutputType]
[monger.collection :as mc] org.bson.types.ObjectId
java.util.Date)
(:require [monger core util]
[clojure stacktrace]
[monger.collection :as mgcol]
[monger.result :as mgres]
[monger.conversion :as mgcnv]
[monger.js :as js] [monger.js :as js]
[clojure.test :refer :all] [monger.test.helper :as helper])
[clojure.set :refer [difference]] (:use [clojure.test]
[monger.operators :refer :all]) [monger.operators]
(:import [com.mongodb QueryOperators])) [monger.test.fixtures]))
;; (use-fixtures :each purge-people purge-docs purge-things purge-libraries) (monger.core/connect!)
(monger.core/set-db! (monger.core/get-db "monger-test"))
(deftest every-query-operator-is-defined (use-fixtures :each purge-people purge-docs purge-things purge-libraries)
(let [driver-query-operators (->> (.getDeclaredFields QueryOperators) (map #(.get % nil)) set)
monger-query-operators (->> (ns-publics 'monger.operators) (map (comp name first)) set)
; $within is deprecated and replaced by $geoWithin since v2.4.
; $uniqueDocs is deprecated since v2.6.
deprecated-query-operators #{"$within" "$uniqueDocs"}
; Query modifier operators that are deprecated in the mongo shell since v3.2
deprecated-meta-operators #{"$comment" "$explain" "$hint" "$maxScan"
"$maxTimeMS" "$max" "$min" "$orderby"
"$returnKey" "$showDiskLoc" "$snapshot" "$query"}
undefined-non-deprecated-operators (difference driver-query-operators
deprecated-query-operators
deprecated-meta-operators
monger-query-operators)]
(is (= #{} undefined-non-deprecated-operators))))
(let [conn (mg/connect) ;;
db (mg/get-db conn "monger-test")] ;; $gt, $gte, $lt, lte
(defn purge-collections ;;
[f]
(mc/remove db "people")
(mc/remove db "libraries")
(f)
(mc/remove db "people")
(mc/remove db "libraries"))
(use-fixtures :each purge-collections) (deftest find-with-conditional-operators-comparison
;;
;; $gt, $gte, $lt, lte
;;
(deftest find-with-conditional-operators-comparison
(let [collection "libraries"] (let [collection "libraries"]
(mc/insert-batch db collection [{:language "Clojure" :name "monger" :users 1} (mgcol/insert-batch collection [{:language "Clojure" :name "monger" :users 1}
{:language "Clojure" :name "langohr" :users 5} {:language "Clojure" :name "langohr" :users 5}
{:language "Clojure" :name "incanter" :users 15} {:language "Clojure" :name "incanter" :users 15}
{:language "Scala" :name "akka" :users 150}]) {:language "Scala" :name "akka" :users 150}])
(are [a b] (= a (.count (mc/find db collection b))) (are [a b] (= a (.count (mgcol/find collection b)))
2 {:users {$gt 10}} 2 {:users {$gt 10}}
3 {:users {$gte 5}} 3 {:users {$gte 5}}
2 {:users {$lt 10}} 2 {:users {$lt 10}}
2 {:users {$lte 5}} 2 {:users {$lte 5}}
1 {:users {$gt 10 $lt 150}}))) 1 {:users {$gt 10 $lt 150}})))
;; ;;
;; $eq ;; $ne
;; ;;
(deftest find-with-eq-operator (deftest find-with-and-or-operators
(let [collection "libraries"] (let [collection "libraries"]
(mc/insert-batch db collection [{:language "Ruby" :name "mongoid" :users 1 :displayName nil} (mgcol/insert-batch collection [{:language "Ruby" :name "mongoid" :users 1}
{:language "Clojure" :name "langohr" :users 5} {:language "Clojure" :name "langohr" :users 5}
{:language "Clojure" :name "incanter" :users 15} {:language "Clojure" :name "incanter" :users 15}
{:language "Scala" :name "akka" :users 150}]) {:language "Scala" :name "akka" :users 150}])
(is (= 2 (.count (mc/find db collection {:language {$eq "Clojure"}})))))) (is (= 2 (.count (mgcol/find collection {$ne {:language "Clojure"}}))))))
;;
;; $ne
;;
(deftest find-with-ne-operator ;;
;; $and, $or, $nor
;;
(deftest find-with-and-or-operators
(let [collection "libraries"] (let [collection "libraries"]
(mc/insert-batch db collection [{:language "Ruby" :name "mongoid" :users 1} (mgcol/insert-batch collection [{:language "Ruby" :name "mongoid" :users 1}
{:language "Clojure" :name "langohr" :users 5} {:language "Clojure" :name "langohr" :users 5}
{:language "Clojure" :name "incanter" :users 15} {:language "Clojure" :name "incanter" :users 15}
{:language "Scala" :name "akka" :users 150}]) {:language "Scala" :name "akka" :users 150}])
(is (= 2 (.count (mc/find db collection {:language {$ne "Clojure"}})))))) (is (= 1 (.count (mgcol/find collection {$and [{:language "Clojure"}
;;
;; $and, $or, $nor
;;
(deftest find-with-and-or-operators
(let [collection "libraries"]
(mc/insert-batch db collection [{:language "Ruby" :name "mongoid" :users 1}
{:language "Clojure" :name "langohr" :users 5}
{:language "Clojure" :name "incanter" :users 15}
{:language "Scala" :name "akka" :users 150}])
(is (= 1 (.count (mc/find db collection {$and [{:language "Clojure"}
{:users {$gt 10}}]})))) {:users {$gt 10}}]}))))
(is (= 3 (.count (mc/find db collection {$or [{:language "Clojure"} (is (= 3 (.count (mgcol/find collection {$or [{:language "Clojure"}
{:users {$gt 10}} ]})))) {:users {$gt 10}} ]}))))
(is (= 1 (.count (mc/find db collection {$nor [{:language "Clojure"} (is (= 1 (.count (mgcol/find collection {$nor [{:language "Clojure"}
{:users {$gt 10}} ]})))))) {:users {$gt 10}} ]}))))))
;; ;;
;; $all, $in, $nin ;; $all, $in, $nin
;; ;;
(deftest find-on-embedded-arrays (deftest find-on-embedded-arrays
(let [collection "libraries"] (let [collection "libraries"]
(mc/insert-batch db collection [{:language "Clojure" :tags [ "functional" ]} (mgcol/insert-batch collection [{:language "Clojure" :tags [ "functional" ]}
{:language "Scala" :tags [ "functional" "object-oriented" ]} {:language "Scala" :tags [ "functional" "object-oriented" ]}
{:language "Ruby" :tags [ "object-oriented" "dynamic" ]}]) {:language "Ruby" :tags [ "object-oriented" "dynamic" ]}])
(is (= "Scala" (:language (first (mc/find-maps db collection {:tags {$all [ "functional" "object-oriented" ]}} ))))) (is (= "Scala" (:language (first (mgcol/find-maps collection {:tags {$all [ "functional" "object-oriented" ]}} )))))
(is (= 3 (.count (mc/find-maps db collection {:tags {$in [ "functional" "object-oriented" ]}} )))) (is (= 3 (.count (mgcol/find-maps collection {:tags {$in [ "functional" "object-oriented" ]}} ))))
(is (= 2 (.count (mc/find-maps db collection {:language {$in [ "Scala" "Ruby" ]}} )))) (is (= 2 (.count (mgcol/find-maps collection {:language {$in [ "Scala" "Ruby" ]}} ))))
(is (= 1 (.count (mc/find-maps db collection {:tags {$nin [ "dynamic" "object-oriented" ]}} )))) (is (= 1 (.count (mgcol/find-maps collection {:tags {$nin [ "dynamic" "object-oriented" ]}} ))))
(is (= 3 (.count (mc/find-maps db collection {:language {$nin [ "C#" ]}} )))))) (is (= 3 (.count (mgcol/find-maps collection {:language {$nin [ "C#" ]}} ))))))
(deftest find-with-conditional-operators-on-embedded-documents (deftest find-with-conditional-operators-on-embedded-documents
(let [collection "people"] (let [collection "people"]
(mc/insert-batch db collection [{:name "Bob" :comments [{:text "Nice!" :rating 1} (mgcol/insert-batch collection [{:name "Bob" :comments [{:text "Nice!" :rating 1}
{:text "Love it" :rating 4} {:text "Love it" :rating 4}
{:text "What?":rating -5} ]} {:text "What?":rating -5} ]}
{:name "Alice" :comments [{:text "Yeah" :rating 2} {:name "Alice" :comments [{:text "Yeah" :rating 2}
{:text "Doh" :rating 1} {:text "Doh" :rating 1}
{:text "Agreed" :rating 3}]}]) {:text "Agreed" :rating 3}]}])
(are [a b] (= a (.count (mc/find db collection b))) (are [a b] (= a (.count (mgcol/find collection b)))
1 {:comments {$elemMatch {:text "Nice!" :rating {$gte 1}}}} 1 {:comments {$elemMatch {:text "Nice!" :rating {$gte 1}}}}
2 {"comments.rating" 1} 2 {"comments.rating" 1}
1 {"comments.rating" {$gt 3}}))) 1 {"comments.rating" {$gt 3}})))
(deftest find-with-regex-operator (deftest find-with-regex-operator
(let [collection "libraries"] (let [collection "libraries"]
(mc/insert-batch db collection [{:language "Ruby" :name "Mongoid" :users 1} (mgcol/insert-batch collection [{:language "Ruby" :name "Mongoid" :users 1}
{:language "Clojure" :name "Langohr" :users 5} {:language "Clojure" :name "Langohr" :users 5}
{:language "Clojure" :name "Incanter" :users 15} {:language "Clojure" :name "Incanter" :users 15}
{:language "Scala" :name "Akka" :users 150}]) {:language "Scala" :name "Akka" :users 150}])
(are [query results] (is (= results (.count (mc/find db collection query)))) (are [query results] (is (= results (.count (mgcol/find collection query))))
{:language {$regex "Clo.*"}} 2 {:language {$regex "Clo.*"}} 2
{:language {$regex "clo.*" $options "i"}} 2 {:language {$regex "clo.*" $options "i"}} 2
{:name {$regex "aK.*" $options "i"}} 1 {:name {$regex "aK.*" $options "i"}} 1
{:language {$regex ".*by"}} 1 {:language {$regex ".*by"}} 1
{:language {$regex ".*ala.*"}} 1))) {:language {$regex ".*ala.*"}} 1)))
(deftest find-with-js-expression
(let [collection "people"]
(mc/insert-batch db collection [{:name "Bob" :placeOfBirth "New York" :address {:city "New York"}}
{:name "Alice" :placeOfBirth "New York" :address {:city "Los Angeles"}}])
(is (= 1 (.count (mc/find db collection {$where "this.placeOfBirth === this.address.city"})))))))

View file

@ -1,69 +1,58 @@
(set! *warn-on-reflection* true)
(ns monger.test.querying-test (ns monger.test.querying-test
(:refer-clojure :exclude [select find sort]) (:refer-clojure :exclude [select find sort])
(:import [com.mongodb WriteResult WriteConcern DBObject ReadPreference] (:import [com.mongodb WriteResult WriteConcern DBCursor DBObject CommandResult$CommandFailure ReadPreference]
org.bson.types.ObjectId org.bson.types.ObjectId
java.util.Date) java.util.Date)
(:require [monger.core :as mg] (:require [monger core util]
[monger.collection :as mc] [monger.collection :as mgcol]
monger.joda-time
[monger.result :as mgres] [monger.result :as mgres]
[clojure.test :refer :all] [monger.test.helper :as helper])
[monger.conversion :refer :all] (:use clojure.test
[monger.query :refer :all] monger.test.fixtures
[monger.operators :refer :all] [monger conversion query operators joda-time]
[clj-time.core :refer [date-time]])) [clj-time.core :only [date-time]]))
(let [conn (mg/connect) (helper/connect!)
db (mg/get-db conn "monger-test")]
(defn purge-collections (use-fixtures :each purge-docs purge-things purge-locations)
[f]
(mc/remove db "docs")
(mc/remove db "things")
(mc/remove db "locations")
(mc/remove db "querying_docs")
(f)
(mc/remove db "docs")
(mc/remove db "things")
(mc/remove db "locations")
(mc/remove db "querying_docs"))
(use-fixtures :each purge-collections)
;; ;;
;; monger.collection/* finders ("low-level API") ;; monger.collection/* finders ("low-level API")
;; ;;
;; by ObjectId ;; by ObjectId
(deftest query-full-document-by-object-id (deftest query-full-document-by-object-id
(let [coll "querying_docs" (let [coll "docs"
oid (ObjectId.) oid (ObjectId.)
doc { :_id oid :title "Introducing Monger" }] doc { :_id oid :title "Introducing Monger" }]
(mc/insert db coll doc) (mgcol/insert coll doc)
(is (= doc (mc/find-map-by-id db coll oid))) (is (= doc (mgcol/find-map-by-id coll oid)))
(is (= doc (mc/find-one-as-map db coll { :_id oid }))))) (is (= doc (mgcol/find-one-as-map coll { :_id oid })))))
;; exact match over string field ;; exact match over string field
(deftest query-full-document-using-exact-matching-over-string-field (deftest query-full-document-using-exact-matching-over-string-field
(let [coll "querying_docs" (let [coll "docs"
doc { :title "monger" :language "Clojure" :_id (ObjectId.) }] doc { :title "monger" :language "Clojure" :_id (ObjectId.) }]
(mc/insert db coll doc) (mgcol/insert coll doc)
(is (= [doc] (mc/find-maps db coll { :title "monger" }))) (is (= [doc] (mgcol/find-maps coll { :title "monger" })))
(is (= doc (from-db-object (first (mc/find db coll { :title "monger" })) true))))) (is (= doc (from-db-object (first (mgcol/find coll { :title "monger" })) true)))))
;; exact match over string field with limit ;; exact match over string field with limit
(deftest query-full-document-using-exact-matching-over-string-with-field-with-limit (deftest query-full-document-using-exact-matching-over-string-with-field-with-limit
(let [coll "querying_docs" (let [coll "docs"
doc1 { :title "monger" :language "Clojure" :_id (ObjectId.) } doc1 { :title "monger" :language "Clojure" :_id (ObjectId.) }
doc2 { :title "langohr" :language "Clojure" :_id (ObjectId.) } doc2 { :title "langohr" :language "Clojure" :_id (ObjectId.) }
doc3 { :title "netty" :language "Java" :_id (ObjectId.) } doc3 { :title "netty" :language "Java" :_id (ObjectId.) }
_ (mc/insert-batch db coll [doc1 doc2 doc3]) _ (mgcol/insert-batch coll [doc1 doc2 doc3])
result (with-collection db coll result (with-collection coll
(find { :title "monger" }) (find { :title "monger" })
(fields [:title, :language, :_id]) (fields [:title, :language, :_id])
(skip 0) (skip 0)
@ -72,13 +61,13 @@
(is (= [doc1] result)))) (is (= [doc1] result))))
(deftest query-full-document-using-exact-matching-over-string-field-with-limit-and-offset (deftest query-full-document-using-exact-matching-over-string-field-with-limit-and-offset
(let [coll "querying_docs" (let [coll "docs"
doc1 { :title "lucene" :language "Java" :_id (ObjectId.) } doc1 { :title "lucene" :language "Java" :_id (ObjectId.) }
doc2 { :title "joda-time" :language "Java" :_id (ObjectId.) } doc2 { :title "joda-time" :language "Java" :_id (ObjectId.) }
doc3 { :title "netty" :language "Java" :_id (ObjectId.) } doc3 { :title "netty" :language "Java" :_id (ObjectId.) }
_ (mc/insert-batch db coll [doc1 doc2 doc3]) _ (mgcol/insert-batch coll [doc1 doc2 doc3])
result (with-collection db coll result (with-collection coll
(find { :language "Java" }) (find { :language "Java" })
(skip 1) (skip 1)
(limit 2) (limit 2)
@ -86,121 +75,95 @@
(is (= 2 (count result))) (is (= 2 (count result)))
(is (= [doc1 doc3] result)))) (is (= [doc1 doc3] result))))
(deftest query-with-sorting-on-multiple-fields
(let [coll "querying_docs"
doc1 { :a 1 :b 2 :c 3 :text "Whatever" :_id (ObjectId.) }
doc2 { :a 1 :b 1 :c 4 :text "Blah " :_id (ObjectId.) }
doc3 { :a 10 :b 3 :c 1 :text "Abc" :_id (ObjectId.) }
doc4 { :a 10 :b 3 :c 3 :text "Abc" :_id (ObjectId.) }
_ (mc/insert-batch db coll [doc1 doc2 doc3 doc4])
result1 (with-collection db coll
(find {})
(limit 2)
(fields [:a :b :c :text])
(sort (sorted-map :a 1 :b 1 :text -1)))
result2 (with-collection db coll
(find {})
(limit 2)
(fields [:a :b :c :text])
(sort (array-map :c 1 :text -1)))
result3 (with-collection db coll
(find {})
(limit 2)
(fields [:a :b :c :text])
(sort (array-map :c -1 :text 1)))]
(is (= [doc2 doc1] result1))
(is (= [doc3 doc1] result2))
(is (= [doc2 doc4] result3))))
;; < ($lt), <= ($lte), > ($gt), >= ($gte)
;; < ($lt), <= ($lte), > ($gt), >= ($gte) (deftest query-using-dsl-and-$lt-operator-with-integers
(let [coll "docs"
(deftest query-using-dsl-and-$lt-operator-with-integers
(let [coll "querying_docs"
doc1 { :language "Clojure" :_id (ObjectId.) :inception_year 2006 } doc1 { :language "Clojure" :_id (ObjectId.) :inception_year 2006 }
doc2 { :language "Java" :_id (ObjectId.) :inception_year 1992 } doc2 { :language "Java" :_id (ObjectId.) :inception_year 1992 }
doc3 { :language "Scala" :_id (ObjectId.) :inception_year 2003 } doc3 { :language "Scala" :_id (ObjectId.) :inception_year 2003 }
_ (mc/insert-batch db coll [doc1 doc2]) _ (mgcol/insert-batch coll [doc1 doc2])
lt-result (with-collection db coll lt-result (with-collection "docs"
(find { :inception_year { $lt 2000 } }) (find { :inception_year { $lt 2000 } })
(limit 2))] (limit 2))]
(is (= [doc2] (vec lt-result))))) (is (= [doc2] (vec lt-result)))))
(deftest query-using-dsl-and-$lt-operator-with-dates (deftest query-using-dsl-and-$lt-operator-with-dates
(let [coll "querying_docs" (let [coll "docs"
;; these rely on monger.joda-time being loaded. MK. ;; these rely on monger.joda-time being loaded. MK.
doc1 { :language "Clojure" :_id (ObjectId.) :inception_year (date-time 2006 1 1) } doc1 { :language "Clojure" :_id (ObjectId.) :inception_year (date-time 2006 1 1) }
doc2 { :language "Java" :_id (ObjectId.) :inception_year (date-time 1992 1 2) } doc2 { :language "Java" :_id (ObjectId.) :inception_year (date-time 1992 1 2) }
doc3 { :language "Scala" :_id (ObjectId.) :inception_year (date-time 2003 3 3) } doc3 { :language "Scala" :_id (ObjectId.) :inception_year (date-time 2003 3 3) }
_ (mc/insert-batch db coll [doc1 doc2]) _ (mgcol/insert-batch coll [doc1 doc2])
lt-result (with-collection db coll lt-result (with-collection "docs"
(find { :inception_year { $lt (date-time 2000 1 2) } }) (find { :inception_year { $lt (date-time 2000 1 2) } })
(limit 2))] (limit 2))]
(is (= (map :_id [doc2]) (is (= (map :_id [doc2])
(map :_id (vec lt-result)))))) (map :_id (vec lt-result))))))
(deftest query-using-both-$lte-and-$gte-operators-with-dates (deftest query-using-both-$lte-and-$gte-operators-with-dates
(let [coll "querying_docs" (let [coll "docs"
;; these rely on monger.joda-time being loaded. MK. ;; these rely on monger.joda-time being loaded. MK.
doc1 { :language "Clojure" :_id (ObjectId.) :inception_year (date-time 2006 1 1) } doc1 { :language "Clojure" :_id (ObjectId.) :inception_year (date-time 2006 1 1) }
doc2 { :language "Java" :_id (ObjectId.) :inception_year (date-time 1992 1 2) } doc2 { :language "Java" :_id (ObjectId.) :inception_year (date-time 1992 1 2) }
doc3 { :language "Scala" :_id (ObjectId.) :inception_year (date-time 2003 3 3) } doc3 { :language "Scala" :_id (ObjectId.) :inception_year (date-time 2003 3 3) }
_ (mc/insert-batch db coll [doc1 doc2 doc3]) _ (mgcol/insert-batch coll [doc1 doc2 doc3])
lt-result (with-collection db coll lt-result (with-collection "docs"
(find { :inception_year { $gt (date-time 2000 1 2) $lte (date-time 2007 2 2) } }) (find { :inception_year { $gt (date-time 2000 1 2) $lte (date-time 2007 2 2) } })
(sort { :inception_year 1 }))] (sort { :inception_year 1 }))]
(is (= (map :_id [doc3 doc1]) (is (= (map :_id [doc3 doc1])
(map :_id (vec lt-result)))))) (map :_id (vec lt-result))))))
(deftest query-using-$gt-$lt-$gte-$lte-operators-as-strings (deftest query-using-$gt-$lt-$gte-$lte-operators-as-strings
(let [coll "querying_docs" (let [coll "docs"
doc1 { :language "Clojure" :_id (ObjectId.) :inception_year 2006 } doc1 { :language "Clojure" :_id (ObjectId.) :inception_year 2006 }
doc2 { :language "Java" :_id (ObjectId.) :inception_year 1992 } doc2 { :language "Java" :_id (ObjectId.) :inception_year 1992 }
doc3 { :language "Scala" :_id (ObjectId.) :inception_year 2003 } doc3 { :language "Scala" :_id (ObjectId.) :inception_year 2003 }
_ (mc/insert-batch db coll [doc1 doc2 doc3])] _ (mgcol/insert-batch coll [doc1 doc2 doc3])]
(are [doc, result] (are [doc, result]
(= doc, result) (= doc, result)
(doc2 (with-collection db coll (doc2 (with-collection coll
(find { :inception_year { "$lt" 2000 } }))) (find { :inception_year { "$lt" 2000 } })))
(doc2 (with-collection db coll (doc2 (with-collection coll
(find { :inception_year { "$lte" 1992 } }))) (find { :inception_year { "$lte" 1992 } })))
(doc1 (with-collection db coll (doc1 (with-collection coll
(find { :inception_year { "$gt" 2002 } }) (find { :inception_year { "$gt" 2002 } })
(limit 1) (limit 1)
(sort { :inception_year -1 }))) (sort { :inception_year -1 })))
(doc1 (with-collection db coll (doc1 (with-collection coll
(find { :inception_year { "$gte" 2006 } })))))) (find { :inception_year { "$gte" 2006 } }))))))
(deftest query-using-$gt-$lt-$gte-$lte-operators-using-dsl-composition (deftest query-using-$gt-$lt-$gte-$lte-operators-using-dsl-composition
(let [coll "querying_docs" (let [coll "docs"
doc1 { :language "Clojure" :_id (ObjectId.) :inception_year 2006 } doc1 { :language "Clojure" :_id (ObjectId.) :inception_year 2006 }
doc2 { :language "Java" :_id (ObjectId.) :inception_year 1992 } doc2 { :language "Java" :_id (ObjectId.) :inception_year 1992 }
doc3 { :language "Scala" :_id (ObjectId.) :inception_year 2003 } doc3 { :language "Scala" :_id (ObjectId.) :inception_year 2003 }
srt (-> {} srt (-> {}
(limit 1) (limit 1)
(sort { :inception_year -1 })) (sort { :inception_year -1 }))
_ (mc/insert-batch db coll [doc1 doc2 doc3])] _ (mgcol/insert-batch coll [doc1 doc2 doc3])]
(is (= [doc1] (with-collection db coll (is (= [doc1] (with-collection coll
(find { :inception_year { "$gt" 2002 } }) (find { :inception_year { "$gt" 2002 } })
(merge srt)))))) (merge srt))))))
;; $all ;; $all
(deftest query-with-using-$all (deftest query-with-using-$all
(let [coll "querying_docs" (let [coll "docs"
doc1 { :_id (ObjectId.) :title "Clojure" :tags ["functional" "homoiconic" "syntax-oriented" "dsls" "concurrency features" "jvm"] } doc1 { :_id (ObjectId.) :title "Clojure" :tags ["functional" "homoiconic" "syntax-oriented" "dsls" "concurrency features" "jvm"] }
doc2 { :_id (ObjectId.) :title "Java" :tags ["object-oriented" "jvm"] } doc2 { :_id (ObjectId.) :title "Java" :tags ["object-oriented" "jvm"] }
doc3 { :_id (ObjectId.) :title "Scala" :tags ["functional" "object-oriented" "dsls" "concurrency features" "jvm"] } doc3 { :_id (ObjectId.) :title "Scala" :tags ["functional" "object-oriented" "dsls" "concurrency features" "jvm"] }
- (mc/insert-batch db coll [doc1 doc2 doc3]) - (mgcol/insert-batch coll [doc1 doc2 doc3])
result1 (with-collection db coll result1 (with-collection coll
(find { :tags { "$all" ["functional" "jvm" "homoiconic"] } })) (find { :tags { "$all" ["functional" "jvm" "homoiconic"] } }))
result2 (with-collection db coll result2 (with-collection coll
(find { :tags { "$all" ["functional" "native" "homoiconic"] } })) (find { :tags { "$all" ["functional" "native" "homoiconic"] } }))
result3 (with-collection db coll result3 (with-collection coll
(find { :tags { "$all" ["functional" "jvm" "dsls"] } }) (find { :tags { "$all" ["functional" "jvm" "dsls"] } })
(sort { :title 1 }))] (sort { :title 1 }))]
(is (= [doc1] result1)) (is (= [doc1] result1))
@ -209,53 +172,53 @@
(is (= doc1 (first result3))))) (is (= doc1 (first result3)))))
;; $exists ;; $exists
(deftest query-with-find-one-as-map-using-$exists (deftest query-with-find-one-as-map-using-$exists
(let [coll "querying_docs" (let [coll "docs"
doc1 { :_id (ObjectId.) :published-by "Jill The Blogger" :draft false :title "X announces another Y" } doc1 { :_id (ObjectId.) :published-by "Jill The Blogger" :draft false :title "X announces another Y" }
doc2 { :_id (ObjectId.) :draft true :title "Z announces a Y competitor" } doc2 { :_id (ObjectId.) :draft true :title "Z announces a Y competitor" }
_ (mc/insert-batch db coll [doc1 doc2]) _ (mgcol/insert-batch coll [doc1 doc2])
result1 (mc/find-one-as-map db coll { :published-by { "$exists" true } }) result1 (mgcol/find-one-as-map coll { :published-by { "$exists" true } })
result2 (mc/find-one-as-map db coll { :published-by { "$exists" false } })] result2 (mgcol/find-one-as-map coll { :published-by { "$exists" false } })]
(is (= doc1 result1)) (is (= doc1 result1))
(is (= doc2 result2)))) (is (= doc2 result2))))
;; $mod ;; $mod
(deftest query-with-find-one-as-map-using-$mod (deftest query-with-find-one-as-map-using-$mod
(let [coll "querying_docs" (let [coll "docs"
doc1 { :_id (ObjectId.) :counter 25 } doc1 { :_id (ObjectId.) :counter 25 }
doc2 { :_id (ObjectId.) :counter 32 } doc2 { :_id (ObjectId.) :counter 32 }
doc3 { :_id (ObjectId.) :counter 63 } doc3 { :_id (ObjectId.) :counter 63 }
_ (mc/insert-batch db coll [doc1 doc2 doc3]) _ (mgcol/insert-batch coll [doc1 doc2 doc3])
result1 (mc/find-one-as-map db coll { :counter { "$mod" [10, 5] } }) result1 (mgcol/find-one-as-map coll { :counter { "$mod" [10, 5] } })
result2 (mc/find-one-as-map db coll { :counter { "$mod" [10, 2] } }) result2 (mgcol/find-one-as-map coll { :counter { "$mod" [10, 2] } })
result3 (mc/find-one-as-map db coll { :counter { "$mod" [11, 1] } })] result3 (mgcol/find-one-as-map coll { :counter { "$mod" [11, 1] } })]
(is (= doc1 result1)) (is (= doc1 result1))
(is (= doc2 result2)) (is (= doc2 result2))
(is (empty? result3)))) (is (empty? result3))))
;; $ne ;; $ne
(deftest query-with-find-one-as-map-using-$ne (deftest query-with-find-one-as-map-using-$ne
(let [coll "querying_docs" (let [coll "docs"
doc1 { :_id (ObjectId.) :counter 25 } doc1 { :_id (ObjectId.) :counter 25 }
doc2 { :_id (ObjectId.) :counter 32 } doc2 { :_id (ObjectId.) :counter 32 }
_ (mc/insert-batch db coll [doc1 doc2]) _ (mgcol/insert-batch coll [doc1 doc2])
result1 (mc/find-one-as-map db coll { :counter { "$ne" 25 } }) result1 (mgcol/find-one-as-map coll { :counter { "$ne" 25 } })
result2 (mc/find-one-as-map db coll { :counter { "$ne" 32 } })] result2 (mgcol/find-one-as-map coll { :counter { "$ne" 32 } })]
(is (= doc2 result1)) (is (= doc2 result1))
(is (= doc1 result2)))) (is (= doc1 result2))))
;; ;;
;; monger.query DSL features ;; monger.query DSL features
;; ;;
;; pagination ;; pagination
(deftest query-using-pagination-dsl (deftest query-using-pagination-dsl
(let [coll "querying_docs" (let [coll "docs"
doc1 { :_id (ObjectId.) :title "Clojure" :tags ["functional" "homoiconic" "syntax-oriented" "dsls" "concurrency features" "jvm"] } doc1 { :_id (ObjectId.) :title "Clojure" :tags ["functional" "homoiconic" "syntax-oriented" "dsls" "concurrency features" "jvm"] }
doc2 { :_id (ObjectId.) :title "Java" :tags ["object-oriented" "jvm"] } doc2 { :_id (ObjectId.) :title "Java" :tags ["object-oriented" "jvm"] }
doc3 { :_id (ObjectId.) :title "Scala" :tags ["functional" "object-oriented" "dsls" "concurrency features" "jvm"] } doc3 { :_id (ObjectId.) :title "Scala" :tags ["functional" "object-oriented" "dsls" "concurrency features" "jvm"] }
@ -263,22 +226,22 @@
doc5 { :_id (ObjectId.) :title "Groovy" :tags ["dynamic" "object-oriented" "dsls" "jvm"] } doc5 { :_id (ObjectId.) :title "Groovy" :tags ["dynamic" "object-oriented" "dsls" "jvm"] }
doc6 { :_id (ObjectId.) :title "OCaml" :tags ["functional" "static" "dsls"] } doc6 { :_id (ObjectId.) :title "OCaml" :tags ["functional" "static" "dsls"] }
doc7 { :_id (ObjectId.) :title "Haskell" :tags ["functional" "static" "dsls" "concurrency features"] } doc7 { :_id (ObjectId.) :title "Haskell" :tags ["functional" "static" "dsls" "concurrency features"] }
- (mc/insert-batch db coll [doc1 doc2 doc3 doc4 doc5 doc6 doc7]) - (mgcol/insert-batch coll [doc1 doc2 doc3 doc4 doc5 doc6 doc7])
result1 (with-collection db coll result1 (with-collection coll
(find {}) (find {})
(paginate :page 1 :per-page 3) (paginate :page 1 :per-page 3)
(sort { :title 1 }) (sort { :title 1 })
(read-preference (ReadPreference/primary)) (read-preference ReadPreference/PRIMARY)
(options com.mongodb.Bytes/QUERYOPTION_NOTIMEOUT)) (options com.mongodb.Bytes/QUERYOPTION_NOTIMEOUT))
result2 (with-collection db coll result2 (with-collection coll
(find {}) (find {})
(paginate :page 2 :per-page 3) (paginate :page 2 :per-page 3)
(sort { :title 1 })) (sort { :title 1 }))
result3 (with-collection db coll result3 (with-collection coll
(find {}) (find {})
(paginate :page 3 :per-page 3) (paginate :page 3 :per-page 3)
(sort { :title 1 })) (sort { :title 1 }))
result4 (with-collection db coll result4 (with-collection coll
(find {}) (find {})
(paginate :page 10 :per-page 3) (paginate :page 10 :per-page 3)
(sort { :title 1 }))] (sort { :title 1 }))]
@ -288,8 +251,8 @@
(is (empty? result4)))) (is (empty? result4))))
(deftest combined-querying-dsl-example1 (deftest combined-querying-dsl-example1
(let [coll "querying_docs" (let [coll "docs"
ma-doc { :_id (ObjectId.) :name "Massachusetts" :iso "MA" :population 6547629 :joined_in 1788 :capital "Boston" } ma-doc { :_id (ObjectId.) :name "Massachusetts" :iso "MA" :population 6547629 :joined_in 1788 :capital "Boston" }
de-doc { :_id (ObjectId.) :name "Delaware" :iso "DE" :population 897934 :joined_in 1787 :capital "Dover" } de-doc { :_id (ObjectId.) :name "Delaware" :iso "DE" :population 897934 :joined_in 1787 :capital "Dover" }
ny-doc { :_id (ObjectId.) :name "New York" :iso "NY" :population 19378102 :joined_in 1788 :capital "Albany" } ny-doc { :_id (ObjectId.) :name "New York" :iso "NY" :population 19378102 :joined_in 1788 :capital "Albany" }
@ -297,15 +260,15 @@
tx-doc { :_id (ObjectId.) :name "Texas" :iso "TX" :population 25145561 :joined_in 1845 :capital "Austin" } tx-doc { :_id (ObjectId.) :name "Texas" :iso "TX" :population 25145561 :joined_in 1845 :capital "Austin" }
top3 (partial-query (limit 3)) top3 (partial-query (limit 3))
by-population-desc (partial-query (sort { :population -1 })) by-population-desc (partial-query (sort { :population -1 }))
_ (mc/insert-batch db coll [ma-doc de-doc ny-doc ca-doc tx-doc]) _ (mgcol/insert-batch coll [ma-doc de-doc ny-doc ca-doc tx-doc])
result (with-collection db coll result (with-collection coll
(find {}) (find {})
(merge top3) (merge top3)
(merge by-population-desc))] (merge by-population-desc))]
(is (= result [ca-doc tx-doc ny-doc])))) (is (= result [ca-doc tx-doc ny-doc]))))
(deftest combined-querying-dsl-example2 (deftest combined-querying-dsl-example2
(let [coll "querying_docs" (let [coll "docs"
ma-doc { :_id (ObjectId.) :name "Massachusetts" :iso "MA" :population 6547629 :joined_in 1788 :capital "Boston" } ma-doc { :_id (ObjectId.) :name "Massachusetts" :iso "MA" :population 6547629 :joined_in 1788 :capital "Boston" }
de-doc { :_id (ObjectId.) :name "Delaware" :iso "DE" :population 897934 :joined_in 1787 :capital "Dover" } de-doc { :_id (ObjectId.) :name "Delaware" :iso "DE" :population 897934 :joined_in 1787 :capital "Dover" }
ny-doc { :_id (ObjectId.) :name "New York" :iso "NY" :population 19378102 :joined_in 1788 :capital "Albany" } ny-doc { :_id (ObjectId.) :name "New York" :iso "NY" :population 19378102 :joined_in 1788 :capital "Albany" }
@ -313,8 +276,8 @@
tx-doc { :_id (ObjectId.) :name "Texas" :iso "TX" :population 25145561 :joined_in 1845 :capital "Austin" } tx-doc { :_id (ObjectId.) :name "Texas" :iso "TX" :population 25145561 :joined_in 1845 :capital "Austin" }
top3 (partial-query (limit 3)) top3 (partial-query (limit 3))
by-population-desc (partial-query (sort { :population -1 })) by-population-desc (partial-query (sort { :population -1 }))
_ (mc/insert-batch db coll [ma-doc de-doc ny-doc ca-doc tx-doc]) _ (mgcol/insert-batch coll [ma-doc de-doc ny-doc ca-doc tx-doc])
result (with-collection db coll result (with-collection coll
(find {}) (find {})
(merge top3) (merge top3)
(merge by-population-desc) (merge by-population-desc)
@ -322,4 +285,4 @@
;; documents have fields as strings, ;; documents have fields as strings,
;; not keywords ;; not keywords
(is (= (map #(% "name") result) (is (= (map #(% "name") result)
(map #(% :name) [ca-doc tx-doc ny-doc])))))) (map #(% :name) [ca-doc tx-doc ny-doc])))))

View file

@ -1,55 +1,40 @@
(set! *warn-on-reflection* true)
(ns monger.test.ragtime-test (ns monger.test.ragtime-test
(:require [monger.core :as mg] (:require [monger.core :as mg]
[monger.collection :as mc] [monger.collection :as mc]
monger.ragtime [monger.test.helper :as helper]
[ragtime.protocols :refer :all] monger.ragtime)
[clojure.test :refer :all])) (:use clojure.test
[monger.test.fixtures :only [purge-migrations]]
ragtime.core))
(let [conn (mg/connect) (helper/connect!)
db (mg/get-db conn "monger-test")]
(defn purge-collections
[f]
(mc/remove db "meta.migrations")
(f)
(mc/remove db "meta.migrations"))
(use-fixtures :each purge-collections) (use-fixtures :each purge-migrations)
(when-not (get (System/getenv) "CI")
(when-not (get (System/getenv) "CI")
(deftest test-add-migration-id (deftest test-add-migration-id
(let [coll "meta.migrations" (let [db (mg/get-db "monger-test")
coll "meta.migrations"
key "1"] key "1"]
(mc/remove db coll {}) (mc/remove db coll {})
(is (not (mc/any? db coll {:_id key}))) (is (not (mc/any? db coll {:_id key})))
(is (not (some #{key} (applied-migration-ids db)))) (is (not (contains? (applied-migration-ids db) key)))
(add-migration-id db key) (add-migration-id db key)
(is (mc/any? db coll {:_id key})) (is (mc/any? db coll {:_id key}))
(is (some #{key} (applied-migration-ids db))))) (is (contains? (applied-migration-ids db) key))))
(deftest test-remove-migration-id (deftest test-remove-migration-id
(let [coll "meta.migrations" (let [db (mg/get-db "monger-test")
coll "meta.migrations"
key "1"] key "1"]
(mc/remove db coll {}) (mc/remove db coll {})
(add-migration-id db key) (add-migration-id db key)
(is (mc/any? db coll {:_id key})) (is (mc/any? db coll {:_id key}))
(is (some #{key} (applied-migration-ids db))) (is (contains? (applied-migration-ids db) key))
(remove-migration-id db key) (remove-migration-id db key)
(is (not (some #{key} (applied-migration-ids db)))))) (is (not (contains? (applied-migration-ids db) key))))))
(deftest test-migrations-ordering
(let [coll "meta.migrations"
all-keys [ "9" "4" "7" "1" "5" "3" "6" "2" "8"]]
(mc/remove db coll {})
(doseq [key all-keys]
(add-migration-id db key))
(doseq [key all-keys]
(is (mc/any? db coll {:_id key}))
(is (some #{key} (applied-migration-ids db))))
(testing "Applied migrations must come out in creation order"
(is (= all-keys (applied-migration-ids db))))))))

View file

@ -1,293 +1,276 @@
(set! *warn-on-reflection* true)
(ns monger.test.regular-finders-test (ns monger.test.regular-finders-test
(:import [com.mongodb WriteResult WriteConcern DBCursor DBObject] (:import [com.mongodb WriteResult WriteConcern DBCursor DBObject]
org.bson.types.ObjectId org.bson.types.ObjectId
java.util.Date) java.util.Date)
(:require [monger.core :as mg] (:require [monger core util]
[monger.collection :as mc] [monger.collection :as mgcol]
[monger.util :as mu]
[monger.result :as mgres] [monger.result :as mgres]
[monger.conversion :as mgcnv] [monger.conversion :as mgcnv]
[clojure.test :refer :all] [monger.test.helper :as helper])
[monger.operators :refer :all] (:use clojure.test
[monger.conversion :refer [to-db-object]])) monger.operators
monger.test.fixtures))
(let [conn (mg/connect) (helper/connect!)
db (mg/get-db conn "monger-test")]
(use-fixtures :each (fn [f]
(mc/remove db "people")
(mc/remove db "docs")
(mc/remove db "regular_finders_docs")
(mc/remove db "things")
(mc/remove db "libraries")
(f)
(mc/remove db "people")
(mc/remove db "docs")
(mc/remove db "regular_finders_docs")
(mc/remove db "things")
(mc/remove db "libraries")))
;; (use-fixtures :each purge-people purge-docs purge-things purge-libraries)
;; find-one
;;
(deftest find-one-full-document-when-collection-is-empty
(let [collection "regular_finders_docs"]
(is (nil? (mc/find-one db collection {})))))
(deftest find-one-full-document-as-map-when-collection-is-empty
(let [collection "regular_finders_docs"]
(mc/remove db collection)
(is (nil? (mc/find-one-as-map db collection {})))))
(deftest find-one-full-document-when-collection-has-matches ;;
(let [collection "regular_finders_docs" ;; find-one
doc-id (mu/random-uuid) ;;
(deftest find-one-full-document-when-collection-is-empty
(let [collection "docs"]
(is (nil? (mgcol/find-one collection {})))))
(deftest find-one-full-document-as-map-when-collection-is-empty
(let [collection "docs"]
(is (nil? (mgcol/find-one-as-map collection {})))))
(deftest find-one-full-document-when-collection-has-matches
(let [collection "docs"
doc-id (monger.util/random-uuid)
doc { :data-store "MongoDB", :language "Clojure", :_id doc-id } doc { :data-store "MongoDB", :language "Clojure", :_id doc-id }
_ (mc/insert db collection doc) _ (mgcol/insert collection doc)
found-one (mc/find-one db collection { :language "Clojure" })] found-one (mgcol/find-one collection { :language "Clojure" })]
(is found-one) (is (= (:_id doc) (monger.util/get-id found-one)))
(is (= (:_id doc) (mu/get-id found-one)))
(is (= (mgcnv/from-db-object found-one true) doc)) (is (= (mgcnv/from-db-object found-one true) doc))
(is (= (mgcnv/to-db-object doc) found-one)))) (is (= (mgcnv/to-db-object doc) found-one))))
(deftest find-one-full-document-as-map-when-collection-has-matches (deftest find-one-full-document-as-map-when-collection-has-matches
(let [collection "regular_finders_docs" (let [collection "docs"
doc-id (mu/random-uuid) doc-id (monger.util/random-uuid)
doc { :data-store "MongoDB", :language "Clojure", :_id doc-id }] doc { :data-store "MongoDB", :language "Clojure", :_id doc-id }]
(mc/insert db collection doc) (mgcol/insert collection doc)
(is (= doc (mc/find-one-as-map db collection { :language "Clojure" }))))) (is (= doc (mgcol/find-one-as-map collection { :language "Clojure" })))))
(deftest find-one-partial-document-when-collection-has-matches (deftest find-one-partial-document-when-collection-has-matches
(let [collection "regular_finders_docs" (let [collection "docs"
doc-id (mu/random-uuid) doc-id (monger.util/random-uuid)
doc { :data-store "MongoDB", :language "Clojure", :_id doc-id } doc { :data-store "MongoDB", :language "Clojure", :_id doc-id }
_ (mc/insert db collection doc) _ (mgcol/insert collection doc)
loaded (mc/find-one db collection { :language "Clojure" } [:language])] loaded (mgcol/find-one collection { :language "Clojure" } [:language])]
(is (nil? (.get ^DBObject loaded "data-store"))) (is (nil? (.get ^DBObject loaded "data-store")))
(is (= doc-id (mu/get-id loaded))) (is (= doc-id (monger.util/get-id loaded)))
(is (= "Clojure" (.get ^DBObject loaded "language"))))) (is (= "Clojure" (.get ^DBObject loaded "language")))))
(deftest find-one-partial-document-using-field-negation-when-collection-has-matches (deftest find-one-partial-document-using-field-negation-when-collection-has-matches
(let [collection "regular_finders_docs" (let [collection "docs"
doc-id (mu/random-uuid) doc-id (monger.util/random-uuid)
doc { :data-store "MongoDB", :language "Clojure", :_id doc-id } doc { :data-store "MongoDB", :language "Clojure", :_id doc-id }
_ (mc/insert db collection doc) _ (mgcol/insert collection doc)
^DBObject loaded (mc/find-one db collection { :language "Clojure" } {:data-store 0 :_id 0})] ^DBObject loaded (mgcol/find-one collection { :language "Clojure" } {:data-store 0 :_id 0})]
(is (nil? (.get loaded "data-store"))) (is (nil? (.get loaded "data-store")))
(is (nil? (.get loaded "_id"))) (is (nil? (.get loaded "_id")))
(is (nil? (mu/get-id loaded))) (is (nil? (monger.util/get-id loaded)))
(is (= "Clojure" (.get loaded "language"))))) (is (= "Clojure" (.get loaded "language")))))
(deftest find-one-partial-document-as-map-when-collection-has-matches (deftest find-one-partial-document-as-map-when-collection-has-matches
(let [collection "regular_finders_docs" (let [collection "docs"
doc-id (mu/random-uuid) doc-id (monger.util/random-uuid)
doc { :data-store "MongoDB", :language "Clojure", :_id doc-id }] doc { :data-store "MongoDB", :language "Clojure", :_id doc-id }]
(mc/insert db collection doc) (mgcol/insert collection doc)
(is (= { :data-store "MongoDB", :_id doc-id } (is (= { :data-store "MongoDB", :_id doc-id } (mgcol/find-one-as-map collection { :language "Clojure" } [:data-store])))))
(mc/find-one-as-map db collection { :language "Clojure" } [:data-store])))))
(deftest find-one-partial-document-as-map-when-collection-has-matches-with-keywordize (deftest find-one-partial-document-as-map-when-collection-has-matches-with-keywordize
(let [collection "regular_finders_docs" (let [collection "docs"
doc-id (mu/random-uuid) doc-id (monger.util/random-uuid)
doc { :data-store "MongoDB", :language "Clojure", :_id doc-id } doc { :data-store "MongoDB", :language "Clojure", :_id doc-id }
fields [:data-store] fields [:data-store]
_id (mc/insert db collection doc) _id (mgcol/insert collection doc)
loaded (mc/find-one-as-map db collection { :language "Clojure" } fields true) loaded (mgcol/find-one-as-map collection { :language "Clojure" } fields true)
] ]
(is (= { :data-store "MongoDB", :_id doc-id } loaded )))) (is (= { :data-store "MongoDB", :_id doc-id } loaded ))))
(deftest find-one-partial-document-as-map-when-collection-has-matches-with-keywordize-false (deftest find-one-partial-document-as-map-when-collection-has-matches-with-keywordize-false
(let [collection "regular_finders_docs" (let [collection "docs"
doc-id (mu/random-uuid) doc-id (monger.util/random-uuid)
doc { :data-store "MongoDB", :language "Clojure", :_id doc-id } doc { :data-store "MongoDB", :language "Clojure", :_id doc-id }
fields [:data-store] fields [:data-store]
_id (mc/insert db collection doc) _id (mgcol/insert collection doc)
loaded (mc/find-one-as-map db collection { :language "Clojure" } fields false)] loaded (mgcol/find-one-as-map collection { :language "Clojure" } fields false)
]
(is (= { "_id" doc-id, "data-store" "MongoDB" } loaded )))) (is (= { "_id" doc-id, "data-store" "MongoDB" } loaded ))))
;; ;;
;; find-by-id ;; find-by-id
;; ;;
(deftest find-full-document-by-string-id-when-that-document-does-not-exist (deftest find-full-document-by-string-id-when-that-document-does-not-exist
(let [collection "libraries" (let [collection "libraries"
doc-id (mu/random-uuid)] doc-id (monger.util/random-uuid)]
(is (nil? (mc/find-by-id db collection doc-id))))) (is (nil? (mgcol/find-by-id collection doc-id)))))
(deftest find-full-document-by-string-id-when-id-is-nil (deftest find-full-document-by-string-id-when-id-is-nil
(let [collection "libraries" (let [collection "libraries"
doc-id nil] doc-id nil]
(is (thrown? IllegalArgumentException (mc/find-by-id db collection doc-id))))) (is (thrown? IllegalArgumentException (mgcol/find-by-id collection doc-id)))))
(deftest find-full-document-by-object-id-when-that-document-does-not-exist (deftest find-full-document-by-object-id-when-that-document-does-not-exist
(let [collection "libraries" (let [collection "libraries"
doc-id (ObjectId.)] doc-id (ObjectId.)]
(is (nil? (mc/find-by-id db collection doc-id))))) (is (nil? (mgcol/find-by-id collection doc-id)))))
(deftest find-full-document-by-id-as-map-when-that-document-does-not-exist (deftest find-full-document-by-id-as-map-when-that-document-does-not-exist
(let [collection "libraries" (let [collection "libraries"
doc-id (mu/random-uuid)] doc-id (monger.util/random-uuid)]
(is (nil? (mc/find-map-by-id db collection doc-id))))) (is (nil? (mgcol/find-map-by-id collection doc-id)))))
(deftest find-full-document-by-id-as-map-when-id-is-nil (deftest find-full-document-by-id-as-map-when-id-is-nil
(let [collection "libraries" (let [collection "libraries"
doc-id nil] doc-id nil]
(is (thrown? IllegalArgumentException (is (thrown? IllegalArgumentException
(mc/find-map-by-id db collection doc-id))))) (mgcol/find-map-by-id collection doc-id)))))
(deftest find-full-document-by-string-id-when-document-does-exist (deftest find-full-document-by-string-id-when-document-does-exist
(let [collection "libraries" (let [collection "libraries"
doc-id (mu/random-uuid) doc-id (monger.util/random-uuid)
doc { :data-store "MongoDB", :language "Clojure", :_id doc-id }] doc { :data-store "MongoDB", :language "Clojure", :_id doc-id }]
(mc/insert db collection doc) (mgcol/insert collection doc)
(is (= (to-db-object doc) (mc/find-by-id db collection doc-id))))) (is (= (doc (mgcol/find-by-id collection doc-id))))))
(deftest find-full-document-by-object-id-when-document-does-exist (deftest find-full-document-by-object-id-when-document-does-exist
(let [collection "libraries" (let [collection "libraries"
doc-id (ObjectId.) doc-id (ObjectId.)
doc { :data-store "MongoDB", :language "Clojure", :_id doc-id }] doc { :data-store "MongoDB", :language "Clojure", :_id doc-id }]
(mc/insert db collection doc) (mgcol/insert collection doc)
(is (= (to-db-object doc) (mc/find-by-id db collection doc-id))))) (is (= (doc (mgcol/find-by-id collection doc-id))))))
(deftest find-full-document-map-by-string-id-when-document-does-exist (deftest find-full-document-map-by-string-id-when-document-does-exist
(let [collection "libraries" (let [collection "libraries"
doc-id (mu/random-uuid) doc-id (monger.util/random-uuid)
doc { :data-store "MongoDB", :language "Clojure", :_id doc-id }] doc { :data-store "MongoDB", :language "Clojure", :_id doc-id }]
(mc/insert db collection doc) (mgcol/insert collection doc)
(is (= doc (mc/find-map-by-id db collection doc-id))))) (is (= (doc (mgcol/find-map-by-id collection doc-id))))))
(deftest find-full-document-map-by-object-id-when-document-does-exist (deftest find-full-document-map-by-object-id-when-document-does-exist
(let [collection "libraries" (let [collection "libraries"
doc-id (ObjectId.) doc-id (ObjectId.)
doc { :data-store "MongoDB", :language "Clojure", :_id doc-id }] doc { :data-store "MongoDB", :language "Clojure", :_id doc-id }]
(mc/insert db collection doc) (mgcol/insert collection doc)
(is (= doc (mc/find-map-by-id db collection doc-id))))) (is (= (doc (mgcol/find-map-by-id collection doc-id))))))
(deftest find-partial-document-by-id-when-document-does-exist (deftest find-partial-document-by-id-when-document-does-exist
(let [collection "libraries" (let [collection "libraries"
doc-id (mu/random-uuid) doc-id (monger.util/random-uuid)
doc { :data-store "MongoDB", :language "Clojure", :_id doc-id }] doc { :data-store "MongoDB", :language "Clojure", :_id doc-id }]
(mc/insert db collection doc) (mgcol/insert collection doc)
(is (= (to-db-object { :_id doc-id :language "Clojure" }) (is (= ({ :language "Clojure" } (mgcol/find-by-id collection doc-id [ :language ]))))))
(mc/find-by-id db collection doc-id [ :language ])))))
(deftest find-partial-document-as-map-by-id-when-document-does-exist (deftest find-partial-document-as-map-by-id-when-document-does-exist
(let [collection "libraries" (let [collection "libraries"
doc-id (mu/random-uuid) doc-id (monger.util/random-uuid)
fields [:data-store] fields [:data-store]
doc { :data-store "MongoDB", :language "Clojure", :_id doc-id } doc { :data-store "MongoDB", :language "Clojure", :_id doc-id }
_ (mc/insert db collection doc) _ (mgcol/insert collection doc)
loaded (mc/find-map-by-id db collection doc-id [ :language ])] loaded (mgcol/find-map-by-id collection doc-id [ :language ])]
(is (= { :language "Clojure", :_id doc-id } loaded )))) (is (= { :language "Clojure", :_id doc-id } loaded ))
)
)
;; ;;
;; find ;; find
;; ;;
(deftest find-full-document-when-collection-is-empty (deftest find-full-document-when-collection-is-empty
(let [collection "regular_finders_docs" (let [collection "docs"
cursor (mc/find db collection)] cursor (mgcol/find collection)]
(is (empty? (iterator-seq cursor))))) (is (empty? (iterator-seq cursor)))))
(deftest find-document-seq-when-collection-is-empty (deftest find-document-seq-when-collection-is-empty
(let [collection "regular_finders_docs"] (let [collection "docs"]
(is (empty? (mc/find-seq db collection))))) (is (empty? (mgcol/find-seq collection)))))
(deftest find-multiple-documents-when-collection-is-empty (deftest find-multiple-documents-when-collection-is-empty
(let [collection "libraries"] (let [collection "libraries"]
(is (empty? (mc/find db collection { :language "Scala" }))))) (is (empty? (mgcol/find collection { :language "Scala" })))))
(deftest find-multiple-maps-when-collection-is-empty (deftest find-multiple-maps-when-collection-is-empty
(let [collection "libraries"] (let [collection "libraries"]
(is (empty? (mc/find-maps db collection { :language "Scala" }))))) (is (empty? (mgcol/find-maps collection { :language "Scala" })))))
(deftest find-multiple-documents-by-regex (deftest find-multiple-documents-by-regex
(let [collection "libraries"] (let [collection "libraries"]
(mc/insert-batch db collection [{ :language "Clojure", :name "monger" } (mgcol/insert-batch collection [{ :language "Clojure", :name "monger" }
{ :language "Java", :name "nhibernate" } { :language "Java", :name "nhibernate" }
{ :language "JavaScript", :name "sprout-core" }]) { :language "JavaScript", :name "sprout-core" }])
(is (= 2 (monger.core/count (mc/find db collection { :language #"Java*" })))))) (is (= 2 (monger.core/count (mgcol/find collection { :language #"Java*" }))))))
(deftest find-multiple-documents (deftest find-multiple-documents
(let [collection "libraries"] (let [collection "libraries"]
(mc/insert-batch db collection [{ :language "Clojure", :name "monger" } (mgcol/insert-batch collection [{ :language "Clojure", :name "monger" }
{ :language "Clojure", :name "langohr" } { :language "Clojure", :name "langohr" }
{ :language "Clojure", :name "incanter" } { :language "Clojure", :name "incanter" }
{ :language "Scala", :name "akka" }]) { :language "Scala", :name "akka" }])
(is (= 1 (monger.core/count (mc/find db collection { :language "Scala" })))) (is (= 1 (monger.core/count (mgcol/find collection { :language "Scala" }))))
(is (= 3 (.count (mc/find db collection { :language "Clojure" })))) (is (= 3 (.count (mgcol/find collection { :language "Clojure" }))))
(is (empty? (mc/find db collection { :language "Java" }))))) (is (empty? (mgcol/find collection { :language "Java" })))))
(deftest find-document-specify-fields (deftest find-document-specify-fields
(let [collection "libraries" (let [collection "libraries"
_ (mc/insert db collection { :language "Clojure", :name "monger" }) _ (mgcol/insert collection { :language "Clojure", :name "monger" })
result (mc/find db collection { :language "Clojure"} [:language])] result (mgcol/find collection { :language "Clojure"} [:language])]
(is (= (set [:_id :language]) (-> (mgcnv/from-db-object (.next result) true) keys set))))) (is (= (seq [:_id :language]) (keys (mgcnv/from-db-object (.next result) true))))))
(deftest find-and-iterate-over-multiple-documents-the-hard-way (deftest find-and-iterate-over-multiple-documents-the-hard-way
(let [collection "libraries"] (let [collection "libraries"]
(mc/insert-batch db collection [{ :language "Clojure", :name "monger" } (mgcol/insert-batch collection [{ :language "Clojure", :name "monger" }
{ :language "Clojure", :name "langohr" } { :language "Clojure", :name "langohr" }
{ :language "Clojure", :name "incanter" } { :language "Clojure", :name "incanter" }
{ :language "Scala", :name "akka" }]) { :language "Scala", :name "akka" }])
(doseq [doc (take 3 (map (fn [dbo] (doseq [doc (take 3 (map (fn [dbo]
(mgcnv/from-db-object dbo true)) (mgcnv/from-db-object dbo true))
(mc/find-seq db collection { :language "Clojure" })))] (mgcol/find-seq collection { :language "Clojure" })))]
(is (= "Clojure" (:language doc)))))) (is (= "Clojure" (:language doc))))))
(deftest find-and-iterate-over-multiple-documents (deftest find-and-iterate-over-multiple-documents
(let [collection "libraries"] (let [collection "libraries"]
(mc/insert-batch db collection [{ :language "Clojure", :name "monger" } (mgcol/insert-batch collection [{ :language "Clojure", :name "monger" }
{ :language "Clojure", :name "langohr" } { :language "Clojure", :name "langohr" }
{ :language "Clojure", :name "incanter" } { :language "Clojure", :name "incanter" }
{ :language "Scala", :name "akka" }]) { :language "Scala", :name "akka" }])
(doseq [doc (take 3 (mc/find-maps db collection { :language "Clojure" }))] (doseq [doc (take 3 (mgcol/find-maps collection { :language "Clojure" }))]
(is (= "Clojure" (:language doc)))))) (is (= "Clojure" (:language doc))))))
(deftest find-multiple-maps (deftest find-multiple-maps
(let [collection "libraries"] (let [collection "libraries"]
(mc/insert-batch db collection [{ :language "Clojure", :name "monger" } (mgcol/insert-batch collection [{ :language "Clojure", :name "monger" }
{ :language "Clojure", :name "langohr" } { :language "Clojure", :name "langohr" }
{ :language "Clojure", :name "incanter" } { :language "Clojure", :name "incanter" }
{ :language "Scala", :name "akka" }]) { :language "Scala", :name "akka" }])
(is (= 1 (clojure.core/count (mc/find-maps db collection { :language "Scala" })))) (is (= 1 (clojure.core/count (mgcol/find-maps collection { :language "Scala" }))))
(is (= 3 (.count (mc/find-maps db collection { :language "Clojure" })))) (is (= 3 (.count (mgcol/find-maps collection { :language "Clojure" }))))
(is (empty? (mc/find-maps db collection { :language "Java" }))) (is (empty? (mgcol/find-maps collection { :language "Java" })))
(is (empty? (mc/find-maps db collection { :language "Java" } [:language :name]))))) (is (empty? (mgcol/find-maps monger.core/*mongodb-database* collection { :language "Java" } [:language :name])))))
(deftest find-multiple-partial-documents (deftest find-multiple-partial-documents
(let [collection "libraries"] (let [collection "libraries"]
(mc/insert-batch db collection [{ :language "Clojure", :name "monger" } (mgcol/insert-batch collection [{ :language "Clojure", :name "monger" }
{ :language "Clojure", :name "langohr" } { :language "Clojure", :name "langohr" }
{ :language "Clojure", :name "incanter" } { :language "Clojure", :name "incanter" }
{ :language "Scala", :name "akka" }]) { :language "Scala", :name "akka" }])
(let [scala-libs (mc/find db collection { :language "Scala" } [:name]) (let [scala-libs (mgcol/find collection { :language "Scala" } [:name])
clojure-libs (mc/find db collection { :language "Clojure"} [:language])] clojure-libs (mgcol/find collection { :language "Clojure"} [:language])]
(is (= 1 (.count scala-libs))) (is (= 1 (.count scala-libs)))
(is (= 3 (.count clojure-libs))) (is (= 3 (.count clojure-libs)))
(doseq [i clojure-libs] (doseq [i clojure-libs]
(let [doc (mgcnv/from-db-object i true)] (let [doc (mgcnv/from-db-object i true)]
(is (= (:language doc) "Clojure")))) (is (= (:language doc) "Clojure"))))
(is (empty? (mc/find db collection { :language "Erlang" } [:name])))))) (is (empty? (mgcol/find collection { :language "Erlang" } [:name]))))))
(deftest find-maps-with-keywordize-false
(let [collection "libraries"]
(mc/insert-batch db collection [{ :language "Clojure", :name "monger" }
{ :language "Clojure", :name "langohr" }])
(let [results (mc/find-maps db collection {:name "langohr"} [] false)]
(is (= 1 (.count results)))
(is (= (get (first results) "language") "Clojure"))))))

View file

@ -1,25 +1,54 @@
(ns monger.test.result-test (ns monger.test.result-test
(:import [com.mongodb BasicDBObject WriteResult WriteConcern] java.util.Date) (:import [com.mongodb BasicDBObject WriteResult WriteConcern] java.util.Date)
(:require [monger.core :as mg] (:require [monger core collection conversion]
[monger.collection :as mc] [monger.test.helper :as helper])
[monger.result :as mgres] (:use clojure.test))
monger.util
[clojure.test :refer :all]))
(let [conn (mg/connect) (helper/connect!)
db (mg/get-db conn "monger-test")]
(deftest test-updated-existing?-with-write-result ;;
(mc/remove db "libraries") ;; MongoCommandResult
;;
(deftest test-ok?
(let [result-that-is-not-ok-1 (doto (BasicDBObject.) (.put "ok" 0))
result-that-is-not-ok-2 (doto (BasicDBObject.) (.put "ok" "false"))
result-that-is-ok-1 (doto (BasicDBObject.) (.put "ok" 1))
result-that-is-ok-2 (doto (BasicDBObject.) (.put "ok" "true"))
result-that-is-ok-3 (doto (BasicDBObject.) (.put "ok" 1.0))]
(is (not (monger.result/ok? result-that-is-not-ok-1)))
(is (not (monger.result/ok? result-that-is-not-ok-2)))
(is (monger.result/ok? result-that-is-ok-1))
(is (monger.result/ok? result-that-is-ok-2))
(is (monger.result/ok? result-that-is-ok-3))))
(deftest test-has-error?
(let [result-that-has-no-error1 (doto (BasicDBObject.) (.put "ok" 0))
result-that-has-no-error2 (doto (BasicDBObject.) (.put "err" ""))
result-that-has-error1 (doto (BasicDBObject.) (.put "err" (BasicDBObject.)))]
(is (not (monger.result/has-error? result-that-has-no-error1)))
(is (not (monger.result/has-error? result-that-has-no-error2)))
(is (monger.result/has-error? result-that-has-error1))))
(deftest test-updated-existing?-with-db-object
(let [input1 (doto (BasicDBObject.) (.put "updatedExisting" true))
input2 (doto (BasicDBObject.) (.put "updatedExisting" false))
input3 (BasicDBObject.)]
(is (monger.result/updated-existing? input1))
(is (not (monger.result/updated-existing? input2)))
(is (not (monger.result/updated-existing? input3)))))
(deftest test-updated-existing?-with-write-result
(monger.collection/remove "libraries")
(let [collection "libraries" (let [collection "libraries"
doc-id (monger.util/random-uuid) doc-id (monger.util/random-uuid)
date (Date.) date (Date.)
doc { :created-at date :data-store "MongoDB" :language "Clojure" :_id doc-id } doc { :created-at date, :data-store "MongoDB", :language "Clojure", :_id doc-id }
modified-doc { :created-at date :data-store "MongoDB" :language "Erlang" :_id doc-id }] modified-doc { :created-at date, :data-store "MongoDB", :language "Erlang", :_id doc-id }]
(let [result (mc/update db collection { :language "Clojure" } doc {:upsert true})] (is (not (monger.result/updated-existing? (monger.collection/update collection { :language "Clojure" } doc :upsert true))))
(is (not (mgres/updated-existing? result))) (is (monger.result/updated-existing? (monger.collection/update collection { :language "Clojure" } doc :upsert true)))
(is (= 1 (mgres/affected-count result)))) (monger.result/updated-existing? (monger.collection/update collection { :language "Clojure" } modified-doc :multi false :upsert true))
(is (mgres/updated-existing? (mc/update db collection { :language "Clojure" } doc {:upsert true}))) (monger.collection/remove collection)))
(is (mgres/updated-existing? (mc/update db collection { :language "Clojure" } modified-doc {:multi false :upsert true})))
(is (= 1 (mgres/affected-count (mc/remove db collection { :_id doc-id }))))
(mc/remove db collection)
(mg/disconnect conn))))

View file

@ -1,29 +1,32 @@
(ns monger.test.ring.clojure-session-store-test (ns monger.test.ring.clojure-session-store-test
(:require [monger.core :as mg] (:require [monger core util]
[monger.collection :as mc] [monger.collection :as mc]
[clojure.test :refer :all] [monger.test.helper :as helper])
[ring.middleware.session.store :refer :all] (:use clojure.test
[monger.ring.session-store :refer :all])) ring.middleware.session.store
monger.ring.session-store))
(let [conn (mg/connect) (helper/connect!)
db (mg/get-db conn "monger-test")]
(defn purge-sessions (defn purge-sessions
[f] [f]
(mc/remove db "sessions") (mc/remove "web_sessions")
(mc/remove "sessions")
(f) (f)
(mc/remove db "sessions")) (mc/remove "web_sessions")
(mc/remove "sessions"))
(use-fixtures :each purge-sessions) (use-fixtures :each purge-sessions)
(deftest test-reading-a-session-that-does-not-exist (deftest test-reading-a-session-that-does-not-exist
(let [store (session-store db "sessions")] (let [store (session-store)]
(is (= {} (read-session store "a-missing-key-1228277"))))) (is (= {} (read-session store "a-missing-key-1228277")))))
(deftest test-reading-a-session-that-does-exist (deftest test-reading-a-session-that-does-exist
(let [store (session-store db "sessions") (let [store (session-store)
sk (write-session store nil {:library "Monger"}) sk (write-session store nil {:library "Monger"})
m (read-session store sk)] m (read-session store sk)]
(is sk) (is sk)
@ -32,8 +35,8 @@
{:library "Monger"})))) {:library "Monger"}))))
(deftest test-updating-a-session (deftest test-updating-a-session
(let [store (session-store db "sessions") (let [store (session-store "sessions")
sk1 (write-session store nil {:library "Monger"}) sk1 (write-session store nil {:library "Monger"})
sk2 (write-session store sk1 {:library "Ring"}) sk2 (write-session store sk1 {:library "Ring"})
m (read-session store sk2)] m (read-session store sk2)]
@ -43,8 +46,9 @@
(is (= (dissoc m :_id) (is (= (dissoc m :_id)
{:library "Ring"})))) {:library "Ring"}))))
(deftest test-deleting-a-session
(let [store (session-store db "sessions") (deftest test-deleting-a-session
(let [store (session-store "sessions")
sk (write-session store nil {:library "Monger"})] sk (write-session store nil {:library "Monger"})]
(is (nil? (delete-session store sk))) (is (nil? (delete-session store sk)))
(is (= {} (read-session store sk)))))) (is (= {} (read-session store sk)))))

View file

@ -1,27 +1,32 @@
(ns monger.test.ring.session-store-test (ns monger.test.ring.session-store-test
(:require [monger.core :as mg] (:require [monger core util]
[monger.collection :as mc] [monger.collection :as mc]
[clojure.test :refer :all] [monger.test.helper :as helper])
[ring.middleware.session.store :refer :all] (:use clojure.test
[monger.ring.session-store :refer :all])) ring.middleware.session.store
monger.ring.session-store))
(let [conn (mg/connect) (helper/connect!)
db (mg/get-db conn "monger-test")]
(defn purge-sessions (defn purge-sessions
[f] [f]
(mc/remove db "sessions") (mc/remove "web_sessions")
(mc/remove "sessions")
(f) (f)
(mc/remove db "sessions")) (mc/remove "web_sessions")
(mc/remove "sessions"))
(use-fixtures :each purge-sessions) (use-fixtures :each purge-sessions)
(deftest test-reading-a-session-that-does-not-exist
(let [store (monger-store db "sessions")] (deftest test-reading-a-session-that-does-not-exist
(let [store (monger-store)]
(is (= {} (read-session store "a-missing-key-1228277"))))) (is (= {} (read-session store "a-missing-key-1228277")))))
(deftest test-reading-a-session-that-does-exist
(let [store (monger-store db "sessions") (deftest test-reading-a-session-that-does-exist
(let [store (monger-store)
sk (write-session store nil {:library "Monger"}) sk (write-session store nil {:library "Monger"})
m (read-session store sk)] m (read-session store sk)]
(is sk) (is sk)
@ -29,8 +34,9 @@
(is (= (dissoc m :_id :date) (is (= (dissoc m :_id :date)
{:library "Monger"})))) {:library "Monger"}))))
(deftest test-updating-a-session
(let [store (monger-store db "sessions") (deftest test-updating-a-session
(let [store (monger-store "sessions")
sk1 (write-session store nil {:library "Monger"}) sk1 (write-session store nil {:library "Monger"})
sk2 (write-session store sk1 {:library "Ring"}) sk2 (write-session store sk1 {:library "Ring"})
m (read-session store sk2)] m (read-session store sk2)]
@ -40,15 +46,17 @@
(is (= (dissoc m :_id :date) (is (= (dissoc m :_id :date)
{:library "Ring"})))) {:library "Ring"}))))
(deftest test-deleting-a-session
(let [store (monger-store db "sessions") (deftest test-deleting-a-session
(let [store (monger-store "sessions")
sk (write-session store nil {:library "Monger"})] sk (write-session store nil {:library "Monger"})]
(is (nil? (delete-session store sk))) (is (nil? (delete-session store sk)))
(is (= {} (read-session store sk))))) (is (= {} (read-session store sk)))))
(deftest test-reader-extensions
(deftest test-reader-extensions
(let [d (java.util.Date.) (let [d (java.util.Date.)
oid (org.bson.types.ObjectId.)] oid (org.bson.types.ObjectId.)]
(binding [*print-dup* true] (binding [*print-dup* true]
(pr-str d) (pr-str d)
(pr-str oid))))) (pr-str oid))))

View file

@ -1,40 +1,42 @@
(ns monger.test.stress-test (ns monger.test.stress-test
(:require [monger.core :as mg] (:import [com.mongodb Mongo DB DBCollection WriteResult DBObject WriteConcern DBCursor]
[monger.collection :as mc] java.util.Date)
[monger.conversion :refer [to-db-object from-db-object]] (:require monger.core
[clojure.test :refer :all]) [monger.test.helper :as helper])
(:import [com.mongodb WriteConcern] (:use clojure.test))
java.util.Date))
(let [conn (mg/connect) ;;
db (mg/get-db conn "monger-test")] ;; Fixture functions
(defn purge-collection ;;
[coll f]
(mc/remove db coll) (defn purge-collection
[collection-name, f]
(monger.collection/remove collection-name)
(f) (f)
(mc/remove db coll)) (monger.collection/remove collection-name))
(defn purge-things-collection (defn purge-things-collection
[f] [f]
(purge-collection "things" f)) (purge-collection "things" f))
(use-fixtures :each purge-things-collection) (use-fixtures :each purge-things-collection)
(deftest ^{:performance true} insert-large-batches-of-documents-without-object-ids
(doseq [n [10 100 1000 10000 20000]]
;;
;; Tests
;;
(monger.core/set-default-write-concern! WriteConcern/NORMAL)
(deftest ^{:performance true} insert-large-batches-of-documents-without-object-ids
(doseq [n [1000 10000 100000]]
(let [collection "things" (let [collection "things"
docs (map (fn [i] docs (map (fn [i]
(to-db-object { :title "Untitled" :created-at (Date.) :number i })) (monger.conversion/to-db-object { :title "Untitled" :created-at (Date.) :number i }))
(take n (iterate inc 1)))] (take n (iterate inc 1)))]
(mc/remove db collection) (monger.collection/remove collection)
(println "Inserting " n " documents...") (println "Inserting " n " documents...")
(time (mc/insert-batch db collection docs)) (time (monger.collection/insert-batch collection docs))
(is (= n (mc/count db collection)))))) (is (= n (monger.collection/count collection))))))
(deftest ^{:performance true} convert-large-number-of-dbojects-to-maps
(doseq [n [10 100 1000 20000 40000]]
(let [docs (map (fn [i]
(to-db-object {:title "Untitled" :created-at (Date.) :number i}))
(take n (iterate inc 1)))]
(time (doall (map (fn [x] (from-db-object x true)) docs)))))))

View file

@ -1,169 +1,151 @@
(set! *warn-on-reflection* true)
(ns monger.test.updating-test (ns monger.test.updating-test
(:import [com.mongodb WriteResult WriteConcern DBObject] (:import [com.mongodb WriteResult WriteConcern DBCursor DBObject]
org.bson.types.ObjectId org.bson.types.ObjectId
java.util.Date) java.util.Date)
(:require [monger.core :as mg] (:require [monger core util]
[monger.collection :as mc] [monger.collection :as mc]
[monger.util :as mu]
[monger.result :as mr] [monger.result :as mr]
[clojure.test :refer :all] [monger.test.helper :as helper])
[monger.operators :refer :all] (:use clojure.test
[monger.conversion :refer [to-db-object]])) monger.operators
monger.test.fixtures
[monger.conversion :only [to-db-object]]))
(let [conn (mg/connect) (helper/connect!)
db (mg/get-db conn "monger-test")]
(defn purge-collections
[f]
(mc/remove db "people")
(mc/remove db "docs")
(mc/remove db "things")
(mc/remove db "libraries")
(f)
(mc/remove db "people")
(mc/remove db "docs")
(mc/remove db "things")
(mc/remove db "libraries"))
(use-fixtures :each purge-collections) (use-fixtures :each purge-people purge-docs purge-things purge-libraries)
(deftest ^{:updating true} update-document-by-id-without-upsert
;;
;; update, save
;;
(deftest ^{:updating true} update-document-by-id-without-upsert
(let [collection "libraries" (let [collection "libraries"
doc-id (mu/random-uuid) doc-id (monger.util/random-uuid)
date (Date.) date (Date.)
doc { :created-at date, :data-store "MongoDB", :language "Clojure", :_id doc-id } doc { :created-at date, :data-store "MongoDB", :language "Clojure", :_id doc-id }
modified-doc { :created-at date, :data-store "MongoDB", :language "Erlang", :_id doc-id }] modified-doc { :created-at date, :data-store "MongoDB", :language "Erlang", :_id doc-id }]
(mc/insert db collection doc) (mc/insert collection doc)
(is (= (to-db-object doc) (mc/find-by-id db collection doc-id))) (is (= (doc (mc/find-by-id collection doc-id))))
(mc/update db collection { :_id doc-id } { $set { :language "Erlang" } }) (mc/update collection { :_id doc-id } { :language "Erlang" })
(is (= (to-db-object modified-doc) (mc/find-by-id db collection doc-id))))) (is (= (modified-doc (mc/find-by-id collection doc-id))))))
(deftest ^{:updating true} update-document-by-id-without-upsert-using-update-by-id (deftest ^{:updating true} update-document-by-id-without-upsert-using-update-by-id
(let [collection "libraries" (let [collection "libraries"
doc-id (mu/random-uuid) doc-id (monger.util/random-uuid)
date (Date.) date (Date.)
doc { :created-at date, :data-store "MongoDB", :language "Clojure", :_id doc-id } doc { :created-at date, :data-store "MongoDB", :language "Clojure", :_id doc-id }
modified-doc { :created-at date, :data-store "MongoDB", :language "Erlang", :_id doc-id }] modified-doc { :created-at date, :data-store "MongoDB", :language "Erlang", :_id doc-id }]
(mc/insert db collection doc) (mc/insert collection doc)
(is (= (to-db-object doc) (mc/find-by-id db collection doc-id))) (is (= (doc (mc/find-by-id collection doc-id))))
(mc/update-by-id db collection doc-id { $set { :language "Erlang" } }) (mc/update-by-id collection doc-id { :language "Erlang" })
(is (= (to-db-object modified-doc) (mc/find-by-id db collection doc-id))))) (is (= (modified-doc (mc/find-by-id collection doc-id))))))
(deftest ^{:updating true} update-nested-document-fields-without-upsert-using-update-by-id (deftest ^{:updating true} update-nested-document-fields-without-upsert-using-update-by-id
(let [collection "libraries" (let [collection "libraries"
doc-id (ObjectId.) doc-id (ObjectId.)
date (Date.) date (Date.)
doc { :created-at date :data-store "MongoDB" :language { :primary "Clojure" } :_id doc-id } doc { :created-at date :data-store "MongoDB" :language { :primary "Clojure" } :_id doc-id }
modified-doc { :created-at date :data-store "MongoDB" :language { :primary "Erlang" } :_id doc-id }] modified-doc { :created-at date :data-store "MongoDB" :language { :primary "Erlang" } :_id doc-id }]
(mc/insert db collection doc) (mc/insert collection doc)
(is (= (to-db-object doc) (mc/find-by-id db collection doc-id))) (is (= (doc (mc/find-by-id collection doc-id))))
(mc/update-by-id db collection doc-id { $set { "language.primary" "Erlang" }}) (mc/update-by-id collection doc-id { $set { "language.primary" "Erlang" }})
(is (= (to-db-object modified-doc) (mc/find-by-id db collection doc-id))))) (is (= (modified-doc (mc/find-by-id collection doc-id))))))
(deftest ^{:updating true} update-multiple-documents (deftest ^{:updating true} update-multiple-documents
(let [collection "libraries"] (let [collection "libraries"]
(mc/insert-batch db collection [{ :language "Clojure", :name "monger" } (mc/insert collection { :language "Clojure", :name "monger" })
{ :language "Clojure", :name "langohr" } (mc/insert collection { :language "Clojure", :name "langohr" })
{ :language "Clojure", :name "incanter" } (mc/insert collection { :language "Clojure", :name "incanter" })
{ :language "Scala", :name "akka" }]) (mc/insert collection { :language "Scala", :name "akka" })
(is (= 3 (mc/count db collection { :language "Clojure" }))) (is (= 3 (mc/count collection { :language "Clojure" })))
(is (= 1 (mc/count db collection { :language "Scala" }))) (is (= 1 (mc/count collection { :language "Scala" })))
(is (= 0 (mc/count db collection { :language "Python" }))) (is (= 0 (mc/count collection { :language "Python" })))
(mc/update db collection { :language "Clojure" } { $set { :language "Python" } } {:multi true}) (mc/update collection { :language "Clojure" } { $set { :language "Python" } } :multi true)
(is (= 0 (mc/count db collection { :language "Clojure" }))) (is (= 0 (mc/count collection { :language "Clojure" })))
(is (= 1 (mc/count db collection { :language "Scala" }))) (is (= 1 (mc/count collection { :language "Scala" })))
(is (= 3 (mc/count db collection { :language "Python" }))))) (is (= 3 (mc/count collection { :language "Python" })))))
(deftest ^{:updating true} save-a-new-document (deftest ^{:updating true} save-a-new-document
(let [collection "people" (let [collection "people"
document {:name "Joe" :age 30}] document {:name "Joe" :age 30}]
(is (mc/save db "people" document)) (is (monger.result/ok? (mc/save "people" document)))
(is (= 1 (mc/count db collection))))) (is (= 1 (mc/count collection)))))
(deftest ^{:updating true} save-and-return-a-new-document (deftest ^{:updating true} save-and-return-a-new-document
(let [collection "people" (let [collection "people"
document {:name "Joe" :age 30} document {:name "Joe" :age 30}
returned (mc/save-and-return db "people" document)] returned (mc/save-and-return "people" document)]
(is (:_id returned)) (is (:_id returned))
(is (= document (dissoc returned :_id))) (is (= document (dissoc returned :_id)))
(is (= 1 (mc/count db collection))))) (is (= 1 (mc/count collection)))))
(deftest ^{:updating true} save-a-new-basic-db-object (deftest ^{:updating true} save-a-new-basic-db-object
(let [collection "people" (let [collection "people"
doc (to-db-object {:name "Joe" :age 30})] doc (to-db-object {:name "Joe" :age 30})]
(is (nil? (mu/get-id doc))) (is (nil? (monger.util/get-id doc)))
(mc/save db "people" doc WriteConcern/SAFE) (mc/save monger.core/*mongodb-database* "people" doc WriteConcern/SAFE)
(is (not (nil? (mu/get-id doc)))))) (is (not (nil? (monger.util/get-id doc))))))
(deftest ^{:updating true} update-an-existing-document-using-save (deftest ^{:updating true} update-an-existing-document-using-save
(let [collection "people" (let [collection "people"
doc-id "people-1" doc-id "people-1"
document { :_id doc-id, :name "Joe", :age 30 }] document { :_id doc-id, :name "Joe", :age 30 }]
(is (mc/insert db collection document)) (is (monger.result/ok? (mc/insert "people" document)))
(is (= 1 (mc/count db collection))) (is (= 1 (mc/count collection)))
(mc/save db collection { :_id doc-id, :name "Alan", :age 40 }) (mc/save collection { :_id doc-id, :name "Alan", :age 40 })
(is (= 1 (mc/count db collection { :name "Alan", :age 40 }))))) (is (= 1 (mc/count collection { :name "Alan", :age 40 })))))
(deftest ^{:updating true} update-an-existing-document-using-save-and-return (deftest ^{:updating true} update-an-existing-document-using-save-and-return
(let [collection "people" (let [collection "people"
document (mc/insert-and-return db collection {:name "Joe" :age 30}) document (mc/insert-and-return "people" {:name "Joe" :age 30})
doc-id (:_id document) doc-id (:_id document)
updated (mc/save-and-return db collection {:_id doc-id :name "Alan" :age 40})] updated (mc/save-and-return collection {:_id doc-id :name "Alan" :age 40})]
(is (= {:_id doc-id :name "Alan" :age 40} updated)) (is (= {:_id doc-id :name "Alan" :age 40} updated))
(is (= 1 (mc/count db collection))) (is (= 1 (mc/count collection)))
(is (= 1 (mc/count db collection {:name "Alan" :age 40}))))) (is (= 1 (mc/count collection {:name "Alan" :age 40})))))
(deftest ^{:updating true} set-an-attribute-on-existing-document-using-update (deftest ^{:updating true} set-an-attribute-on-existing-document-using-update
(let [collection "people" (let [collection "people"
doc-id (mu/object-id) doc-id (monger.util/object-id)
document { :_id doc-id, :name "Joe", :age 30 }] document { :_id doc-id, :name "Joe", :age 30 }]
(is (mc/insert db collection document)) (is (monger.result/ok? (mc/insert "people" document)))
(is (= 1 (mc/count db collection))) (is (= 1 (mc/count collection)))
(is (= 0 (mc/count db collection { :has_kids true }))) (is (= 0 (mc/count collection { :has_kids true })))
(mc/update db collection { :_id doc-id } { $set { :has_kids true } }) (mc/update collection { :_id doc-id } { $set { :has_kids true } })
(is (= 1 (mc/count db collection { :has_kids true }))))) (is (= 1 (mc/count collection { :has_kids true })))))
(deftest ^{:updating true} increment-multiple-fields-using-exists-operator-and-update (deftest ^{:updating true} increment-multiple-fields-using-exists-operator-and-update
(let [collection "matches" (let [collection "matches"
doc-id (mu/object-id) doc-id (monger.util/object-id)
document { :_id doc-id :abc 0 :def 10 }] document { :_id doc-id :abc 0 :def 10 }]
(mc/remove db collection) (mc/remove collection)
(is (mc/insert db collection document)) (is (monger.result/ok? (mc/insert collection document)))
(is (= 1 (mc/count db collection {:abc {$exists true} :def {$exists true}}))) (is (= 1 (mc/count collection {:abc {$exists true} :def {$exists true}})))
(mc/update db collection {:abc {$exists true} :def {$exists true}} {$inc {:abc 1 :def 0}}) (mc/update collection {:abc {$exists true} :def {$exists true}} {$inc {:abc 1 :def 0}})
(is (= 1 (mc/count db collection { :abc 1 }))))) (is (= 1 (mc/count collection { :abc 1 })))))
(deftest ^{:updating true} upsert-a-document-using-update (deftest ^{:updating true} upsert-a-document
(let [collection "libraries" (let [collection "libraries"
doc-id (mu/random-uuid) doc-id (monger.util/random-uuid)
date (Date.) date (Date.)
doc { :created-at date, :data-store "MongoDB", :language "Clojure", :_id doc-id } doc { :created-at date, :data-store "MongoDB", :language "Clojure", :_id doc-id }
modified-doc { :created-at date, :data-store "MongoDB", :language "Erlang", :_id doc-id }] modified-doc { :created-at date, :data-store "MongoDB", :language "Erlang", :_id doc-id }]
(is (not (mr/updated-existing? (mc/update db collection { :language "Clojure" } doc {:upsert true})))) (is (not (monger.result/updated-existing? (mc/update collection { :language "Clojure" } doc :upsert true))))
(is (= 1 (mc/count db collection))) (is (= 1 (mc/count collection)))
(is (mr/updated-existing? (mc/update db collection { :language "Clojure" } modified-doc {:multi false :upsert true}))) (is (monger.result/updated-existing? (mc/update collection { :language "Clojure" } modified-doc :multi false :upsert true)))
(is (= 1 (mc/count db collection))) (is (= 1 (mc/count collection)))
(is (= (to-db-object modified-doc) (mc/find-by-id db collection doc-id))) (is (= (modified-doc (mc/find-by-id collection doc-id))))
(mc/remove db collection))) (mc/remove collection)))
(deftest ^{:updating true} upsert-a-document-using-upsert
(let [collection "libraries"
doc-id (mu/random-uuid)
date (Date.)
doc {:created-at date :data-store "MongoDB" :language "Clojure" :_id doc-id}
modified-doc {:created-at date :data-store "MongoDB" :language "Erlang" :_id doc-id}]
(mc/remove db collection)
(is (not (mr/updated-existing? (mc/upsert db collection {:language "Clojure"} doc))))
(is (= 1 (mc/count db collection)))
(is (mr/updated-existing? (mc/upsert db collection {:language "Clojure"} modified-doc {:multi false})))
(is (= 1 (mc/count db collection)))
(is (= (to-db-object modified-doc) (mc/find-by-id db collection doc-id)))
(mc/remove db collection))))

View file

@ -1,7 +1,7 @@
(ns monger.test.util-test (ns monger.test.util-test
(:import com.mongodb.DBObject) (:import com.mongodb.DBObject)
(:require [monger util conversion] (:require [monger util conversion])
[clojure.test :refer :all])) (:use clojure.test))
(deftest get-object-id (deftest get-object-id

View file

@ -1,11 +0,0 @@
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<logger name="org.mongodb" level="WARN"/>
<root level="DEBUG">
<appender-ref ref="STDOUT"/>
</root>
</configuration>