Code related to fetching FieldValues for Fields to populate parameter widgets. Always used by the field
values ( | (ns metabase.models.params.field-values (:require [metabase.db.query :as mdb.query] [metabase.models.field :as field] [metabase.models.field-values :as field-values :refer [FieldValues]] [metabase.models.interface :as mi] [metabase.plugins.classloader :as classloader] [metabase.public-settings.premium-features :refer [defenterprise]] [metabase.util :as u] [toucan2.core :as t2])) |
OSS implementation; used as a fallback for the EE implementation if the field isn't sandboxed. | (defn default-get-or-create-field-values-for-current-user! [field] (field-values/get-or-create-full-field-values! field)) |
Fetch cached FieldValues for a | (defenterprise get-or-create-field-values-for-current-user!* metabase-enterprise.sandbox.models.params.field-values [field] (default-get-or-create-field-values-for-current-user! field)) |
Whether the current User has permissions to fetch FieldValues for a | (defn current-user-can-fetch-field-values? [field] ;; read permissions for a Field = partial permissions for its parent Table (including EE segmented permissions) (mi/can-read? field)) |
Format a FieldValues to use by params functions. ;; (postprocess-field-values (t2/select-one FieldValues :id 1) (Field 1)) ;; => {:values [[1] [2] [3] [4]] :field_id 1 :hasmorevalues boolean} | (defn- postprocess-field-values [field-values field] (if field-values (-> field-values (assoc :values (field-values/field-values->pairs field-values)) (select-keys [:values :field_id :has_more_values])) {:values [], :field_id (u/the-id field), :has_more_values false})) |
OSS implementation; used as a fallback for the EE implementation for any fields that aren't subject to sandboxing. | (defn default-field-id->field-values-for-current-user [field-ids] (when (seq field-ids) (let [field-ids (->> (t2/select :model/Field :id [:in (set field-ids)]) field/readable-fields-only (map :id))] (when (seq field-ids) (update-vals (field-values/batched-get-latest-full-field-values field-ids) #(select-keys % [:field_id :human_readable_values :values])))))) |
Fetch existing FieldValues for a sequence of | (defenterprise field-id->field-values-for-current-user metabase-enterprise.sandbox.models.params.field-values [field-ids] (default-field-id->field-values-for-current-user field-ids)) |
+----------------------------------------------------------------------------------------------------------------+ | Advanced FieldValues | +----------------------------------------------------------------------------------------------------------------+ | |
(defn- fetch-advanced-field-values [fv-type field constraints] {:pre [(field-values/advanced-field-values-types fv-type)]} (case fv-type :linked-filter (do (classloader/require 'metabase.models.params.chain-filter) (let [{:keys [values has_more_values]} ((resolve 'metabase.models.params.chain-filter/unremapped-chain-filter) (:id field) constraints {}) ;; we have a hard limit for how many values we want to store in FieldValues, ;; let's make sure we respect that limit here. ;; For a more detailed docs on this limt check out [[field-values/distinct-values]] limited-values (field-values/take-by-length field-values/*total-max-length* values)] {:values limited-values :has_more_values (or (> (count values) (count limited-values)) has_more_values)})) (field-values/distinct-values field))) | |
Returns hash-key for Advanced FieldValues by types. | (defn hash-key-for-advanced-field-values [fv-type field-id constraints] (case fv-type :linked-filter (field-values/hash-key-for-linked-filters field-id constraints) :sandbox (field-values/hash-key-for-sandbox field-id) :impersonation (field-values/hash-key-for-impersonation field-id))) |
Fetch and construct the FieldValues for | (defn prepare-advanced-field-values [fv-type field hash-key constraints] (when-let [{wrapped-values :values :keys [has_more_values]} (fetch-advanced-field-values fv-type field constraints)] (let [;; each value in `wrapped-values` is a 1-tuple, so unwrap the raw values for storage values (map first wrapped-values) ;; If the full FieldValues of this field have human-readable-values, ensure that we reuse them full-field-values (field-values/get-latest-full-field-values (:id field)) human-readable-values (field-values/fixup-human-readable-values full-field-values values)] {:field_id (:id field) :type fv-type :hash_key hash-key :has_more_values has_more_values :human_readable_values human-readable-values :values values}))) |
Fetch an Advanced FieldValues with type | (defn get-or-create-advanced-field-values! ([fv-type field] (get-or-create-advanced-field-values! fv-type field nil)) ([fv-type field constraints] (let [hash-key (hash-key-for-advanced-field-values fv-type (:id field) constraints) select-kvs {:field_id (:id field) :type fv-type :hash_key hash-key} fv (mdb.query/select-or-insert! :model/FieldValues select-kvs #(prepare-advanced-field-values fv-type field hash-key constraints))] (cond (nil? fv) nil ;; If it's expired, delete then try to re-create it (field-values/advanced-field-values-expired? fv) (do ;; It's possible another process has already recalculated this, but spurious recalculations are OK. (t2/delete! FieldValues :id (:id fv)) (recur fv-type field constraints)) :else fv)))) |
+----------------------------------------------------------------------------------------------------------------+ | Public functions | +----------------------------------------------------------------------------------------------------------------+ | |
Fetch FieldValues for a | (defn get-or-create-field-values-for-current-user! [field] (-> (get-or-create-field-values-for-current-user!* field) (postprocess-field-values field))) |
Fetch linked-filter FieldValues for a | (defn get-or-create-linked-filter-field-values! [field constraints] (-> (get-or-create-advanced-field-values! :linked-filter field constraints) (postprocess-field-values field))) |