(ns metabase.models.field-usage (:require [metabase.lib.core :as lib] [metabase.lib.schema :as lib.schema] [metabase.lib.util :as lib.util] [metabase.models.interface :as mi] [metabase.query-processor.middleware.fetch-source-query :as fetch-source-query] [metabase.util.malli :as mu] [methodical.core :as methodical] [toucan2.core :as t2] [toucan2.tools.disallow :as t2.disallow])) | |
(doto :model/FieldUsage (derive :metabase/model) (derive ::t2.disallow/update) (derive :hook/created-at-timestamped?)) | |
(methodical/defmethod t2/table-name :model/FieldUsage [_model] :field_usage) | |
(t2/deftransforms :model/FieldUsage {:used_in mi/transform-keyword :aggregation_function mi/transform-keyword :breakout_temporal_unit mi/transform-keyword :breakout_binning_strategy mi/transform-keyword :filter_op mi/transform-keyword}) | |
(defn- filter->field-usage [query stage filter-clause] (let [filter-parts (lib/filter-parts query stage filter-clause) field-id (-> filter-parts :column :id)] (when (int? field-id) {:field_id field-id :used_in :filter :filter_op (-> filter-parts :operator :short)}))) | |
(defn- aggregation->field-usage [query stage aggregation-clause] (let [aggregation-column (lib/aggregation-column query stage aggregation-clause) field-id (:id aggregation-column)] (when (int? field-id) {:field_id field-id :used_in :aggregation :aggregation_function (first aggregation-clause)}))) | |
(defn- breakout->field-usage [query stage breakout-clause] (let [breakout-column (lib/breakout-column query stage breakout-clause) field-id (:id breakout-column)] (when (int? field-id) (let [binning-option (lib/binning breakout-clause)] {:field_id field-id :used_in :breakout :breakout_temporal_unit (lib/raw-temporal-bucket breakout-clause) :breakout_binning_strategy (:strategy binning-option) :breakout_binning_bin_width (:bin-width binning-option) :breakout_binning_num_bins (:num-bins binning-option)})))) | |
(defn- expression->field-usage [expression-clause] (when-let [field-ids (seq (lib.util/referenced-field-ids expression-clause))] (for [field-id field-ids] {:field_id field-id :used_in :expression}))) | |
(declare pmbql->field-usages) | |
(defn- join->field-usages [query join] (let [join-query (fetch-source-query/resolve-source-cards (assoc query :stages (:stages join)))] ;; treat the source query as a :mbql/query (pmbql->field-usages join-query))) | |
(defn- stage->field-usages [query stage-number] (concat (keep #(filter->field-usage query stage-number %) (lib/filters query stage-number)) (keep #(aggregation->field-usage query stage-number %) (lib/aggregations query stage-number)) (keep #(breakout->field-usage query stage-number %) (lib/breakouts query stage-number)) (flatten (keep expression->field-usage (lib/expressions query stage-number))) (flatten (keep #(join->field-usages query %) (lib/joins query stage-number))))) | |
Given a pmbql query, returns field usages from filter, breakout, aggregation, expression of a query. Walk all stages and joins. Expects all the source cards were resolved | (mu/defn pmbql->field-usages [pmbql :- ::lib.schema/query] (mapcat #(stage->field-usages pmbql %) (range (lib/stage-count pmbql)))) |