Skip to content
Snippets Groups Projects
Commit 6a286884 authored by Ryan Senior's avatar Ryan Senior
Browse files

First cut at constraing min/max from query criteria

parent 45841f94
No related branches found
No related tags found
No related merge requests found
......@@ -89,7 +89,7 @@
(sql/date (driver) unit (formatted field)))
BinnedField
(formatted [{{:keys [min-value max-value] :as field} :field bin-width :bin-width}]
(formatted [{:keys [bin-width min-value max-value field]}]
(let [formatted-field (formatted field)]
(apply hsql/call :case
(mapcat (fn [bin-floor]
......
......@@ -12,6 +12,7 @@
[add-row-count-and-status :as row-count-and-status]
[add-settings :as add-settings]
[annotate-and-sort :as annotate-and-sort]
[binning :as binning]
[cache :as cache]
[catch-exceptions :as catch-exceptions]
[cumulative-aggregations :as cumulative-ags]
......@@ -85,6 +86,7 @@
cumulative-ags/handle-cumulative-aggregations
implicit-clauses/add-implicit-clauses
format-rows/format-rows
binning/update-binning-strategy
expand-resolve/expand-resolve ; ▲▲▲ QUERY EXPANSION POINT ▲▲▲ All functions *above* will see EXPANDED query during PRE-PROCESSING
row-count-and-status/add-row-count-and-status ; ▼▼▼ RESULTS WRAPPING POINT ▼▼▼ All functions *below* will see results WRAPPED in `:data` during POST-PROCESSING
parameters/substitute-parameters
......
......@@ -141,8 +141,11 @@
clojure.lang.Named
(getName [_] (name field)))
(s/defrecord BinnedField [field :- Field
num-bins :- s/Int]
(s/defrecord BinnedField [field :- Field
num-bins :- s/Int
min-value :- s/Num
max-value :- s/Num
bin-width :- s/Num]
clojure.lang.Named
(getName [_] (name field)))
......
(ns metabase.query-processor.middleware.binning
(:require [clojure.walk :as walk]
[metabase.util :as u])
(:import [metabase.query_processor.interface BinnedField ComparisonFilter BetweenFilter]))
(defn- update! [^clojure.lang.ITransientAssociative coll k f]
(assoc! coll k (f (get coll k))))
(defn- filter->field-map [mbql-filter]
(let [acc (transient {})]
(clojure.walk/prewalk
(fn [x]
(when (or
(instance? BetweenFilter x)
(and (instance? ComparisonFilter x)
(contains? #{:< :> :<= :>=} (:filter-type x))))
(update! acc (get-in x [:field :field-id]) #(if (seq %)
(conj % x)
[x])))
x)
mbql-filter)
(persistent! acc)))
(defn- calculate-bin-width [min-value max-value num-bins]
(u/round-to-decimals 5 (/ (- max-value min-value)
num-bins)))
(defn extract-bounds [{field-id :field-id, global-min :min-value, global-max :max-value} field-filter-map]
;; Assuming only one for now
(let [bound-1 (first (for [{:keys [value filter-type]} (get field-filter-map field-id)
:when (or (= :> filter-type)
(= :>= filter-type))]
(:value value) ))
bound-2 (first (for [{:keys [value filter-type]} (get field-filter-map field-id)
:when (or (= :< filter-type)
(= :<= filter-type))]
(:value value)))
comparison-bounds (when (and bound-1 bound-2)
(if (> bound-1 bound-2)
[bound-2 bound-1]
[bound-1 bound-2]))
;;Assuming either >/< or between
between-bounds (first (for [{:keys [filter-type min-value max-value]} (get field-filter-map field-id)
:when (= :between filter-type)]
[min-value max-value]))]
(or (seq comparison-bounds)
(seq between-bounds)
[global-min global-max])) )
(defn update-bin-width [breakouts filter-field-map]
(mapv (fn [{:keys [field num-bins] :as breakout}]
(if-let [[min-value max-value] (and (instance? BinnedField breakout)
(extract-bounds field filter-field-map))]
(assoc breakout
:min-value min-value
:max-value max-value
:bin-width (calculate-bin-width min-value max-value num-bins))
breakout))
breakouts))
(defn update-binning-strategy
[qp]
(fn [query]
(let [binned-breakouts (filter #(instance? BinnedField %) (get-in query [:query :breakout]))]
(if (seq binned-breakouts)
(qp (update-in query [:query :breakout] update-bin-width (filter->field-map (get-in query [:query :filter]))))
(qp query)))))
......@@ -100,11 +100,6 @@
;;; ## ------------------------------------------------------------ FIELD PLACEHOLDER ------------------------------------------------------------
(defn- calculate-bin-width [field num-bins]
(u/round-to-decimals 5 (/ (- (:max-value field)
(:min-value field))
num-bins)))
(defn- field-ph-resolve-field [{:keys [field-id datetime-unit fk-field-id binning-strategy], :as this} field-id->field]
(if-let [{:keys [base-type special-type], :as field} (some-> (field-id->field field-id)
i/map->Field
......@@ -116,8 +111,7 @@
:unit (or datetime-unit :day)}) ; default to `:day` if a unit wasn't specified
binning-strategy
(i/map->BinnedField {:field field
:num-bins binning-strategy
:bin-width (calculate-bin-width field binning-strategy)})
:num-bins binning-strategy})
:else field)
;; If that fails just return ourselves as-is
this))
......
......@@ -111,6 +111,16 @@
(ql/aggregation (ql/count))
(ql/breakout (ql/binning-strategy $latitude :default)))))))
(expect-with-non-timeseries-dbs
[[33.0 4] [34.0 57]]
(tu/with-temporary-setting-values [breakout-bins-num 15]
(format-rows-by [(partial u/round-to-decimals 1) int]
(rows (data/run-query venues
(ql/aggregation (ql/count))
(ql/filter (ql/and (ql/< $latitude 35)
(ql/> $latitude 20)))
(ql/breakout (ql/binning-strategy $latitude :default)))))))
;;Validate binning info is returned with the binning-strategy
(expect-with-non-timeseries-dbs
(merge (venues-col :latitude)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment