How to expand HugSQL parameter into multiple like statements - clojure

Does anyone know how this can be accomplished?
(get-lists ["free" "food"]) ->
Select name
From Lists
Where name like '%free%' and name like '%food%'
I have tried:
-- :name get-lists :? :*
Select id, name
from Lists
where
--~ (clojure.string/join "" (interpose " AND " (map #(str "name LIKE '%" % "%'") :sKeyWords)))
But of course that does not work. Can someone point me in the right direction please?

To form a query you can also use clojure.pprint/cl-format from clojure's standard lib, which is quite powerful and concise:
user> (require '[clojure.pprint :refer [cl-format]])
user> (cl-format nil "WHERE~{ name LIKE '%~a%' ~^AND~}"
["me" "you" "somebody"])
;;=> "WHERE name LIKE '%me%' AND name LIKE '%you%' AND name LIKE '%somebody%' "

Figured out answer. Putting here for anyone else who might need it.
-- :name get-lists :? :*
Select id, name
from Lists
--~ (str "WHERE "
(clojure.string/join " AND " (map #(str "name LIKE '%" % "%'")
(:key-words params))))

Related

How to chain function calls in Clojure?

Imagine I have a string which I want to transform as follows:
Remove all spaces.
Remove all dots.
Make the string lower-case.
One way to do it is this:
(defn my-function
[s]
(let
[
clean-string1 (clojure.string/replace s " " "")
clean-string2 (clojure.string/replace clean-string1 "." "")
clean-string3 (clojure.string/lower-case clean-string2)
]
;; ...
)
)
How can I "chain" the functions clojure.string/replace and clojure.string/lower-case so that
the output of (clojure.string/replace s " " "") is fed to the input of
(clojure.string/replace clean-string1 "." "") and its output is fed to the input of
(clojure.string/lower-case clean-string2)
so that I don't need intermediate variables clean-string1 and clean-string2?
You just do it the same way you would in any language. You're asking for function composition, which in math or non-lisp languages looks like f(g(x)). In lisp of course that's (f (g x)), but the principle is the same.
(require '[clojure.string :as s])
(s/lower-case (s/replace (s/replace s " " "") "." ""))
is the most straightforward answer. But it's rather unpleasant to have this level of nesting where the function names are so far removed from their extra arguments, and so most people would instead write
(-> s
(s/replace " " "")
(s/replace "." "")
(s/lower-case))
which is the same thing but just using -> to shuffle the forms around a bit for clarity.

Pass values from a collection as the first argument of a function

Say I have a collection of user-ids i.e. [001 002 003], and then I would have a function that does something and requires the user-id as its first argument.
(defn some-function [user-id name e-mail] (do-something user-id name e-mail))
What I'd like to do is to use this "some-function" to go through the collection of user-ids so that it would just change the user-id argument but the other arguments would remain the same i.e. so that it would return the following:
=>
[(some-function 001 name e-mail) (some-function 002 name e-mail) (some-function 003 name e-mail)]
Any help here? :) Thanks!
You can just use map:
(map #(some-function % name email) user-ids)
If "does something" is side-effecting then you should be using doseq rather than map:
(def user-ids [1 2 3])
(def email "me#my.com")
(def named "me")
(defn some-function [id name email]
(println (str id ", " name ", " email)))
(doseq [user-id user-ids]
(some-function user-id named email))
"Doing something" normally means affecting the world in some way - from printing to the screen to launching rockets into space.
However if you wanted to return a series of functions that can be executed later then map would be fine:
(def fns (map (fn [id]
(fn []
(some-function id named email)))
user-ids))
Here fns is the data structure you wrote out in your question.
To actually execute these 'thunks' you still need to doseq:
(doseq [f fns]
(f))
As a side-note, the kind of function you are talking about, that accepts different arguments at different times, is normally described as a 'higher order function', and it is best to code it that way from the start:
(defn some-function-hof [name email]
(fn [id]
(println (str id ", " name ", " email))))
(def some-fn! (some-function-hof named email))

sequence comprehensions to get run

The below code I have found from a book (Functional Programming Patterns in Scala and Clojure). The for statement uses close-zip? to filter out people outside of the zips and then it generates a greeting to the people who are left. However, I am not quite sure how people should look like as argument for generate-greetings and print-greetings functions?
(def close-zip? #{19123 19103})
(defn generate-greetings [people]
(for [{:keys [name address]} people :when (close-zip? (address :zip-code))]
(str "Hello, " name ", and welcome to the Lambda Bar And Grille!")))
(defn print-greetings [people]
(doseq [{:keys [name address]} people :when (close-zip? (address :zip-code))]
(println (str "Hello, " name ", and welcome to the Lambda Bar And Grille!"))))
They need to be maps with :name and :address keys, like:
{:name "A Person", :address {:zip-code 19103}}
for will take each element from people and assign each one to {:keys [name address]}. This is called destructuring, and it's just a convenience. It's the same as saying:
(for [person people
:let [name (:name person)
address (:address person)]
:when (close-zip? (:zip-code address))]
(str ...))

Clojure: Dynamically create functions from a map -- Time for a Macro?

I have a function that begins like this:
(defn data-one [suser]
(def suser-first-name
(select db/firstNames
(fields :firstname)
(where {:username suser})))
(def suser-middle-name
(select db/middleNames
(fields :middlename)
(where {:username suser})))
(def suser-last-name
(select db/middleNames
(fields :lastname)
(where {:username suser})))
;; And it just continues on and on...
)
Of course, I don't like this at all. I have this pattern repeating in many areas in my code-base and I'd like to generalize this.
So, I came up with the following to start:
(def data-input {:one '[suser-first-name db/firstNames :firstname]
'[suser-middle-name db/middleNames :middlename]
'[suser-last-name db/lastNames :lastname]})
(defpartial data-build [data-item suser]
;; data-item takes the arg :one in this case
`(def (data-input data-item)
(select (data-input data-item)
(fields (data-input data-item))
(where {:username suser}))))
There's really a few questions here:
-- How can I deconstruct the data-input so that it creates x functions when x is unknown, ie. that the values of :one is unknown, and that the quantities of keys in data-input is unknown.
-- I'm thinking that this is a time to create a macro, but I've never built one before, so I am hesitant on the idea.
And to give a little context, the functions must return values to be deconstructed, but I think once I get this piece solved, generalizing all of this will be doable:
(defpage "/page-one" []
(let [suser (sesh/get :username)]
(data-one suser)
[:p "Firat Name: "
[:i (let [[{fname :firstname}] suser-first-name]
(format "%s" fname))]
[:p "Middle Name: "
[:i (let [[{mname :emptype}] suser-middle-name]
(format "%s" mname))]
[:p "Last Name: "
[:i (let [[{lname :months}] suser-last-name]
(format "%s" lname))]]))
Some suggestions:
def inside a function is really nasty - you are altering the global environment, and it can cause all kinds of issues with concurrency. I would suggest storing the results in a map instead.
You don't need a macro here - all of the data fetches can be done relatively easily within a function
I would therefore suggest something like:
(def data-input [[:suser-first-name db/firstNames :firstname]
[:suser-middle-name db/middleNames :middlename]
[:suser-last-name db/lastNames :lastname]])
(def data-build [data-input suser]
(loop [output {}
items (seq data-input)]
(if items
(recur
(let [[kw db fieldname] (first items)]
(assoc output kw (select db (fields fieldname) (where {:username suser}))))
(next items))
output)))
Not tested as I don't have your database setup - but hopefully that gives you an idea of how to do this without either macros or mutable globals!
Nice question. First of all here's the macro that you asked for:
(defmacro defquery [fname table fields ]
(let [arg-name (symbol 'user-name)
fname (symbol fname)]
`(defn ~fname [~arg-name]
(print ~arg-name (str ~# fields)))))
You can call it like that:
(defquery suser-first-name db/firstNames [:firstname])
or if you prefer to keep all your configurations in a map, then it will accept string as the first argument instead of a symbol:
(defquery "suser-first-name" db/firstNames [:firstname])
Now, if you don't mind me recommending another solution, I would probably chose to use a single function closed around configuration. Something like that:
(defn make-reader [query-configurations]
(fn [query-type user-name]
(let [{table :table field-names :fields}
(get query-configurations query-type)]
(select table
(apply fields field-names)
(where {:username suser})))))
(def data-input {:firstname {:table db/firstNames :fields :firstname}
:middlename {:table db/middleNames :fields :middlename}
:lastname {:table db/lastNames :fields :lastname}})
(def query-function (make-reader data-input))
;; Example of executing a query
(query-function :firstname "tom")
By the way there's another way to use Korma:
;; This creates a template select from the table
(def table-select (select* db/firstNames))
;; This creates new select query for a specific field
(def first-name-select (fields table-select :firstname))
;; Creating yet another query that filters results by :username
(defn mkselect-for-user [suser query]
(where query {:username suser}))
;; Running the query for username "tom"
;; I fully specified exec function name only to show where it comes from.
(korma.core/exec (mkselect-for-user "tom" first-name-select))
For more information I highly recommend looking at Korma sources.

Binding a local var in deftemplate for enlive

I'm brand new to clojure and the web development stack. I'm trying to use enlive to set values in an HTML template:
(en/deftemplate project-main-page
(en/xml-resource "project-main.html")
[id]
[:#project-name] (en/content (str "Name: " ((get-project id) :name)))
[:#project-desc] (en/content (str "Desc: " ((get-project id) :desc))))
This works fine to set my two HTML elements, but it involves a repeated call to my function get-project. At the moment this just reads from a local map, but eventually it will involve some external storage access, so I'd prefer to just perform it once in this function.
I was thinking of using let:
(en/deftemplate project-main-page
(en/xml-resource "project-main.html")
[id]
(let [project (get-project id)]
[:#project-name] (en/content (str "Name: " (project :name)))
[:#project-desc] (en/content (str "Desc: " (project :desc)))))
But this only affects the description element and ignores the name forms.
What is the best way to bind a local var within deftemplate?
If I have understood what you are trying to achieve; you could also try using the transformation macro provided by enlive.
(defn main-page [{:keys [name desc] :as project}]
(en/transformation
[:#project-name] (en/content (str "Name: " name)
[:#project-desc] (en/content (str "Desc: " desc))))
(en/deftemplate project-main-page
(en/xml-resource "project-main.html")
[id]
(main-page (get-project id)))
The code is untested, but I hope it conveys a different way to do what you need
Enlive's deftemplate macro expects a series of tag/content pairs after the args vector (the args vector is [id] in your example). You can't just stick a let in there because the macro isn't expecting a let form, so when it does its splicing everything gets messed up and results in the behavior you described above.
One way you could fix this would be to write your own deftemplate macro that allows binding definitions using the identifiers in the args vector. Example:
(alt/deftemplate project-main-page
(en/xml-resource "project-main.html")
[id]
[project (get-project id)]
[:#project-name] (en/content (str "Name: " (project :name)))
[:#project-desc] (en/content (str "Desc: " (project :desc))))
The deftemplate macro is a simple wrapper around template, which uses snippet* and this is probably where you'd need to insert your changes:
(defmacro snippet* [nodes args & forms]
`(let [nodes# (map annotate ~nodes)]
(fn ~args
; You could add let bindings here since args are in scope
(doall (flatmap (transformation ~#forms) nodes#)))))
The other option—which might be simpler since you don't have to muck around in the library code—would be to add a level of indirection to your get-project function to cache results. You might try the core.cache library.