Skip to content

Instantly share code, notes, and snippets.

@crinklywrappr
Created March 10, 2022 18:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save crinklywrappr/b01f59c43862fe17881959345d036ced to your computer and use it in GitHub Desktop.
Save crinklywrappr/b01f59c43862fe17881959345d036ced to your computer and use it in GitHub Desktop.
save the union with honeysql
(require '[clojure.str :as str]
'[honeysql.core :as hsql])
;; Makes unions easier to write
(defn get-column-name [col]
(cond
(vector? col) (second col)
(and (keyword? col)
(str/ends-with? (name col) ".*")) col
(and (keyword? col)
(str/includes? (name col) ".")) (-> col name
(str/split #"\.")
second keyword)
(keyword? col) col))
(defn ordered-union
"Like clojure.set/union, but does not
enforce set constraints, and maintains
order between the two collections"
[cmp coll1 coll2]
(loop [xs [] coll1 coll1 coll2 coll2]
(cond
(and (empty? coll1) (empty? coll2)) xs
(empty? coll1) (vec (concat xs coll2))
(empty? coll2) (vec (concat xs coll1))
(cmp (first coll1) (first coll2)) (recur (conj xs (first coll2)) (rest coll1) (rest coll2))
:else (recur (conj xs (first coll1)) (rest coll1) coll2))))
(defn get-all-columns-in-union [m]
(reduce
(fn [a b]
(ordered-union = a (map get-column-name b)))
[] (map :select (:union m))))
(defn format-nil-column [type-map column]
(if (contains? type-map column)
[(hsql/call :cast nil (get type-map column)) column]
[nil column]))
(defn add-columns [type-map all-columns provided-columns]
(ordered-union
#(= (get-column-name %1) (get-column-name %2))
(mapv (partial format-nil-column type-map) all-columns) provided-columns))
(defn normalize-union
"Back-fills [nil column] in union queries.
Does not know how to expand '*'
Use the 3-arity function to be specific about the columns in your query.
Example:
m = {:union [{:select [:foo]}
{:select [:bar]}]}
=>
{:union [{:select [:foo [nil :bar]]}
{:select [[nil :foo] :bar]}]}
type-map: unions with more than 2 queries may need to help postgres' type-resolution
use this like `{:bar :text}`, to specify the type to postgres
columns: [:foo :bar]
queries: the value of :union eg
[{:select [:foo]}
{:select [:bar]}]"
([m]
(normalize-union m {}))
([m type-map]
(let [cols (get-all-columns-in-union m)]
(update m :union (partial normalize-union type-map cols))))
([type-map columns queries]
(mapv #(update % :select (partial add-columns type-map columns)) queries)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment