Skip to content
Snippets Groups Projects
Unverified Commit c9f0f7a1 authored by Braden Shepherdson's avatar Braden Shepherdson Committed by GitHub
Browse files

[MLv2] Add description and fingerprint to `displayInfo` for columns (#38476)

If present. Also adds these fields to the TS types.
parent 74444ce9
No related branches found
No related tags found
No related merge requests found
......@@ -135,9 +135,10 @@ type TableInlineDisplayInfo = Pick<
export type ColumnDisplayInfo = {
name: string;
description?: string;
displayName: string;
longDisplayName: string;
semanticType: string;
semanticType: string | null;
effectiveType: string;
isCalculated: boolean;
......@@ -146,6 +147,7 @@ export type ColumnDisplayInfo = {
isAggregation: boolean;
isBreakout: boolean;
table?: TableInlineDisplayInfo;
fingerprint?: FingerprintDisplayInfo;
breakoutPosition?: number;
filterPositions?: number[];
......@@ -153,6 +155,44 @@ export type ColumnDisplayInfo = {
selected?: boolean; // used in aggregation and field clauses
};
export type FingerprintDisplayInfo = {
global?: FingerprintGlobalDisplayInfo;
type?: FingerprintTypeDisplayInfo;
};
export type FingerprintGlobalDisplayInfo = {
distinctCount?: number;
"nil%"?: number;
};
export type FingerprintTypeDisplayInfo = {
"type/Text"?: TextFingerprintDisplayInfo;
"type/Number"?: NumberFingerprintDisplayInfo;
"type/DateTime"?: DateTimeFingerprintDisplayInfo;
};
export type TextFingerprintDisplayInfo = {
averageLength: number;
percentEmail: number;
percentJson: number;
percentState: number;
percentUrl: number;
};
export type NumberFingerprintDisplayInfo = {
avg: number;
max: number;
min: number;
q1: number;
q3: number;
sd: number;
};
export type DateTimeFingerprintDisplayInfo = {
earliest: string;
latest: string;
};
export type ColumnGroupDisplayInfo = TableDisplayInfo & {
fkReferenceName?: string;
};
......
......@@ -307,6 +307,12 @@
;; because the query processor doesn't produce nested display-names.
{:display-name (lib.metadata.calculation/display-name query stage-number field-metadata)
:long-display-name (lib.metadata.calculation/display-name query stage-number field-metadata :long)}
;; Include description and fingerprint if they're present on the column. Only proper fields or columns from a model
;; have these, not aggregations or expressions.
(when-let [description (:description field-metadata)]
{:description description})
(when-let [fingerprint (:fingerprint field-metadata)]
{:fingerprint fingerprint})
;; if this column comes from a source Card (Saved Question/Model/etc.) use the name of the Card as the 'table' name
;; rather than the ACTUAL table name.
(when (= (:lib/source field-metadata) :source/card)
......
......@@ -3,6 +3,7 @@
[clojure.test :refer [are deftest is testing]]
[goog.object :as gobject]
[malli.core :as mc]
[medley.core :as m]
[metabase.lib.core :as lib]
[metabase.lib.js :as lib.js]
[metabase.lib.options :as lib.options]
......@@ -398,3 +399,121 @@
:day]
(lib.js/expression-clause "time-interval" [(meta/field-metadata :products :created-at)
(lib.js/expression-clause "interval" [10 "day"] nil) "day"] nil)))))
(defn- js= [a b]
(cond
;; Compare objects recursively for each key. If either object contains keys the other does not, that's false.
(and (object? a) (object? b))
(and (every? (fn [k] (and (gobject/containsKey b k)
(js= (gobject/get a k) (gobject/get b k))))
(js-keys a))
(every? (fn [k] (gobject/containsKey a k))
(js-keys b)))
;; Compare arrays by length and then pairwise recursively.
(and (array? a) (array? b))
(and (= (count a) (count b))
(every? boolean (map js= (seq a) (seq b))))
;; Default to Clojure's = which will compare strings, numbers, Clojure values, etc.
;; That will return false for mismatched types as well.
:else (= a b)))
(deftest ^:parallel js=-metatest
(testing "check js= works correctly (who tests the tests?)"
(testing "should be true"
(are [a b] (= true (js= a b))
7 7
0 0
-1 -1
nil nil
js/undefined nil
nil js/undefined
"foo" "foo"
true true
false false
;; Objects
#js {:foo "bar"}
#js {:foo "bar"}
#js {:foo "bar", :baz "quux"}
#js {:foo "bar", :baz "quux"}
;; Arrays
#js ["foo" #js [1 2 3]]
#js ["foo" #js [1 2 3]]
;; Nesting
#js [#js {:foo "bar", :baz #js [4 5]}, #js [1 2 3]]
#js [#js {:foo "bar", :baz #js [4 5]}, #js [1 2 3]]))
(testing "should be false"
(are [a b] (= false (js= a b))
7 8
0 1
-1 1
nil {}
"foo" "bar"
true false
false 7
;; Objects
#js {:foo "bar"} #js {:foo "baz"} ; Different value
#js {:foo "bar"} #js {} ; Missing an a key in b
#js {} #js {:foo "bar"} ; Missing a b key in a
#js {:foo nil} #js {} ; Missing is not the same as present-but-nil
#js {} #js {:foo nil} ; And likewise in reverse
;; Arrays
#js ["foo" "bar"] #js ["foo" "baz"] ; Different values
#js ["foo" "bar"] #js ["foo"] ; Different lengths
#js ["foo"] #js ["foo" "bar"]
;; Nesting
#js [#js {:foo "bar", :baz #js [4 5 6]}, #js [1 2 3]]
#js [#js {:foo "bar", :baz #js [4 5]}, #js [1 2 3]]))))
(deftest ^:parallel display-info-test
(let [query (lib/query meta/metadata-provider (meta/table-metadata :orders))
by-name (m/index-by :name (lib/visible-columns query))
discount (by-name "DISCOUNT")]
(testing "description is present in the display-info for a column"
(is (= (:description discount)
(.-description (lib.js/display-info query -1 discount))))
(testing "but if missing from the input, it's missing from the display-info"
(let [di (lib.js/display-info query -1 (dissoc discount :fingerprint))]
(is (not (gobject/containsKey di "description"))))))
(testing "fingerprint is included in display-info"
(let [query (lib/query meta/metadata-provider (meta/table-metadata :orders))
by-dca (m/index-by :lib/desired-column-alias (lib/visible-columns query))
discount (by-dca "DISCOUNT")
vendor (by-dca "PRODUCTS__via__PRODUCT_ID__VENDOR")
created-at (by-dca "CREATED_AT")]
(testing "for number columns"
(is (js= #js {:global #js {:distinctCount 479
:nil% 0.898}
:type #js {"type/Number" #js {:avg 5.161009803921569
:min 0.17
:max 61.7
:q1 2.978591571097236
:q3 7.337323315325942
:sd 3.053736975739119}}}
(.-fingerprint (lib.js/display-info query -1 discount)))))
(testing "for string columns"
(is (js= #js {:global #js {:distinctCount 200
:nil% 0}
:type #js {"type/Text" #js {:percentJson 0
:percentUrl 0
:percentEmail 0
:percentState 0
:averageLength 20.6}}}
(.-fingerprint (lib.js/display-info query -1 vendor)))))
(testing "for datetime columns"
(is (js= #js {:global #js {:distinctCount 10000
:nil% 0}
:type #js {"type/DateTime" #js {:earliest "2016-04-30T18:56:13.352Z"
:latest "2020-04-19T14:07:15.657Z"}}}
(.-fingerprint (lib.js/display-info query -1 created-at)))))
(testing "unless it's missing in the input"
(let [di (lib.js/display-info query -1 (dissoc discount :fingerprint))]
(is (not (gobject/containsKey di "fingerprint")))))))))
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment