Skip to content
Snippets Groups Projects
Unverified Commit f3930a6f authored by Cam Saul's avatar Cam Saul Committed by GitHub
Browse files

MLv2 support listing and adding order bys in JS (including JS unit tests) (#29664)

* [MLv2] Basic QP support for MLv2 queries

* Fix data/id init on launch

* MLv2 Order bys in JS (WIP)

* Fix Kondo errors

* Order bys in Cljs

* Add missing docstrings

* Prettier

* Seriously Kondo!

* Appease ESLint

* Fix docstring
parent 9abaddb3
No related branches found
No related tags found
No related merge requests found
import _ from "underscore";
import * as ML from "cljs/metabase.lib.js";
import * as ML_MetadataCalculation from "cljs/metabase.lib.metadata.calculation";
import * as SampleDatabase from "__support__/sample_database_fixture";
const createMetadataProvider = () =>
ML.metadataProvider(
SampleDatabase.SAMPLE_DATABASE.id,
SampleDatabase.metadata,
);
const createQuery = () => {
const query = ML.query(
SampleDatabase.SAMPLE_DATABASE.id,
createMetadataProvider(),
{
database: SampleDatabase.SAMPLE_DATABASE.id,
type: "query",
query: {
"source-table": SampleDatabase.ORDERS.id,
},
},
);
it("can create an MLv2 query", () => {
expect(query).toBeTruthy();
expect(ML.suggestedName(query)).toBe("Orders");
});
return query;
};
describe("orderableColumns", () => {
const query = createQuery();
const orderableColumns = ML.orderable_columns(query);
it("returns an array", () => {
expect(orderableColumns).toBeInstanceOf(Array);
});
describe("returns metadata for columns in the source table", () => {
it("contains ORDERS.ID", () => {
const ordersID = _.find(
orderableColumns,
({ id }) => id === SampleDatabase.ORDERS.ID.id,
);
expect(ordersID).toEqual(
expect.objectContaining({
table_id: SampleDatabase.ORDERS.id,
name: "ID",
id: SampleDatabase.ORDERS.ID.id,
display_name: "ID",
base_type: "type/BigInteger",
}),
);
});
});
describe("returns metadata for columns in implicitly joinable tables", () => {
it("contains PRODUCTS.TITLE", () => {
const productsTitle = _.find(
orderableColumns,
({ id }) => id === SampleDatabase.PRODUCTS.TITLE.id,
);
expect(productsTitle).toEqual(
expect.objectContaining({
table_id: SampleDatabase.PRODUCTS.id,
name: "TITLE",
id: SampleDatabase.PRODUCTS.TITLE.id,
display_name: "Title",
base_type: "type/Text",
}),
);
});
});
});
describe("add order by", () => {
const query = createQuery();
const orderBys = ML.order_bys(query);
it("should not have order bys yet", () => {
expect(orderBys).toBeNull();
});
const orderableColumns = ML.orderable_columns(query);
const productsTitle = _.find(
orderableColumns,
({ id }) => id === SampleDatabase.PRODUCTS.TITLE.id,
);
it("should include PRODUCTS.TITLE in orderableColumns", () => {
expect(productsTitle).toBeTruthy();
});
it("should update the query", () => {
const updatedQuery = ML.order_by(query, productsTitle);
// This name isn't GREAT but it's ok for now, we can update this if we improve MLv2.
expect(ML.suggestedName(updatedQuery)).toBe(
"Orders, Sorted by Title ascending",
);
const updatedOrderBys = ML.order_bys(updatedQuery);
expect(updatedOrderBys).toHaveLength(1);
const orderBy = updatedOrderBys[0];
expect(ML_MetadataCalculation.display_name(query, orderBy)).toBe(
"Title ascending",
);
});
});
......@@ -6,8 +6,11 @@
[metabase.lib.js.metadata :as js.metadata]
[metabase.lib.metadata.calculation :as lib.metadata.calculation]
[metabase.lib.metadata.protocols :as lib.metadata.protocols]
[metabase.lib.normalize :as lib.normalize]
[metabase.lib.order-by :as lib.order-by]
[metabase.lib.query :as lib.query]
[metabase.mbql.normalize :as mbql.normalize]
[metabase.util :as u]
[metabase.util.log :as log]))
;;; this is mostly to ensure all the relevant namespaces with multimethods impls get loaded.
......@@ -93,3 +96,37 @@
"Coerce a CLJS pMBQL query back to (1) a legacy query (2) in vanilla JS."
[query-map]
(-> query-map convert/->legacy-MBQL fix-namespaced-values clj->js))
(defn ^:export orderable-columns
"Return a sequence of Column metadatas about the columns you can add order bys for in a given stage of `a-query.` To
add an order by, pass the result to [[order-by]]."
([a-query]
(orderable-columns a-query -1))
([a-query stage-number]
(-> (lib.order-by/orderable-columns a-query stage-number)
(clj->js :keyword-fn u/qualified-name))))
(defn ^:export order-by
"Add an `order-by` clause to `a-query`. Returns updated query."
([a-query x]
(order-by a-query -1 x nil))
([a-query x direction]
(order-by a-query -1 x direction))
([a-query stage-number x direction]
(lib.order-by/order-by
a-query
stage-number
(lib.normalize/normalize (js->clj x :keywordize-keys true))
(js->clj direction))))
(defn ^:export order-bys
"Get the order-by clauses (as an array of opaque objects) in `a-query` at a given `stage-number`. Returns `nil` if
there are no order bys in the query."
([a-query]
(order-bys a-query -1))
([a-query stage-number]
(some-> (lib.order-by/order-bys a-query stage-number)
not-empty
to-array)))
......@@ -36,7 +36,8 @@
{:base-type keyword
:type keyword
:lib/type keyword
:lib/options normalize})
:lib/options normalize
:field_ref normalize})
(defn normalize-map
"[[normalize]] a map using `key-fn` (default [[clojure.core/keyword]]) for keys and
......
......@@ -66,11 +66,11 @@
(assoc (vec clause) 0 direction))
(mu/defn order-by
"Create an MBQL order-by clause (i.e., `:asc` or `:desc`) from something that you can theoretically sort by -- maybe a
"Add an MBQL order-by clause (i.e., `:asc` or `:desc`) from something that you can theoretically sort by -- maybe a
Field, or `:field` clause, or expression of some sort, etc.
You can teach Metabase lib how to generate order by clauses for different things by implementing the
underlying [[->order-by]] multimethod."
underlying [[->order-by-clause]] multimethod."
([query x]
(order-by query -1 x nil))
......@@ -87,7 +87,7 @@
(lib.util/update-query-stage query stage-number update :order-by (fn [order-bys]
(conj (vec order-bys) new-order-by))))))
(mu/defn order-bys :- [:maybe [:sequential ::lib.schema.order-by/order-by]]
(mu/defn ^:export order-bys :- [:maybe [:sequential ::lib.schema.order-by/order-by]]
"Get the order-by clauses in a query."
([query :- ::lib.schema/query]
(order-bys query -1))
......
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