A Metric is a saved MBQL query stage snippet with EXACTLY ONE | (ns metabase.lib.metric (:require [metabase.legacy-mbql.normalize :as mbql.normalize] [metabase.lib.aggregation :as lib.aggregation] [metabase.lib.convert :as lib.convert] [metabase.lib.join :as lib.join] [metabase.lib.metadata :as lib.metadata] [metabase.lib.metadata.calculation :as lib.metadata.calculation] [metabase.lib.options :as lib.options] [metabase.lib.query :as lib.query] [metabase.lib.ref :as lib.ref] [metabase.lib.schema :as lib.schema] [metabase.lib.schema.expression :as lib.schema.expression] [metabase.lib.schema.metadata :as lib.schema.metadata] [metabase.lib.util :as lib.util] [metabase.util.i18n :as i18n] [metabase.util.malli :as mu])) |
(defn- resolve-metric [query metric-id] (when (integer? metric-id) (lib.metadata/metric query metric-id))) | |
(mu/defn- metric-definition :- [:maybe ::lib.schema/stage.mbql] [{:keys [dataset-query], :as _metric-metadata} :- ::lib.schema.metadata/metric] (when dataset-query (let [normalized-definition (cond-> dataset-query (not (contains? dataset-query :lib/type)) ;; legacy; needs conversion (-> mbql.normalize/normalize lib.convert/->pMBQL))] (lib.util/query-stage normalized-definition -1)))) | |
(defmethod lib.ref/ref-method :metadata/metric [{:keys [id ::lib.join/join-alias], :as metric-metadata}] (let [effective-type (or (:effective-type metric-metadata) (:base-type metric-metadata) (when-let [aggregation (first (:aggregation (metric-definition metric-metadata)))] (let [ag-effective-type (lib.schema.expression/type-of aggregation)] (when (isa? ag-effective-type :type/*) ag-effective-type)))) options (cond-> {:lib/uuid (str (random-uuid))} join-alias (assoc :join-alias join-alias) effective-type (assoc :effective-type effective-type))] [:metric options id])) | |
(defmethod lib.metadata.calculation/type-of-method :metadata/metric [query stage-number metric-metadata] (or (when-let [[aggregation] (not-empty (:aggregation (metric-definition metric-metadata)))] (lib.metadata.calculation/type-of query stage-number aggregation)) :type/*)) | |
(defmethod lib.metadata.calculation/type-of-method :metric [query stage-number [_tag _opts metric-id-or-name]] (or (when-let [metric-metadata (resolve-metric query metric-id-or-name)] (lib.metadata.calculation/type-of query stage-number metric-metadata)) :type/*)) | |
(defn- fallback-display-name [] (i18n/tru "[Unknown Metric]")) | |
(defmethod lib.metadata.calculation/display-name-method :metadata/metric [_query _stage-number metric-metadata _style] (or ((some-fn :display-name :name) metric-metadata) (fallback-display-name))) | |
(defmethod lib.metadata.calculation/display-name-method :metric [query stage-number [_tag _opts metric-id-or-name] style] (or (when-let [metric-metadata (resolve-metric query metric-id-or-name)] (lib.metadata.calculation/display-name query stage-number metric-metadata style)) (fallback-display-name))) | |
(defmethod lib.metadata.calculation/display-info-method :metadata/metric [query stage-number metric-metadata] (merge ((get-method lib.metadata.calculation/display-info-method :default) query stage-number metric-metadata) (select-keys metric-metadata [:description :aggregation-position]))) | |
(defmethod lib.metadata.calculation/display-info-method :metric [query stage-number [_tag opts metric-id-or-name]] (let [display-name (:display-name opts) opts (cond-> opts (and display-name (not (:long-display-name opts))) (assoc :long-display-name display-name))] (merge (if-let [metric-metadata (resolve-metric query metric-id-or-name)] (lib.metadata.calculation/display-info query stage-number metric-metadata) {:effective-type :type/* :display-name (fallback-display-name) :long-display-name (fallback-display-name)}) (select-keys opts [:name :display-name :long-display-name])))) | |
(defmethod lib.metadata.calculation/column-name-method :metric [query stage-number [_tag _opts metric-id-or-name]] (or (when-let [metric-metadata (resolve-metric query metric-id-or-name)] (lib.metadata.calculation/column-name query stage-number metric-metadata)) "metric")) | |
(mu/defn available-metrics :- [:maybe [:sequential {:min 1} ::lib.schema.metadata/metric]] "Get a list of Metrics that you may consider using as aggregations for a query." ([query] (available-metrics query -1)) ([query :- ::lib.schema/query stage-number :- :int] (let [first-stage? (zero? (lib.util/canonical-stage-index query stage-number)) metric-aggregations (into {} (keep-indexed (fn [index aggregation-clause] (when (lib.util/clause-of-type? aggregation-clause :metric) [[(get aggregation-clause 2) (:join-alias (lib.options/options aggregation-clause))] index]))) (lib.aggregation/aggregations query stage-number)) maybe-add-aggregation-pos (fn [metric-metadata] (let [aggregation-pos (-> metric-metadata ((juxt :id ::lib.join/join-alias)) metric-aggregations)] (cond-> metric-metadata aggregation-pos (assoc :aggregation-position aggregation-pos))))] (when first-stage? (let [source-table (lib.util/source-table-id query) metrics (if source-table (lib.metadata/metadatas-for-table query :metadata/metric source-table) (lib.metadata/metadatas-for-card query :metadata/metric (lib.util/source-card-id query)))] (not-empty (into [] (comp (filter (fn [metric-card] (= 1 (lib.query/stage-count (lib.query/query query (:dataset-query metric-card)))))) (map maybe-add-aggregation-pos)) (sort-by (some-fn :display-name :name) metrics)))))))) | |
(defn- normalize-legacy-query [query] (cond-> query (#{:query :native} (lib.util/normalized-query-type query)) mbql.normalize/normalize)) | |
(defmethod lib.metadata.calculation/metadata-method :metric [query stage-number [_ _ metric-id]] (let [metric-meta (lib.metadata/metric query metric-id) metric-aggregation (some-> metric-meta :dataset-query normalize-legacy-query lib.convert/->pMBQL lib.aggregation/aggregations first) metric-name (:name metric-meta)] (assoc (lib.metadata.calculation/metadata query stage-number metric-aggregation) :display-name metric-name))) | |