Skip to content
Snippets Groups Projects
Unverified Commit 7abc9971 authored by metamben's avatar metamben Committed by GitHub
Browse files

Support adding and getting filters (#29381)

* Support adding and getting filters

* Add current-filters function for handling a conjunction of filters
parent df003b99
Branches
Tags
No related merge requests found
......@@ -103,6 +103,9 @@
with-join-alias]
[lib.filter
filter
add-filter
current-filter
current-filters
and
or
not
......
......@@ -7,6 +7,7 @@
[metabase.lib.metadata.calculation :as lib.metadata.calculation]
[metabase.lib.options :as lib.options]
[metabase.lib.schema]
[metabase.lib.schema.expression :as schema.expression]
[metabase.lib.temporal-bucket :as lib.temporal-bucket]
[metabase.lib.util :as lib.util]
[metabase.shared.util.i18n :as i18n]
......@@ -261,3 +262,53 @@
(let [stage-number (clojure.core/or stage-number -1)
new-filter (->filter-clause query stage-number boolean-expression)]
(lib.util/update-query-stage query stage-number assoc :filter new-filter))))
(defn- and-clause? [clause]
(clojure.core/and (vector? clause)
(clojure.core/= (first clause) :and)))
(mu/defn current-filter :- [:maybe ::schema.expression/boolean]
"Returns the current filter in stage with `stage-number` of `query`.
If `stage-number` is omitted, the last stage is used.
See also [[metabase.lib.util/query-stage]]."
([query :- :metabase.lib.schema/query] (current-filter query -1))
([query :- :metabase.lib.schema/query
stage-number :- :int]
(:filter (lib.util/query-stage query stage-number))))
(mu/defn current-filters :- [:sequential ::schema.expression/boolean]
"Returns the current filters in stage with `stage-number` of `query`.
If `stage-number` is omitted, the last stage is used. Logicaly, the
filter attached to the query is the conjunction of the expressions
in the returned list. If the returned list is empty, then there is no
filter attached to the query.
See also [[metabase.lib.util/query-stage]]."
([query :- :metabase.lib.schema/query] (current-filters query -1))
([query :- :metabase.lib.schema/query
stage-number :- :int]
(if-let [existing-filter (:filter (lib.util/query-stage query stage-number))]
(if (and-clause? existing-filter)
(subvec existing-filter 2)
[existing-filter])
[])))
(defn- conjoin [existing-filter new-filter]
(-> (cond
(nil? existing-filter) new-filter
(and-clause? existing-filter) (conj existing-filter new-filter)
:else [:and existing-filter new-filter])
lib.options/ensure-uuid))
(mu/defn add-filter :- :metabase.lib.schema/query
"Adds `boolean-expression` as a filter on `query` if there is no filter
yet, builds a conjunction with the current filter otherwise."
([query :- :metabase.lib.schema/query
boolean-expression]
(metabase.lib.filter/add-filter query -1 boolean-expression))
([query :- :metabase.lib.schema/query
stage-number :- :int
boolean-expression]
(let [stage-number (clojure.core/or stage-number -1)
new-filter (->filter-clause query stage-number boolean-expression)]
(lib.util/update-query-stage query stage-number update :filter conjoin new-filter))))
......@@ -117,23 +117,33 @@
venues-category-id-metadata (lib.metadata/field q1 nil "VENUES" "CATEGORY_ID")
venues-name-metadata (lib.metadata/field q1 nil "VENUES" "NAME")
categories-id-metadata (lib.metadata/stage-column q2 -1 "ID")
original-filter
[:between
{:lib/uuid string?}
[:field {:base-type :type/Integer :lib/uuid string?} (meta/id :venues :category-id)]
42
100]
simple-filtered-query
{:lib/type :mbql/query,
{:lib/type :mbql/query
:database (meta/id)
:type :pipeline
:stages [{:lib/type :mbql.stage/mbql
:source-table (meta/id :categories)
:lib/options {:lib/uuid string?},
:filter [:between
{:lib/uuid string?}
[:field {:base-type :type/Integer, :lib/uuid string?} (meta/id :venues :category-id)]
42
100]}]}]
:lib/options {:lib/uuid string?}
:filter original-filter}]}]
(testing "no filter"
(is (nil? (lib/current-filter q1)))
(is (= [] (lib/current-filters q2))))
(testing "setting a simple filter"
(is (=? simple-filtered-query
(-> q1
(lib/filter (lib/between {:lib/metadata meta/metadata} -1 venues-category-id-metadata 42 100))
(dissoc :lib/metadata)))))
(let [result-query
(lib/filter q1 (lib/between {:lib/metadata meta/metadata} -1 venues-category-id-metadata 42 100))]
(is (=? simple-filtered-query
(dissoc result-query :lib/metadata)))
(testing "and getting the current filter"
(is (=? original-filter
(lib/current-filter result-query)))
(is (=? [original-filter]
(lib/current-filters result-query))))))
(testing "setting a simple filter thunk"
(is (=? simple-filtered-query
......@@ -181,3 +191,73 @@
[:= venues-category-id-metadata 242 categories-id-metadata]
[:contains venues-name-metadata "part"]]])
(dissoc :lib/metadata)))))))
(deftest ^:parallel add-filter-test
(let [simple-query (lib/query-for-table-name meta/metadata-provider "CATEGORIES")
venues-name-metadata (lib.metadata/field simple-query nil "VENUES" "NAME")
first-filter
[:between
{:lib/uuid string?}
[:field
{:base-type :type/Integer, :lib/uuid string?}
(meta/id :venues :category-id)]
42
100]
second-filter
[:starts-with
{:lib/uuid string?}
[:field {:base-type :type/Text, :lib/uuid string?} (meta/id :venues :name)]
"prefix"]
third-filter
[:contains
{:lib/uuid string?}
[:field {:base-type :type/Text, :lib/uuid string?} (meta/id :venues :name)]
"part"]
first-add
(lib/add-filter simple-query
(lib/->between
(lib.metadata/field simple-query nil "VENUES" "CATEGORY_ID")
42
100))
filtered-query
(assoc-in simple-query [:stages 0 :filter] first-filter)
second-add
(lib/add-filter first-add
(lib/starts-with
{:lib/metadata meta/metadata}
0
venues-name-metadata
"prefix"))
and-query
(assoc-in filtered-query
[:stages 0 :filter]
[:and
{:lib/uuid string?}
first-filter
second-filter])
third-add
(lib/add-filter second-add
(lib/->contains venues-name-metadata "part"))
extended-and-query
(assoc-in filtered-query
[:stages 0 :filter]
[:and
{:lib/uuid string?}
first-filter
second-filter
[:contains
{:lib/uuid string?}
[:field {:base-type :type/Text, :lib/uuid string?} (meta/id :venues :name)]
"part"]])]
(testing "adding an initial filter"
(is (=? filtered-query first-add))
(is (=? [first-filter]
(lib/current-filters first-add))))
(testing "conjoining to filter"
(is (=? and-query second-add))
(is (=? [first-filter second-filter]
(lib/current-filters second-add))))
(testing "conjoining to conjunction filter"
(is (=? extended-and-query third-add))
(is (=? [first-filter second-filter third-filter]
(lib/current-filters third-add))))))
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment