Skip to content
Snippets Groups Projects
Unverified Commit daed46d5 authored by dpsutton's avatar dpsutton Committed by GitHub
Browse files

Dataset search (#18715)

* Move the multimethods over to strings not classes

datasets are Cards so don't have a good way to intercept. And this kinda
took all of the keywords, turned them into classes, and then didn't
actually use those classes-- they were just opaque keys in the
hierarchy. No bueno.

* Include datasets in search results

* Docstring for the linter. Thank you clj-kondo

* Fix namespace decls

* Update tests

* Clean up ns and make test more resilient

these tests that test global collections can be sensitive to test order
and the CI dbs that continually exist rather than be
recreated. Sometimes "table" shows up in these available models. Kinda
don't care so just check for subset

* Rename for confusion
parent 50c35300
No related branches found
No related tags found
No related merge requests found
...@@ -7,16 +7,12 @@ ...@@ -7,16 +7,12 @@
[honeysql.helpers :as h] [honeysql.helpers :as h]
[metabase.api.common :as api] [metabase.api.common :as api]
[metabase.db :as mdb] [metabase.db :as mdb]
[metabase.models.card :refer [Card]]
[metabase.models.card-favorite :refer [CardFavorite]] [metabase.models.card-favorite :refer [CardFavorite]]
[metabase.models.collection :as coll :refer [Collection]] [metabase.models.collection :as coll :refer [Collection]]
[metabase.models.dashboard :refer [Dashboard]]
[metabase.models.dashboard-favorite :refer [DashboardFavorite]] [metabase.models.dashboard-favorite :refer [DashboardFavorite]]
[metabase.models.database :refer [Database]]
[metabase.models.interface :as mi] [metabase.models.interface :as mi]
[metabase.models.metric :refer [Metric]] [metabase.models.metric :refer [Metric]]
[metabase.models.permissions :as perms] [metabase.models.permissions :as perms]
[metabase.models.pulse :refer [Pulse]]
[metabase.models.segment :refer [Segment]] [metabase.models.segment :refer [Segment]]
[metabase.models.table :refer [Table]] [metabase.models.table :refer [Table]]
[metabase.search.config :as search-config] [metabase.search.config :as search-config]
...@@ -39,7 +35,10 @@ ...@@ -39,7 +35,10 @@
(s/optional-key :offset-int) (s/maybe s/Int)}) (s/optional-key :offset-int) (s/maybe s/Int)})
(def ^:private SearchableModel (def ^:private SearchableModel
(apply s/enum search-config/searchable-models)) (apply s/enum search-config/all-models))
(def ^:private DBModel
(apply s/enum search-config/searchable-db-models))
(def ^:private HoneySQLColumn (def ^:private HoneySQLColumn
(s/cond-pre (s/cond-pre
...@@ -65,7 +64,7 @@ ...@@ -65,7 +64,7 @@
are cast to the appropriate type because Postgres will assume `SELECT NULL` is `TEXT` by default and will refuse to are cast to the appropriate type because Postgres will assume `SELECT NULL` is `TEXT` by default and will refuse to
`UNION` two columns of two different types.)" `UNION` two columns of two different types.)"
(ordered-map/ordered-map (ordered-map/ordered-map
;; returned for all models ;; returned for all models. Important to be first for changing model for dataset
:model :text :model :text
:id :integer :id :integer
:name :text :name :text
...@@ -98,7 +97,7 @@ ...@@ -98,7 +97,7 @@
(s/defn ^:private model->alias :- s/Keyword (s/defn ^:private model->alias :- s/Keyword
[model :- SearchableModel] [model :- SearchableModel]
(keyword (str/lower-case (name model)))) (keyword model))
(s/defn ^:private ->column-alias :- s/Keyword (s/defn ^:private ->column-alias :- s/Keyword
"Returns the column name. If the column is aliased, i.e. [`:original_name` `:aliased_name`], return the aliased "Returns the column name. If the column is aliased, i.e. [`:original_name` `:aliased_name`], return the aliased
...@@ -117,7 +116,7 @@ ...@@ -117,7 +116,7 @@
:let [maybe-aliased-col (get col-alias->honeysql-clause search-col)]] :let [maybe-aliased-col (get col-alias->honeysql-clause search-col)]]
(cond (cond
(= search-col :model) (= search-col :model)
[(hx/literal (name (model->alias model))) :model] [(hx/literal model) :model]
;; This is an aliased column, no need to include the table alias ;; This is an aliased column, no need to include the table alias
(sequential? maybe-aliased-col) (sequential? maybe-aliased-col)
...@@ -148,26 +147,27 @@ ...@@ -148,26 +147,27 @@
cols-or-nils (canonical-columns model column-alias->honeysql-clause)] cols-or-nils (canonical-columns model column-alias->honeysql-clause)]
cols-or-nils)) cols-or-nils))
(s/defn ^:private from-clause-for-model :- [(s/one [(s/one SearchableModel "model") (s/one s/Keyword "alias")] (s/defn ^:private from-clause-for-model :- [(s/one [(s/one DBModel "model") (s/one s/Keyword "alias")]
"from clause")] "from clause")]
[model :- SearchableModel] [model :- SearchableModel]
[[model (model->alias model)]]) (let [db-model (get search-config/model-to-db-model model)]
[[db-model (-> db-model name str/lower-case keyword)]]))
(defmulti ^:private archived-where-clause (defmulti ^:private archived-where-clause
{:arglists '([model archived?])} {:arglists '([model archived?])}
(fn [model _] (class model))) (fn [model _] model))
(defmethod archived-where-clause :default (defmethod archived-where-clause :default
[model archived?] [model archived?]
[:= (hsql/qualify (model->alias model) :archived) archived?]) [:= (hsql/qualify (model->alias model) :archived) archived?])
;; Databases can't be archived ;; Databases can't be archived
(defmethod archived-where-clause (class Database) (defmethod archived-where-clause "database"
[model archived?] [model archived?]
[:= 1 (if archived? 2 1)]) [:= 1 (if archived? 2 1)])
;; Table has an `:active` flag, but no `:archived` flag; never return inactive Tables ;; Table has an `:active` flag, but no `:archived` flag; never return inactive Tables
(defmethod archived-where-clause (class Table) (defmethod archived-where-clause "table"
[model archived?] [model archived?]
(if archived? (if archived?
[:= 1 0] ; No tables should appear in archive searches [:= 1 0] ; No tables should appear in archive searches
...@@ -246,11 +246,11 @@ ...@@ -246,11 +246,11 @@
(defmulti ^:private search-query-for-model (defmulti ^:private search-query-for-model
{:arglists '([model search-context])} {:arglists '([model search-context])}
(fn [model _] (class model))) (fn [model _] model))
(s/defmethod search-query-for-model (class Card) (s/defn ^:private shared-card-impl [dataset? :- s/Bool search-ctx :- SearchContext]
[_ search-ctx :- SearchContext] (-> (base-query-for-model "card" search-ctx)
(-> (base-query-for-model Card search-ctx) (update :where (fn [where] [:and [:= :card.dataset dataset?] where]))
(h/left-join [CardFavorite :fave] (h/left-join [CardFavorite :fave]
[:and [:and
[:= :card.id :fave.card_id] [:= :card.id :fave.card_id]
...@@ -258,48 +258,58 @@ ...@@ -258,48 +258,58 @@
(add-collection-join-and-where-clauses :card.collection_id search-ctx) (add-collection-join-and-where-clauses :card.collection_id search-ctx)
(add-card-db-id-clause (:table-db-id search-ctx)))) (add-card-db-id-clause (:table-db-id search-ctx))))
(s/defmethod search-query-for-model (class Collection) (s/defmethod search-query-for-model "card"
[_ search-ctx :- SearchContext] [_model search-ctx :- SearchContext]
(-> (base-query-for-model Collection search-ctx) (shared-card-impl false search-ctx))
(s/defmethod search-query-for-model "dataset"
[_model search-ctx :- SearchContext]
(-> (shared-card-impl true search-ctx)
(update :select (fn [columns]
(cons [(hx/literal "dataset") :model] (rest columns))))))
(s/defmethod search-query-for-model "collection"
[model search-ctx :- SearchContext]
(-> (base-query-for-model model search-ctx)
(add-collection-join-and-where-clauses :collection.id search-ctx))) (add-collection-join-and-where-clauses :collection.id search-ctx)))
(s/defmethod search-query-for-model (class Database) (s/defmethod search-query-for-model "database"
[_ search-ctx :- SearchContext] [model search-ctx :- SearchContext]
(base-query-for-model Database search-ctx)) (base-query-for-model model search-ctx))
(s/defmethod search-query-for-model (class Dashboard) (s/defmethod search-query-for-model "dashboard"
[_ search-ctx :- SearchContext] [model search-ctx :- SearchContext]
(-> (base-query-for-model Dashboard search-ctx) (-> (base-query-for-model model search-ctx)
(h/left-join [DashboardFavorite :fave] (h/left-join [DashboardFavorite :fave]
[:and [:and
[:= :dashboard.id :fave.dashboard_id] [:= :dashboard.id :fave.dashboard_id]
[:= :fave.user_id api/*current-user-id*]]) [:= :fave.user_id api/*current-user-id*]])
(add-collection-join-and-where-clauses :dashboard.collection_id search-ctx))) (add-collection-join-and-where-clauses :dashboard.collection_id search-ctx)))
(s/defmethod search-query-for-model (class Pulse) (s/defmethod search-query-for-model "pulse"
[_ search-ctx :- SearchContext] [model search-ctx :- SearchContext]
;; Pulses don't currently support being archived, omit if archived is true ;; Pulses don't currently support being archived, omit if archived is true
(-> (base-query-for-model Pulse search-ctx) (-> (base-query-for-model model search-ctx)
(add-collection-join-and-where-clauses :pulse.collection_id search-ctx) (add-collection-join-and-where-clauses :pulse.collection_id search-ctx)
;; We don't want alerts included in pulse results ;; We don't want alerts included in pulse results
(h/merge-where [:and (h/merge-where [:and
[:= :alert_condition nil] [:= :alert_condition nil]
[:= :pulse.dashboard_id nil]]))) [:= :pulse.dashboard_id nil]])))
(s/defmethod search-query-for-model (class Metric) (s/defmethod search-query-for-model "metric"
[_ search-ctx :- SearchContext] [model search-ctx :- SearchContext]
(-> (base-query-for-model Metric search-ctx) (-> (base-query-for-model model search-ctx)
(h/left-join [Table :table] [:= :metric.table_id :table.id]))) (h/left-join [Table :table] [:= :metric.table_id :table.id])))
(s/defmethod search-query-for-model (class Segment) (s/defmethod search-query-for-model "segment"
[_ search-ctx :- SearchContext] [model search-ctx :- SearchContext]
(-> (base-query-for-model Segment search-ctx) (-> (base-query-for-model model search-ctx)
(h/left-join [Table :table] [:= :segment.table_id :table.id]))) (h/left-join [Table :table] [:= :segment.table_id :table.id])))
(s/defmethod search-query-for-model (class Table) (s/defmethod search-query-for-model "table"
[_ {:keys [current-user-perms table-db-id], :as search-ctx} :- SearchContext] [model {:keys [current-user-perms table-db-id], :as search-ctx} :- SearchContext]
(when (seq current-user-perms) (when (seq current-user-perms)
(let [base-query (base-query-for-model Table search-ctx)] (let [base-query (base-query-for-model model search-ctx)]
(add-table-db-id-clause (add-table-db-id-clause
(if (contains? current-user-perms "/") (if (contains? current-user-perms "/")
base-query base-query
...@@ -355,18 +365,12 @@ ...@@ -355,18 +365,12 @@
[{:keys [id]}] [{:keys [id]}]
(-> id Segment mi/can-read?)) (-> id Segment mi/can-read?))
(defn- models-to-search
[{:keys [models]} default]
(if models
(vec (map search-config/model-name->instance models))
default))
(defn- query-model-set (defn- query-model-set
"Queries all models with respect to query for one result, to see if we get a result or not" "Queries all models with respect to query for one result, to see if we get a result or not"
[search-ctx] [search-ctx]
(map #((first %) :model) (map #((first %) :model)
(filter not-empty (filter not-empty
(for [model search-config/searchable-models] (for [model search-config/all-models]
(let [search-query (search-query-for-model model search-ctx) (let [search-query (search-query-for-model model search-ctx)
query-with-limit (h/limit search-query 1)] query-with-limit (h/limit search-query 1)]
(db/query query-with-limit)))))) (db/query query-with-limit))))))
...@@ -376,7 +380,8 @@ ...@@ -376,7 +380,8 @@
to make the union-all degenerate down to trivial case of one model without errors. to make the union-all degenerate down to trivial case of one model without errors.
Therefore, we degenerate it down for it" Therefore, we degenerate it down for it"
[search-ctx] [search-ctx]
(let [models (models-to-search search-ctx search-config/searchable-models) (let [models (or (:models search-ctx)
search-config/all-models)
sql-alias :alias_is_required_by_sql_but_not_needed_here sql-alias :alias_is_required_by_sql_but_not_needed_here
order-clause [((fnil order-clause "") (:search-string search-ctx))]] order-clause [((fnil order-clause "") (:search-string search-ctx))]]
(if (= (count models) 1) (if (= (count models) 1)
...@@ -388,7 +393,6 @@ ...@@ -388,7 +393,6 @@
query)} sql-alias]] query)} sql-alias]]
:order-by order-clause}))) :order-by order-clause})))
(s/defn ^:private search (s/defn ^:private search
"Builds a search query that includes all of the searchable entities and runs it" "Builds a search query that includes all of the searchable entities and runs it"
[search-ctx :- SearchContext] [search-ctx :- SearchContext]
...@@ -410,15 +414,15 @@ ...@@ -410,15 +414,15 @@
;; We get to do this slicing and dicing with the result data because ;; We get to do this slicing and dicing with the result data because
;; the pagination of search is for UI improvement, not for performance. ;; the pagination of search is for UI improvement, not for performance.
;; We intend for the cardinality of the search results to be below the default max before this slicing occurs ;; We intend for the cardinality of the search results to be below the default max before this slicing occurs
{ :total (count total-results) {:total (count total-results)
:data (cond->> total-results :data (cond->> total-results
(some? (:offset-int search-ctx)) (drop (:offset-int search-ctx)) (some? (:offset-int search-ctx)) (drop (:offset-int search-ctx))
(some? (:limit-int search-ctx)) (take (:limit-int search-ctx))) (some? (:limit-int search-ctx)) (take (:limit-int search-ctx)))
:available_models (query-model-set search-ctx) :available_models (query-model-set search-ctx)
:limit (:limit-int search-ctx) :limit (:limit-int search-ctx)
:offset (:offset-int search-ctx) :offset (:offset-int search-ctx)
:table_db_id (:table-db-id search-ctx) :table_db_id (:table-db-id search-ctx)
:models (:models search-ctx)}))) :models (:models search-ctx)})))
;;; +----------------------------------------------------------------------------------------------------------------+ ;;; +----------------------------------------------------------------------------------------------------------------+
;;; | Endpoint | ;;; | Endpoint |
...@@ -449,7 +453,7 @@ ...@@ -449,7 +453,7 @@
(api/defendpoint GET "/" (api/defendpoint GET "/"
"Search within a bunch of models for the substring `q`. "Search within a bunch of models for the substring `q`.
For the list of models, check `metabase.search.config/searchable-models. For the list of models, check `metabase.search.config/all-models.
To search in archived portions of models, pass in `archived=true`. To search in archived portions of models, pass in `archived=true`.
If you want, while searching tables, only tables of a certain DB id, If you want, while searching tables, only tables of a certain DB id,
......
(ns metabase.search.config (ns metabase.search.config
(:require [cheshire.core :as json] (:require [cheshire.core :as json]
[clojure.string :as str]
[honeysql.core :as hsql] [honeysql.core :as hsql]
[metabase.models :refer [Card Collection Dashboard Database Metric Pulse Segment Table]] [metabase.models :refer [Card Collection Dashboard Database Metric Pulse Segment Table]]
[metabase.models.setting :refer [defsetting]] [metabase.models.setting :refer [defsetting]]
...@@ -37,26 +36,26 @@ ...@@ -37,26 +36,26 @@
"Show this many words of context before/after matches in long search results" "Show this many words of context before/after matches in long search results"
2) 2)
(def searchable-models (def searchable-db-models
"Models that can be searched. The order of this list also influences the order of the results: items earlier in the "Models that can be searched."
#{Dashboard Metric Segment Card Collection Table Pulse Database})
(def model-to-db-model
"Mapping from string model to the Toucan model backing it."
{"dashboard" Dashboard
"metric" Metric
"segment" Segment
"card" Card
"dataset" Card
"collection" Collection
"table" Table
"pulse" Pulse
"database" Database})
(def all-models
"All valid models to search for. The order of this list also influences the order of the results: items earlier in the
list will be ranked higher." list will be ranked higher."
[Dashboard Metric Segment Card Collection Table Pulse Database]) ["dashboard" "metric" "segment" "card" "dataset" "collection" "table" "pulse" "database"])
(defn model-name->class
"Given a model name as a string, return its Class."
[model-name]
(Class/forName (format "metabase.models.%s.%sInstance" model-name (str/capitalize model-name))))
(defn model-name->instance
"Given a model name as a string, return the specific instance"
[model-name]
(first (filter (fn [x] (= (str/capitalize model-name) (:name x))) searchable-models)))
(defn- ->class
[class-or-instance]
(if (class? class-or-instance)
class-or-instance
(class class-or-instance)))
(def ^:const displayed-columns (def ^:const displayed-columns
"All of the result components that by default are displayed by the frontend." "All of the result components that by default are displayed by the frontend."
...@@ -65,29 +64,29 @@ ...@@ -65,29 +64,29 @@
(defmulti searchable-columns-for-model (defmulti searchable-columns-for-model
"The columns that will be searched for the query." "The columns that will be searched for the query."
{:arglists '([model])} {:arglists '([model])}
->class) (fn [model] model))
(defmethod searchable-columns-for-model :default (defmethod searchable-columns-for-model :default
[_] [_]
[:name]) [:name])
(defmethod searchable-columns-for-model (class Card) (defmethod searchable-columns-for-model "card"
[_] [_]
[:name [:name
:dataset_query :dataset_query
:description]) :description])
(defmethod searchable-columns-for-model (class Dashboard) (defmethod searchable-columns-for-model "dashboard"
[_] [_]
[:name [:name
:description]) :description])
(defmethod searchable-columns-for-model (class Database) (defmethod searchable-columns-for-model "database"
[_] [_]
[:name [:name
:description]) :description])
(defmethod searchable-columns-for-model (class Table) (defmethod searchable-columns-for-model "table"
[_] [_]
[:name [:name
:display_name]) :display_name])
...@@ -118,9 +117,9 @@ ...@@ -118,9 +117,9 @@
(defmulti columns-for-model (defmulti columns-for-model
"The columns that will be returned by the query for `model`, excluding `:model`, which is added automatically." "The columns that will be returned by the query for `model`, excluding `:model`, which is added automatically."
{:arglists '([model])} {:arglists '([model])}
->class) (fn [model] model))
(defmethod columns-for-model (class Card) (defmethod columns-for-model "card"
[_] [_]
(conj default-columns :collection_id :collection_position :dataset_query (conj default-columns :collection_id :collection_position :dataset_query
[:collection.name :collection_name] [:collection.name :collection_name]
...@@ -138,34 +137,34 @@ ...@@ -138,34 +137,34 @@
:moderated_status] :moderated_status]
favorite-col dashboardcard-count-col)) favorite-col dashboardcard-count-col))
(defmethod columns-for-model (class Dashboard) (defmethod columns-for-model "dashboard"
[_] [_]
(conj default-columns :collection_id :collection_position favorite-col (conj default-columns :collection_id :collection_position favorite-col
[:collection.name :collection_name] [:collection.name :collection_name]
[:collection.authority_level :collection_authority_level])) [:collection.authority_level :collection_authority_level]))
(defmethod columns-for-model (class Database) (defmethod columns-for-model "database"
[_] [_]
[:id :name :description :updated_at]) [:id :name :description :updated_at])
(defmethod columns-for-model (class Pulse) (defmethod columns-for-model "pulse"
[_] [_]
[:id :name :collection_id [:collection.name :collection_name]]) [:id :name :collection_id [:collection.name :collection_name]])
(defmethod columns-for-model (class Collection) (defmethod columns-for-model "collection"
[_] [_]
(conj (remove #{:updated_at} default-columns) [:id :collection_id] [:name :collection_name] (conj (remove #{:updated_at} default-columns) [:id :collection_id] [:name :collection_name]
[:authority_level :collection_authority_level])) [:authority_level :collection_authority_level]))
(defmethod columns-for-model (class Segment) (defmethod columns-for-model "segment"
[_] [_]
(into default-columns table-columns)) (into default-columns table-columns))
(defmethod columns-for-model (class Metric) (defmethod columns-for-model "metric"
[_] [_]
(into default-columns table-columns)) (into default-columns table-columns))
(defmethod columns-for-model (class Table) (defmethod columns-for-model "table"
[_] [_]
[:id [:id
:name :name
......
...@@ -86,7 +86,7 @@ ...@@ -86,7 +86,7 @@
(defn- text-score-with (defn- text-score-with
[weighted-scorers query-tokens search-result] [weighted-scorers query-tokens search-result]
(let [total-weight (reduce + (map :weight weighted-scorers)) (let [total-weight (reduce + (map :weight weighted-scorers))
scores (for [column (search-config/searchable-columns-for-model (search-config/model-name->class (:model search-result))) scores (for [column (search-config/searchable-columns-for-model (:model search-result))
:let [matched-text (-> search-result :let [matched-text (-> search-result
(get column) (get column)
(search-config/column->string (:model search-result) column)) (search-config/column->string (:model search-result) column))
...@@ -157,10 +157,7 @@ ...@@ -157,10 +157,7 @@
:weight 2}]) :weight 2}])
(def ^:private model->sort-position (def ^:private model->sort-position
(into {} (map-indexed (fn [i model] (zipmap (reverse search-config/all-models) (range)))
[(str/lower-case (name model)) i])
;; Reverse so that they're in descending order
(reverse search-config/searchable-models))))
(defn- model-score (defn- model-score
[{:keys [model]}] [{:keys [model]}]
......
(ns metabase.api.search-test (ns metabase.api.search-test
(:require [clojure.string :as str] (:require [clojure.set :as set]
[clojure.string :as str]
[clojure.test :refer :all] [clojure.test :refer :all]
[honeysql.core :as hsql] [honeysql.core :as hsql]
[metabase.api.search :as api.search] [metabase.api.search :as api.search]
[metabase.models [metabase.models
:refer :refer
[Card [Card CardFavorite Collection Dashboard DashboardCard DashboardFavorite
CardFavorite Database Metric PermissionsGroup PermissionsGroupMembership Pulse PulseCard
Collection Segment Table]]
Dashboard
DashboardCard
DashboardFavorite
Database
Metric
PermissionsGroup
PermissionsGroupMembership
Pulse
PulseCard
Segment
Table]]
[metabase.models.permissions :as perms] [metabase.models.permissions :as perms]
[metabase.models.permissions-group :as group] [metabase.models.permissions-group :as group]
[metabase.search.config :as search-config] [metabase.search.config :as search-config]
...@@ -77,6 +67,7 @@ ...@@ -77,6 +67,7 @@
[(make-result "dashboard test dashboard", :model "dashboard", :favorite false) [(make-result "dashboard test dashboard", :model "dashboard", :favorite false)
test-collection test-collection
(make-result "card test card", :model "card", :favorite false, :dataset_query nil, :dashboardcard_count 0) (make-result "card test card", :model "card", :favorite false, :dataset_query nil, :dashboardcard_count 0)
(make-result "dataset test dataset", :model "dataset", :favorite false, :dataset_query nil, :dashboardcard_count 0)
(make-result "pulse test pulse", :model "pulse", :archived nil, :updated_at false) (make-result "pulse test pulse", :model "pulse", :archived nil, :updated_at false)
(merge (merge
(make-result "metric test metric", :model "metric", :description "Lookin' for a blueberry") (make-result "metric test metric", :model "metric", :description "Lookin' for a blueberry")
...@@ -100,7 +91,7 @@ ...@@ -100,7 +91,7 @@
search-item))) search-item)))
(defn- default-results-with-collection [] (defn- default-results-with-collection []
(on-search-types #{"dashboard" "pulse" "card"} (on-search-types #{"dashboard" "pulse" "card" "dataset"}
#(assoc % :collection {:id true, :name true :authority_level nil}) #(assoc % :collection {:id true, :name true :authority_level nil})
(default-search-results))) (default-search-results)))
...@@ -113,12 +104,15 @@ ...@@ -113,12 +104,15 @@
{:collection_id (u/the-id collection)})))] {:collection_id (u/the-id collection)})))]
(mt/with-temp* [Collection [coll (data-map "collection %s collection")] (mt/with-temp* [Collection [coll (data-map "collection %s collection")]
Card [card (coll-data-map "card %s card" coll)] Card [card (coll-data-map "card %s card" coll)]
Card [dataset (assoc (coll-data-map "dataset %s dataset" coll)
:dataset true)]
Dashboard [dashboard (coll-data-map "dashboard %s dashboard" coll)] Dashboard [dashboard (coll-data-map "dashboard %s dashboard" coll)]
Pulse [pulse (coll-data-map "pulse %s pulse" coll)] Pulse [pulse (coll-data-map "pulse %s pulse" coll)]
Metric [metric (data-map "metric %s metric")] Metric [metric (data-map "metric %s metric")]
Segment [segment (data-map "segment %s segment")]] Segment [segment (data-map "segment %s segment")]]
(f {:collection coll (f {:collection coll
:card card :card card
:dataset dataset
:dashboard dashboard :dashboard dashboard
:pulse pulse :pulse pulse
:metric metric :metric metric
...@@ -151,7 +145,7 @@ ...@@ -151,7 +145,7 @@
(dissoc :scores)))) (dissoc :scores))))
(defn- make-search-request [user-kwd params] (defn- make-search-request [user-kwd params]
(apply (partial mt/user-http-request user-kwd) :get 200 "search" params)) (apply mt/user-http-request user-kwd :get 200 "search" params))
(defn- search-request-data-with [xf user-kwd & params] (defn- search-request-data-with [xf user-kwd & params]
(let [raw-results-data (:data (make-search-request user-kwd params)) (let [raw-results-data (:data (make-search-request user-kwd params))
...@@ -225,7 +219,7 @@ ...@@ -225,7 +219,7 @@
(is (<= 4 (count (search-request-data :crowberto :q "test" :limit "100" :offset "2")))))) (is (<= 4 (count (search-request-data :crowberto :q "test" :limit "100" :offset "2"))))))
(testing "It offsets without limit properly" (testing "It offsets without limit properly"
(with-search-items-in-root-collection "test" (with-search-items-in-root-collection "test"
(is (= 4 (count (search-request-data :crowberto :q "test" :offset "2")))))) (is (= 5 (count (search-request-data :crowberto :q "test" :offset "2"))))))
(testing "It limits without offset properly" (testing "It limits without offset properly"
(with-search-items-in-root-collection "test" (with-search-items-in-root-collection "test"
(is (= 2 (count (search-request-data :crowberto :q "test" :limit "2")))))) (is (= 2 (count (search-request-data :crowberto :q "test" :limit "2"))))))
...@@ -233,6 +227,14 @@ ...@@ -233,6 +227,14 @@
(with-search-items-in-root-collection "test" (with-search-items-in-root-collection "test"
(is (= 0 (count (search-request-data :crowberto :q "test" :models "database")))) (is (= 0 (count (search-request-data :crowberto :q "test" :models "database"))))
(is (= 1 (count (search-request-data :crowberto :q "test" :models "database" :models "card")))))) (is (= 1 (count (search-request-data :crowberto :q "test" :models "database" :models "card"))))))
(testing "It distinguishes datasets from cards"
(with-search-items-in-root-collection "test"
(let [results (search-request-data :crowberto :q "test" :models "dataset")]
(is (= 1 (count results)))
(is (= "dataset" (-> results first :model))))
(let [results (search-request-data :crowberto :q "test" :models "card")]
(is (= 1 (count results)))
(is (= "card" (-> results first :model))))))
(testing "It returns limit and offset params in return result" (testing "It returns limit and offset params in return result"
(with-search-items-in-root-collection "test" (with-search-items-in-root-collection "test"
(is (= 2 (:limit (search-request :crowberto :q "test" :limit "2" :offset "3")))) (is (= 2 (:limit (search-request :crowberto :q "test" :limit "2" :offset "3"))))
...@@ -241,8 +243,11 @@ ...@@ -241,8 +243,11 @@
(deftest query-model-set (deftest query-model-set
(testing "It returns some stuff when you get results" (testing "It returns some stuff when you get results"
(with-search-items-in-root-collection "test" (with-search-items-in-root-collection "test"
(is (contains? (apply hash-set (:available_models ;; sometimes there is a "table" in these responses. might be do to garbage in CI
(mt/user-http-request :crowberto :get 200 "search?q=test"))) "dashboard")))) (is (set/subset? #{"dashboard" "dataset" "segment" "collection" "pulse" "database" "metric" "card"}
(-> (mt/user-http-request :crowberto :get 200 "search?q=test")
:available_models
set)))))
(testing "It returns nothing if there are no results" (testing "It returns nothing if there are no results"
(with-search-items-in-root-collection "test" (with-search-items-in-root-collection "test"
(is (= [] (:available_models (mt/user-http-request :crowberto :get 200 "search?q=noresults"))))))) (is (= [] (:available_models (mt/user-http-request :crowberto :get 200 "search?q=noresults")))))))
...@@ -419,6 +424,7 @@ ...@@ -419,6 +424,7 @@
(testing "Should return unarchived results by default" (testing "Should return unarchived results by default"
(with-search-items-in-root-collection "test" (with-search-items-in-root-collection "test"
(mt/with-temp* [Card [_ (archived {:name "card test card 2"})] (mt/with-temp* [Card [_ (archived {:name "card test card 2"})]
Card [_ (archived {:name "dataset test dataset" :dataset true})]
Dashboard [_ (archived {:name "dashboard test dashboard 2"})] Dashboard [_ (archived {:name "dashboard test dashboard 2"})]
Collection [_ (archived {:name "collection test collection 2"})] Collection [_ (archived {:name "collection test collection 2"})]
Metric [_ (archived {:name "metric test metric 2"})] Metric [_ (archived {:name "metric test metric 2"})]
...@@ -430,6 +436,7 @@ ...@@ -430,6 +436,7 @@
(with-search-items-in-root-collection "test2" (with-search-items-in-root-collection "test2"
(mt/with-temp* [Card [_ (archived {:name "card test card"})] (mt/with-temp* [Card [_ (archived {:name "card test card"})]
Card [_ (archived {:name "card that will not appear in results"})] Card [_ (archived {:name "card that will not appear in results"})]
Card [_ (archived {:name "dataset test dataset" :dataset true})]
Dashboard [_ (archived {:name "dashboard test dashboard"})] Dashboard [_ (archived {:name "dashboard test dashboard"})]
Collection [_ (archived {:name "collection test collection"})] Collection [_ (archived {:name "collection test collection"})]
Metric [_ (archived {:name "metric test metric"})] Metric [_ (archived {:name "metric test metric"})]
...@@ -439,6 +446,7 @@ ...@@ -439,6 +446,7 @@
(testing "Should return archived results when specified without a search query" (testing "Should return archived results when specified without a search query"
(with-search-items-in-root-collection "test2" (with-search-items-in-root-collection "test2"
(mt/with-temp* [Card [_ (archived {:name "card test card"})] (mt/with-temp* [Card [_ (archived {:name "card test card"})]
Card [_ (archived {:name "dataset test dataset" :dataset true})]
Dashboard [_ (archived {:name "dashboard test dashboard"})] Dashboard [_ (archived {:name "dashboard test dashboard"})]
Collection [_ (archived {:name "collection test collection"})] Collection [_ (archived {:name "collection test collection"})]
Metric [_ (archived {:name "metric test metric"})] Metric [_ (archived {:name "metric test metric"})]
......
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