Skip to content

Instantly share code, notes, and snippets.

@olivergeorge
Last active September 6, 2017 11:05
Show Gist options
  • Save olivergeorge/b67c2bf5b0529206a747d5ca7ef4eff8 to your computer and use it in GitHub Desktop.
Save olivergeorge/b67c2bf5b0529206a747d5ca7ef4eff8 to your computer and use it in GitHub Desktop.
Spike to see what an alias aware SQL query DSL might look like. Make joins easier to add. Deal with munging/demunging column names. Boils down to a HoneySQL query and a row-fn demunger.
(s/def ::columns (s/coll-of qualified-keyword? :type set?))
(s/def ::from (s/coll-of simple-keyword? :type set?))
(s/def ::aliases (s/map-of simple-keyword? simple-keyword?))
(s/def ::left-join (s/map-of simple-keyword? ::where))
(s/def ::where (s/coll-of ::condition))
(s/def ::condition (s/cat :op #{:=} :args (s/+ any?)))
(def my-query1
{::columns #{:SITE/NAME :BL/NAME}
::from #{:SITE}
::left-join {:BL [[:= :SITE/SITE_ID :BL/SITE_ID]]}
::where [[:= :SITE/SITE_ID "BB"]]})
(def my-query2
{::columns #{:SITE/NAME :SITE2/NAME}
::aliases {:SITE :SITE :SITE2 :SITE}
::from #{:SITE :SITE2}})
(defn query-plan
[{:keys [::columns ::from ::aliases ::left-join ::where]}]
(let [table-kmap (memoize (fn [s] (name (gensym (first (name s))))))
column-as (map (comp keyword gensym name) columns)
column-sel #(keyword (str (table-kmap (namespace %)) "." (name %)))
query-filter (fn [[op & args]]
(let [prep-arg #(if (keyword? %) (column-sel %) %)]
(into [op] (map prep-arg args))))
filters (map query-filter where)
table-as (juxt #(get aliases % %) (comp keyword table-kmap name))
column-kmap (zipmap column-as columns)
left-join-term (mapcat
(fn [[k v]]
[(table-as k) (into [:and] (map query-filter v))])
left-join)]
{:row-fn (fn [row] (set/rename-keys row column-kmap))
:query (cond-> {:select (map (juxt val key) (zipmap column-as (map column-sel columns)))
:from (map table-as from)}
(seq left-join-term) (assoc :left-join left-join-term)
(seq filters) (assoc :where (into [:and] filters)))}))
(comment
(let [{:keys [query row-fn]} (query-plan my-query1)
opts {:identifiers string/upper-case :row-fn row-fn}
sql (sql/format query)]
{:query query
:sql sql
:xrel (try (take 10 (j/query local-db sql opts))
(catch Exception e
(.getMessage e)))}))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment