G'day all,
I am building a reporting interface on top of a database with a star
schema. The star schema makes it very easy to restrict queries by various
combinations of dimension. Some combinations are legal; others are not.
Hence core.match. The number of possible combinations is large, and I would
like to provide a simple API for setting up the dimensional constraints
before running a query.
I have in mind to use dynamic variables because in generating reports, a
few separate queries to the database are required - always with the same
dimensional constraints for a particular instance of a report. It is a pain
to pass a large number of parameters down the call stack - all the way from
the interface to the presentation layer, to the mapping layer which
ultimately builds and runs the query against the db. It would be even more
of a pain to update all calls with new parameters every time I add a new
dimension to the database.
So the interface would look something like this:
(binding [*dimension1* ... *dimension2* ... *dimension3* ...]
(get-fact-1-aggregates mandatory-dimension-restriction)
(get-fact-2-aggs mandatory-dim-rest)
....)
Or more concretely if you like a bit of context:
(binding [*drill-name* "DR1" *hole-name* "h1" *from* timestamp *to*
timestamp2]
(do-something-with (drilling-aggregates 123))
(do-something-with (reaming-aggregates 123))
....)
Where 123 would be a company ID set by the user's login credentials and
since it is mandatory and will always be passed I have put it as a formal
parameters.
Here is a rough draft of the code I have in mind.
I would very much appreciate any comments as to the sanity of this
approach, and any other ideas on how to do it better.
Specifically - any better ways of building the query??
Note: I have not populated all the legal combinations of variables so the
match would grow.
Thanks!
David
(ns hummingbird.mappers.core
(:require
[hummingbird.mappers.database :as db]
[clojure.java.jdbc :as sql]
[clojure.core.match :refer [match]]))
(def ^:dynamic *job-name* nil)
(def ^:dynamic *drill-name* nil)
(def ^:dynamic *hole-name* nil)
(def ^:dynamic *from* nil)
(def ^:dynamic *to* nil)
(def !nil? (complement nil?))
(defn build-dr-query
([company-name hole-selection from-selection to-selection]
(format
"SELECT
round((sum(dr.distance)/sum(dr.sample_period))::numeric, 2) as feed_rate
FROM
drilling as dr,
dim_time as t,
dim_hole as h
WHERE
dr.time_key = t.time_key
AND dr.hole_key = h.hole_key
AND h.company_name = '%s' -- mandatory company restriction
%s -- optional hole restriction
%s -- optional time from restriction
%s -- optional time to restriction
" company-name hole-selection from-selection to-selection))
([company-name]
(match [company-name *hole-name* *from* *to*]
[company-name nil nil nil]
(build-dr-query company-name "" "" "")
[company-name (hole-name :guard !nil?) nil nil]
(build-dr-query company-name
(format "AND h.hole_name = '%s'" hole-name)
""
"")
[company-name (hole-name :guard !nil?) (from :guard !nil?) nil]
(build-dr-query company-name
(format "AND h.hole_name = '%s'" hole-name)
(format "AND t.date >= '%s'::timestamp" from)
"")
[company-name (hole-name :guard !nil?) (from :guard !nil?) (to
:guard !nil?)]
(build-dr-query company-name
(format "AND h.hole_name = '%s'" hole-name)
(format "AND t.date >= '%s'::timestamp" from)
(format "AND t.date < '%s'::timestamp" to))
:else (throw (Exception. "Illegal combination")))))
(defn drilling-aggregates [& {:keys [group-by order-by]}]
(sql/with-connection db/osprey
(sql/with-query-results rows [(build-dr-query "ACME")]
(db/only-one rows))))
--
David Jagoe
[email protected]
+18053284389
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to [email protected]
Note that posts from new members are moderated - please be patient with your
first post.
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en