"Zoom" transform for numeric (including location) columns. Entry points:
Requirements:
Query transformation:
Question transformation:
This covers two types of 'zoom in' drills:
| (ns metabase.lib.drill-thru.zoom-in-bins (:require [metabase.lib.binning :as lib.binning] [metabase.lib.breakout :as lib.breakout] [metabase.lib.drill-thru.common :as lib.drill-thru.common] [metabase.lib.equality :as lib.equality] [metabase.lib.filter :as lib.filter] [metabase.lib.remove-replace :as lib.remove-replace] [metabase.lib.schema :as lib.schema] [metabase.lib.schema.binning :as lib.schema.binning] [metabase.lib.schema.drill-thru :as lib.schema.drill-thru] [metabase.lib.schema.metadata :as lib.schema.metadata] [metabase.lib.underlying :as lib.underlying] [metabase.util.malli :as mu])) |
available-drill-thrus | |
(mu/defn zoom-in-binning-drill :- [:maybe ::lib.schema.drill-thru/drill-thru.zoom-in.binning]
"Return a drill thru that 'zooms in' on a breakout that uses `:binning` if applicable.
See [[metabase.lib.drill-thru.zoom-in-bins]] docstring for more information."
[query :- ::lib.schema/query
_stage-number :- :int
{:keys [column value], :as _context} :- ::lib.schema.drill-thru/context]
(when (and column value (not= value :null))
(when-let [existing-breakout (first (lib.breakout/existing-breakouts query
(lib.underlying/top-level-stage-number query)
column))]
(when-let [binning (lib.binning/binning existing-breakout)]
(when-let [{:keys [min-value max-value bin-width]}
;; If the column has binning options, use those; otherwise, check the top-level-column.
(or (lib.binning/resolve-bin-width query column value)
(lib.binning/resolve-bin-width
query
;; One of the "superflous" options is ::lib.field/binning, which we want to preserve here.
(lib.underlying/top-level-column query column :rename-superflous-options? false)
value))]
(case (:strategy binning)
(:num-bins :default)
{:lib/type :metabase.lib.drill-thru/drill-thru
:type :drill-thru/zoom-in.binning
:column column
:min-value value
:max-value (+ value bin-width)
:new-binning {:strategy :default}}
:bin-width
{:lib/type :metabase.lib.drill-thru/drill-thru
:type :drill-thru/zoom-in.binning
:column column
:min-value min-value
:max-value max-value
:new-binning (update binning :bin-width #(double (/ % 10.0)))})))))) | |
application | |
(mu/defn- update-breakout :- ::lib.schema/query
[query :- ::lib.schema/query
stage-number :- :int
old-column :- ::lib.schema.metadata/column
new-column :- ::lib.schema.metadata/column
new-binning :- ::lib.schema.binning/binning]
(if-let [existing-breakout (first (lib.breakout/existing-breakouts query stage-number old-column))]
(lib.remove-replace/replace-clause query stage-number existing-breakout (lib.binning/with-binning new-column new-binning))
(lib.breakout/breakout query stage-number (lib.binning/with-binning new-column new-binning)))) | |
(mu/defmethod lib.drill-thru.common/drill-thru-method :drill-thru/zoom-in.binning :- ::lib.schema/query
[query :- ::lib.schema/query
stage-number :- :int
{:keys [column min-value max-value new-binning]} :- ::lib.schema.drill-thru/drill-thru.zoom-in.binning]
;; We add and remove filters on the last stage rather than top-level-stage-number because that is
;; where [[metabase.query-processor.middleware.binning/update-binning-strategy]] expects to find them. Adding the
;; filters to top-level-stage-number breaks the binning.
(let [top-level-stage-number (lib.underlying/top-level-stage-number query)
resolved-column (lib.drill-thru.common/breakout->resolved-column query stage-number column)
old-filters (filter (fn [[operator _opts filter-column]]
(and (#{:>= :<} operator)
(lib.equality/find-matching-column filter-column [column])))
(lib.filter/filters query stage-number))]
(-> (reduce #(lib.remove-replace/remove-clause %1 stage-number %2) query old-filters)
(update-breakout top-level-stage-number column resolved-column new-binning)
(lib.filter/filter stage-number (lib.filter/>= resolved-column min-value))
(lib.filter/filter stage-number (lib.filter/< resolved-column max-value))))) | |