Clojure - return value from db query - clojure

I want to created post as return value just after execute my db query function. Here is one example from my db functions:
(defn add-post-record [post]
(sql/with-connection
db
(sql/insert-record :post post )))
and what i need in my route is something like:
(def post (db/add-post-record {:title title
:body body
:owner user
:isdraft isdraft}))
Then i am gonna use this like: (:id post)
I am so new in clojure. This may be a very simple problem but i am stuck.
thank you.

I can not test this right now, but reading the documentation of insert-record and with-connection, I think something like:
(defn add-post-record [post]
(let [keys (sql/with-connection db
(sql/insert-record :post post ))]
(merge post keys))
It is not very clear to me what exactly the map returned by insert-record contains, try it out.

Related

Datomic entity-api is slow on large amount of entities?

I need to apply additional logic (like mapping, conditionals, aggregating) to entities I get from Datomic. I had hard time translating it to Datomic query (I'm not sure if it's even possible in my case), which is why I used datomic's raw index access instead, so the most work and logic is done in Clojure.
It worked fine until I got to ~500K entries and the whole approach is getting very slow.
The relevant code:
(defn e->entry
"Map e into entry"
[e]
{:id (:entry/uuid e)
;; each flat field increases mapping time (seems linearly)
:date (:entry/date e)
:summ (:entry/summ e)
;; although when using nested fields, mapping time rises significantly
:groups (map #(-> % :dimension/group :group/name)
(:entry/dimensions e))})
;; query code:
(->> (d/datoms db :aevt :entry/uuid)
(map #(->> %
:e
(d/entity db)
e->entry))))
;; TODO: other actions on mapped entries ...
It takes about 30 seconds to run query code just to map entities and the more fields I need in my query, the more it takes.
Is this an expected behavior? Is there a way I can speed things up or am I missing something and this is bad approach?
To fully answer this question would require more information, please feel free to ask on the forum or open a support ticket.
I ended up with following optimizations, in case someone will need it:
(defn eid->entry
"Mapping via :eavt index"
[db eid]
(->> (d/datoms db :eavt eid) ; access all datoms by eid once
(seq)
(reduce (fn [m dtm]
(let [attr-key (d/ident db (:a dtm))
v (:v dtm)]
(assoc m attr-key v))))))
;; new query code
(->> (d/datoms db :aevt :entry/uuid)
(pmap #(->> %
:e
(eid->entry db))))
I used pmap instead of map and resorted to :eavt index to get all attributes and values of entity instead of accessing fields directly with d/entity

Add data in Firestore with Clojure

I am new to the Firestore and Clojure.
Below is my code, Get returns the correct data, however, Set is not successful, without any exception or response.
(defn database-instance
[]
(FirestoreClient/getFirestore))
(def user (java.util.HashMap. {"age" 50
"name" "Josh"
"user_id" "user02"}))
(defn get-credit-detail!
[msg]
(def result
(-> (database-instance)
(.collection "credits")
(.document "document")
(.get)
(.get)
))
(println (.getData result))
;; this Set does not work
(-> (database-instance)
(.collection "credits")
(.document "document1")
(.set user)
)
)
Can you help me with the Set to be able to add new data to firestore?
Thanks!
A little late to answer this but what you have should work. I tried it through the REPL and verified the set worked from the firebase console. Note that you can use a Clojure map with String keys as input without having to resort to creating a java.util.Map.
(-> (database-instance)
(.collection "credits")
(.document "document1")
(.set {"age" 50 "name" "Josh" "user_id" "user02"}))

Checkbox in Luminus

I started to learn Clojure this week, specifically I'm learning web development with Luminus. Since I want to understand the CRUD process, I setup a function to save my post into the DB:
(defn save-post! [{:keys [params]}]
(if-let [errors (validate-post params)]
(-> (response/found "/posts")
(assoc :flash (assoc params :errors errors)))
(do
(db/save-post!
(assoc params :created_at (java.util.Date.)))
(response/found "/posts"))))
The query is pretty basic:
-- :name save-post! :! :n
-- :doc creates a new post record
INSERT INTO posts
(title, body, active, created_at)
VALUES (:title, :body, :active, :created_at)
but the HTML form has a checkbox field:
<input type="checkbox" name="active" value="1">Published<br />
and when it is not selected, the field is not send and the SQL insert query sends the error message "No active field". How can I check if the element "active" is set and add it to "params" as true or false?
Something like:
(assoc params :active (if (nil? params/active) false true))
after the ":created_at (java.util.Date.)" line.
How can I check if the element "active" is set and add it to "params" as true or false?
Looks like your code isn't far from working. You'll need to check the params map to see if it has the checkbox's value. If (:active params) is equal to "1" when the checkbox is checked, then you might do something like this:
(assoc params :active (= "1" (:active params)))
But what this is really trying to do is update a particular value in the map, which can be done more idiomatically:
(update params :active #(= "1" %))
Where the final argument is a function that takes any current value of the keyword and returns the new value.
Another potential gotcha: you may not want to use the params map as direct input to your DB query, because it could very easily contain keys/values that you don't want or expect. It'd be safer to pull only the values you need from it explicitly e.g. (select-keys params [:title :body :active]).
(def params {:active "1", :admin true}) ;; wouldn't want admin to leak through!
(-> params
(select-keys [:title :body :active])
(assoc :created_at (java.util.Date.))
(update :active #(= "1" %)))
;;=> {:active true, :created_at #inst "2017-10-09T20:16:06.167-00:00"}

Check if URL parameter exists in map

(defroutes my-routes
(GET "/:id" [id] (html/display-thing id)))
(def my-map
{:id 1 :title "One"
:id 2 :title "Two"})
Is there a nice way to check if the url parameter id exists in my-map else continue checking if the other routes match? I know you can do something similar with regex like so: ["/:id", :id #"[0-9]+"] and suspect it might be possible to plug in an arbitrary predicate function.
Not actually at a REPL, but isn't this as straightforward as returning nil from html/display-thing if there's no id element in my-map? Take a look at (source GET) to see how the macro passes control to the next route if the method or URL don't match.

Getting the id of an inserted entity in datomic?

After I run a transaction in datomic to insert a value, how I can use the return value of the transaction to get the ids of any entities that were created?
Here is a sample of the return value I get after an insert:
#<promise$settable_future$reify__4841#7c92b2e9: {:db-before datomic.db.Db#62d0401f, :db-after datomic.db.Db#bba61dfc,
:tx-data [#Datum{:e 13194139534331 :a 50
:v #inst "2013-06-19T11:38:08.025-00:00"
:tx 13194139534331 :added true} #Datum{:e 17592186045436 .....
I can see the underlying datums...how can I extract their values?
Use d/resolve-tempid. If you were to transact a single entity, looking at :tx-data would work but if your transaction contained more than one entity, then you wouldn't know the order in which they appear in :tx-data.
What you should do is give temporary ids to your entities (before transacting them) using either (d/tempid) or its literal representation #db/id[:db.part/user _negativeId_] and then use d/resolve-tempid to go from your temporary id to the real id given by the database. The code would look something like:
(d/resolve-tempid (d/db conn) (:tempids tx) (d/tempid :db.part/user _negativeId_))
For a full code sample, see this gist.
Ah, figured it out.
I had to deref the Clojure promise, and then I was able to yank out the values I wanted:
(:e (second (:tx-data #(transact! conn query))))
Wrote a quick function based on a2ndrade's answer. The naming isn't ideal and I may be committing idiomatic faux pas; suggestions are very much welcome.
(ns my.datomic.util
(:require [datomic.api :as d]))
(defn transact-and-get-id
"Transact tx and return entity id."
[conn tx]
(let [tempid (:db/id tx)
post-tx #(d/transact conn [tx])
db (:db-after post-tx)
entid (d/resolve-tempid db (:tempids post-tx) tempid)]
entid))
Example usage:
(def my-conn
(d/connect (str "datomic:sql://datomic?jdbc:postgresql://"
"127.0.1:5432/datomic?user=datomic&password=somepw")
(defn thing-tx
"Create transaction for new thing."
[name]
{:db/id (d/tempid :db.part/user)
:thing/name name})
(transact-and-get-id my-conn (thing-tx "Bob")) ;; => 17592186045502
The Tupelo Datomic library has a function (td/eids tx-result) to easily extract the EIDs created in a transaction. For example:
; Create Honey Rider and add her to the :people partition
(let [tx-result #(td/transact *conn*
(td/new-entity :people ; <- partition is first arg (optional) to td/new-entity
{ :person/name "Honey Rider" :location "Caribbean" :weapon/type #{:weapon/knife} } ))
[honey-eid] (td/eids tx-result) ; retrieve Honey Rider's EID from the seq (destructuring)
]