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] [metabase.models.interface :as mi] [metabase.plugins.classloader :as classloader] [metabase.premium-features.core :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! :model/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))) |