Running "repairDatabase" with Monger? - mongodb

Is it possible to execute the "repairDatabase" command with Monger? If so how?

you can do like this:
(:require [clojure.test :refer :all]
[monger.core :as mc]))
(deftest ^:focused repair-db-test
(let [conn (mc/connect)
db (mc/get-db conn "test-db")]
(is (= {"ok" 1.0}
(mc/command db {:repairDatabase 1})))))

I found out that you can just do:
(monger.core/command db {:repairDatabase 1})

Related

Setting ReadPreference in Clojure MongoDb Driver Monger

How do I set ReadPreference while using find-maps in Monger ? The Monger documentation only specifies the usage with with-collection of monger.query as shown below
(ns my.service.server
(:refer-clojure :exclude [sort find])
(:require [monger.core :as mg]
[monger.query :refer :all])
(:import com.mongodb.ReadPreference))
(let [conn (mg/connect)
db (mg/get-db conn "monger-test")
coll "scores"]
;; reads from primary (master) to guarantee consistency
;; (at the cost of putting extra load on the primary)
(with-collection db coll
(find {:email "joe#example.com"})
(read-preference (ReadPreference/primary))))
One way of doing it is setting the read preference while connecting to Mongo:
(mg/connect
(mg/server-address "127.0.0.1" 27017)
(mg/mongo-options {:read-preference (com.mongodb.ReadPreference/secondaryPreferred)}))
If you're thinking about using this method in the Java API: https://mongodb.github.io/mongo-java-driver/3.4/javadoc/com/mongodb/DBCollection.html#setReadPreference-com.mongodb.ReadPreference- I did not find a way to use this via monger. However, you can roll your own functions to take advantage of this API:
(defn find-with-read-preference [db coll]
(.find (doto
(.getCollection db (name coll))
(.setReadPreference (ReadPreference/secondaryPreferred)))))
(defn find-maps-with-read-preference
([^DB db ^String coll]
(with-open [result (find-with-read-preference db coll)]
(map (fn [x] (monger.conversion/from-db-object x true)) result))))

clojure.data.xml and parsing jdbc xml object

I'm attempting to read xml (actual xml type) from a postgres database in clojure, however i'm not sure how to cast the org.postgresql.jdbc4.Jdbc4SQLXML object to something clojure.data.xml can make sense of.
When running (xml/parse ...) on this object i'm getting the following error:
IllegalArgumentException No matching method found: createXMLStreamReader for class com.sun.xml.internal.stream.XMLInputFactoryImpl clojure.lang.Reflector.invokeMatchingMethod (Reflector.java:80)
code below:
(ns clj-xml-dbms.core-test
(:require [clojure.test :refer :all]
[clojure.java.jdbc :as j]
[clojure.data.xml :as xml]
[clj-xml-dbms.core :refer :all])
(:use [clojure.pprint ] ))
(def db-spec {
:classname "org.postgres.Driver"
:subprotocol "postgres"
:subname "//localhost:5432/mydb"
:user "me"
:password "secret"
})
;; get the results of a query
(def results
(let [
sql "select row_id, xml_col from stg.some_table limit 1 "
db-connection (j/get-connection db-spec )
statement (j/prepare-statement db-connection sql)
query-results (j/query db-connection [statement]) ]
(first query-results)))
(pprint results)
;; {:row_id 18627,
;; :xml_col #object[org.postgresql.jdbc4.Jdbc4SQLXML 0x6897f635 "org.postgresql.jdbc4.Jdbc4SQLXML#6897f635"]}
;; obviously clojure.data.xml/parse isn't going to work on that:
(xml/parse (:xml_col results))
;; IllegalArgumentException No matching method found: createXMLStreamReader for class com.sun.xml.internal.stream.XMLInputFactoryImpl clojure.lang.Reflector.invokeMatchingMethod (Reflector.java:80)
I guess i could cast the xml type in the db to a string, but there has to be something cleaner that that i would think. Any tips appreciated, thanks.

ring redirect after login

(ns ...
(:require [ring.util.response :refer [ response redirect]))
My original code be-all-like
(-> (response "You are now logged in! communist party time!")
(assoc :session new-session)
(assoc :headers {"Content-Type" "text/html"}))
Which worked well, but the user still has to navigate elsewhere manually.
Trying to use http://ring-clojure.github.io/ring/ring.util.response.html#var-redirect
(-> (redirect requri)
(assoc :session new-session)
(assoc :headers {"Content-Type" "text/html"}))
doesn't do anything (aside from returning a blank page).
How can I achieve a redirect to a known uri using ring?
response is return a body.
you code is (response requri),but the param of the funtion reponse is html body,not a uri,you can use the this function
like this
(ns foo
(:require [ring.util.response :as response]))
(def requi "/")
(-> (response/redirect requri)
(assoc :session new-session)
(assoc :headers {"Content-Type" "text/html"}))
ps: if you are writing a web site.the lib-noir is a good way to control the session and other.
ipaomian has the answer.
Wanted to share a nice redirect hack:
(ns foo
(:require [ring.util.response :as response]))
(defn redirect
"Like ring.util.response/redirect but also accepts key value pairs
to assoc to response."
[url & kvs]
(let [resp (response/redirect url)]
(if kvs (apply assoc resp kvs) resp)))
(redirect "/" :session new-session :headers {"Content-Type" "text/html"})
ipaomian is right however mine worked by removing the headers. This is my code:
(:require
[ring.util.response :refer [redirect]])
(defn set-user! [id {session :session}]
(-> (redirect "/home")
(assoc :session (assoc session :user id))))

"Shared" Connection on Monger Clojure

I am start with monger today, the docs use examples like this:
(let [conn (mg/connect)
db (mg/get-db conn "monger-test")
coll "documents"]
(mc/insert db coll {:first_name "John" :last_name "Lennon"})
(mc/insert db coll {:first_name "Ringo" :last_name "Starr"})
(mc/find db coll {:first_name "Ringo"}))
All documentation examples use that structure. Always connect to MongoDB, then use db on mc/insert... . The question is, how I can put this code on a function and use it in my functions that execute mongodb queries, without repeat below code all time:
(let [conn (mg/connect)
db (mg/get-db conn "monger-test")
coll "documents"] ...
Thanks.
Here's how I did it:
(defmacro with-db [body]
`(let [conn# (mg/connect)
db# (mg/get-db conn "monger-test")]
(-> db#
~body)))
Used like this:
(with-db (mc/find-maps "mycoll"))
you can also do this without defining a macro, using just a plain old function:
(def db-name "monger-test")
(defn with-db [op & args]
(let [conn (mongo/connect)
db (mongo/get-db conn db-name)]
(apply op db args)))
(with-db monger.collection/find-maps "test-collection")
will list all the entries in the collection named "test-collection"

How do I drop or create a database from clojure.java.jdbc?

I would like to create/drop a database from clojure.java.jdbc. This fails:
(require '[clojure.java.jdbc :as sql])
(def db
{:classname "org.postgresql.Driver"
:subprotocol "postgresql"
:subname "//localhost/postgres"
:user "postgres"})
(defn drop-database [name]
(sql/do-commands (str "drop database " name)))
(sql/with-connection db
(drop-database "db_name"))
because do-commands starts a transaction, and apparently you can't drop or create databases inside a transaction. Any ideas?
Thanks!
Take the source for do-commands (here) and remove the call to transaction:
(defn drop-database [name]
(sql/with-connection db
(with-open [s (.createStatement (sql/connection))]
(.addBatch s (str "drop database " name))
(seq (.executeBatch s)))))
The transactionless execution functionality was rolled into db-do-commands.
Now this slightly simpler version is working:
(jdbc/db-do-commands postgres-db false "CREATE DATABASE foo")
If you don't specify false as the second argument, it won't work as it will attempt to start a transaction.
With newer clojure versions, the suggested approach no longer works. I was successful with this function:
(defn exec-db-command [db command]
(jdbc/with-db-connection [conn db]
(with-open [s (.createStatement (:connection conn))]
(.executeUpdate s command))))
(exec-db-command db "create database foo")
This is the only solution that worked for me
(def datasource-options {:auto-commit true
:read-only false
:connection-timeout 30000
:validation-timeout 5000
:idle-timeout 600000
:max-lifetime 1800000
:minimum-idle 10
;; :maximum-pool-size 10
:pool-name "db-pool"
:adapter (:database-adapter env)
:username (:database-username env)
:password (:database-password env)
:database-name (:database-name env)
:server-name (:database-host env)
:port-number (:database-port env)
:register-mbeans false})
(defonce datasource
(delay (make-datasource datasource-options)))
(defn db-jdbc-uri [& {:as args}]
(let [datasource-options (merge datasource-options args)]
(format "jdbc:%s://%s:%s/%s?user=%s&password=%s"
(datasource-options :adapter) (datasource-options :server-name)
(datasource-options :port-number) (datasource-options :database-name)
(datasource-options :username) (datasource-options :password))))
(defn create-database [name]
(println {:connection-uri (db-jdbc-uri :database-name "")})
(jdbc/with-db-connection [conn {:connection-uri (db-jdbc-uri :database-name "")}]
(jdbc/db-do-commands conn false (str "CREATE DATABASE " name))))
(defn drop-database [name]
(jdbc/with-db-connection [conn {:connection-uri (db-jdbc-uri :database-name "")}]
(jdbc/db-do-commands conn false (str "DROP DATABASE " name) )))
Basically you need to connect without providing a database or connecting to a different database (not the one you are deleting)
This will get transilated to this code.
(defn create-database [name]
(jdbc/with-db-connection [conn {:connection-uri "jdbc:postgresql://localhost/postgres?user=<name>&password=<pass>"}]
(jdbc/db-do-commands conn false (str "CREATE DATABASE " name) )))