Skip to content

Commit

Permalink
Rename granularities to hierarchies
Browse files Browse the repository at this point in the history
  • Loading branch information
retro committed Aug 7, 2024
1 parent a197102 commit 156047f
Show file tree
Hide file tree
Showing 11 changed files with 201 additions and 220 deletions.
11 changes: 4 additions & 7 deletions src/__tests__/clone.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const userModel = semanticLayer
type: "string",
sql: ({ model, sql }) => sql`COUNT(DISTINCT ${model.column("UserId")})`,
})
.withCategoricalGranularity("customer", ({ element }) => [
.withCategoricalHierarchy("customer", ({ element }) => [
element("user")
.withDimensions(["user_id", "first_name", "last_name"])
.withFormat(
Expand Down Expand Up @@ -85,13 +85,10 @@ const queryBuilder = repository.build("postgresql");
describe("clone", async () => {
it("can clone a model", async () => {
assert.deepEqual(
userModel.categoricalGranularities,
customerModel.categoricalGranularities,
);
assert.deepEqual(
userModel.granularitiesNames,
customerModel.granularitiesNames,
userModel.categoricalHierarchies,
customerModel.categoricalHierarchies,
);
assert.deepEqual(userModel.hierarchyNames, customerModel.hierarchyNames);

const query = queryBuilder.buildQuery({
members: [
Expand Down
6 changes: 3 additions & 3 deletions src/__tests__/dialects/mssql.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import * as semanticLayer from "../../index.js";

import { assert, beforeAll, describe, it } from "vitest";

import { InferSqlQueryResultType } from "../../index.js";
import { MSSQLServerContainer } from "@testcontainers/mssqlserver";
import fs from "node:fs/promises";
import mssql from "mssql";
import path from "node:path";
import { MSSQLServerContainer } from "@testcontainers/mssqlserver";
import mssql from "mssql";
import { InferSqlQueryResultType } from "../../index.js";

const customersModel = semanticLayer
.model()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,15 @@ const customersModel = semanticLayer
type: "string",
sql: ({ model }) => model.column("Email"),
})
.withCategoricalGranularity("full_address", ({ element }) => [
.withCategoricalHierarchy("full_address", ({ element }) => [
element.fromDimension("country"),
element.fromDimension("state"),
element.fromDimension("city"),
element.fromDimension("postal_code"),
element.fromDimension("address"),
])

.withCategoricalGranularity("personal_information", ({ element }) => [
.withCategoricalHierarchy("personal_information", ({ element }) => [
element("customer")
.withDimensions(["customer_id", "first_name", "last_name"])
.withKey(["customer_id"])
Expand All @@ -73,7 +73,7 @@ const customersModel = semanticLayer
`${dimension("first_name")} ${dimension("last_name")}`,
),
])
.withCategoricalGranularity("company", ({ element }) => [
.withCategoricalHierarchy("company", ({ element }) => [
element.fromDimension("company"),
]);

Expand Down Expand Up @@ -119,7 +119,7 @@ const invoicesModel = semanticLayer
description: "Invoice total.",
sql: ({ model, sql }) => sql`SUM(COALESCE, ${model.column("Total")}, 0))`,
})
.withCategoricalGranularity("billing_address", ({ element }) => [
.withCategoricalHierarchy("billing_address", ({ element }) => [
element.fromDimension("billing_country"),
element.fromDimension("billing_state"),
element.fromDimension("billing_city"),
Expand Down Expand Up @@ -233,13 +233,13 @@ const artistModel = semanticLayer
sql: ({ model }) => model.column("Name"),
format: (value) => `Artist: ${value}`,
})
.withCategoricalGranularity("artist", ({ element }) => [
.withCategoricalHierarchy("artist", ({ element }) => [
element("name")
.withDimensions(["name", "artist_id"])
.withKey(["artist_id"])
.withFormat(["name"], ({ dimension }) => `${dimension("name")}`),
])
.withCategoricalGranularity("formatting_test", ({ element }) => [
.withCategoricalHierarchy("formatting_test", ({ element }) => [
element("name1").withDimensions(["name", "artist_id"]),
element("name2")
.withDimensions(["name", "artist_id"])
Expand Down Expand Up @@ -277,7 +277,7 @@ const mediaTypeModel = semanticLayer
type: "string",
sql: ({ model }) => model.column("Name"),
})
.withCategoricalGranularity("media_type", ({ element }) => [
.withCategoricalHierarchy("media_type", ({ element }) => [
element("name")
.withDimensions(["name", "media_type_id"])
.withFormat(["name"], ({ dimension }) => `${dimension("name")}`),
Expand All @@ -296,7 +296,7 @@ const genreModel = semanticLayer
primaryKey: true,
sql: ({ model }) => model.column("GenreId"),
})
.withCategoricalGranularity("genre", ({ element }) => [
.withCategoricalHierarchy("genre", ({ element }) => [
element("name")
.withDimensions(["name", "genre_id"])
.withFormat(["name"], ({ dimension }) => `${dimension("name")}`),
Expand All @@ -315,7 +315,7 @@ const playlistModel = semanticLayer
type: "string",
sql: ({ model }) => model.column("Name"),
})
.withCategoricalGranularity("name", ({ element }) => [
.withCategoricalHierarchy("name", ({ element }) => [
element("name")
.withDimensions(["playlist_id", "name"])
.withKey(["playlist_id"])
Expand Down Expand Up @@ -347,7 +347,7 @@ const repository = semanticLayer
.withModel(genreModel)
.withModel(playlistModel)
.withModel(playlistTrackModel)
.withCategoricalGranularity("album", ({ element }) => [
.withCategoricalHierarchy("album", ({ element }) => [
element("artists.name")
.withDimensions(["artists.name", "artists.artist_id"])
.withKey(["artists.artist_id"])
Expand All @@ -363,8 +363,7 @@ const repository = semanticLayer
({ dimension }) => `${dimension("albums.title")}`,
),
])
.withCategoricalGranularity("track", ({ element }) => [
// Reduce this duplication by tracking element names in the generic, and then using them here by adding a function that will look like this: granularity("album").element("artists.name")
.withCategoricalHierarchy("track", ({ element }) => [
element("artists.name")
.withDimensions(["artists.name", "artists.artist_id"])
.withKey(["artists.artist_id"])
Expand All @@ -387,7 +386,7 @@ const repository = semanticLayer
({ dimension }) => `${dimension("tracks.name")}`,
),
])
.withCategoricalGranularity("formatting_test", ({ element }) => [
.withCategoricalHierarchy("formatting_test", ({ element }) => [
element("name1").withDimensions(["artists.name", "artists.artist_id"]),
element("name2")
.withDimensions(["artists.name", "artists.artist_id"])
Expand Down Expand Up @@ -486,28 +485,28 @@ const repository = semanticLayer

const queryBuilder = repository.build("postgresql");

it("can correctly generate granularities", () => {
const granularitiesWithoutFormatters = queryBuilder.granularities.map(
(granularity) => {
const elements = granularity.elements.map(
it("can correctly generate hierarchies", () => {
const hierarchiesWithoutFormatters = queryBuilder.hierarchies.map(
(hierarchy) => {
const elements = hierarchy.elements.map(
({ formatter: _formatter, ...element }) => {
return element;
},
);
return {
...granularity,
...hierarchy,
elements: elements,
};
},
);

for (const granularity of queryBuilder.granularities) {
for (const element of granularity.elements) {
for (const hierarchy of queryBuilder.hierarchies) {
for (const element of hierarchy.elements) {
assert.isFunction(element.formatter);
}
}

assert.deepEqual(granularitiesWithoutFormatters, [
assert.deepEqual(hierarchiesWithoutFormatters, [
{
name: "album",
type: "categorical",
Expand Down Expand Up @@ -826,14 +825,14 @@ it("can correctly format granularities", () => {
artists___artist_id: 1,
artists___name: "AC/DC",
};
const granularity1 = queryBuilder.getGranularity("artists.formatting_test");
const formattedValues1 = granularity1.elements.map((element) => [
const hierarchy1 = queryBuilder.getHierarchy("artists.formatting_test");
const formattedValues1 = hierarchy1.elements.map((element) => [
element.name,
element.formatter(row),
]);

const granularity2 = queryBuilder.getGranularity("formatting_test");
const formattedValues2 = granularity2.elements.map((element) => [
const hierarchy2 = queryBuilder.getHierarchy("formatting_test");
const formattedValues2 = hierarchy2.elements.map((element) => [
element.name,
element.formatter(row),
]);
Expand Down
23 changes: 9 additions & 14 deletions src/lib/custom-granularity.ts → src/lib/hierarchy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,17 @@ import { MemberNameToType, MemberType, MemberTypeToType } from "./types.js";
import { AnyModel } from "./model.js";
import { AnyRepository } from "./repository.js";

export interface CustomGranularityElementConfig {
export interface HierarchyElementConfig {
name: string;
dimensions: string[];
keyDimensions: string[];
formatDimensions: string[];
formatter: (row: Record<string, unknown>) => string;
}

export type AnyCustomGranularityElement = CustomGranularityElement<any, any>;
export type AnyHierarchyElement = HierarchyElement<any, any>;

export class CustomGranularityElement<
D extends MemberNameToType,
DN extends keyof D,
> {
export class HierarchyElement<D extends MemberNameToType, DN extends keyof D> {
private keys: string[] | null = null;
private formatDimensions: string[];
private formatter:
Expand Down Expand Up @@ -87,7 +84,7 @@ export class CustomGranularityElement<
}
return this.getDefaultFormatter(parent);
}
getConfig(parent: AnyModel | AnyRepository): CustomGranularityElementConfig {
getConfig(parent: AnyModel | AnyRepository): HierarchyElementConfig {
const dimensionNames = this.dimensionNames.map((dimensionName) =>
parent.getDimension(dimensionName).getPath(),
);
Expand All @@ -106,21 +103,19 @@ export class CustomGranularityElement<
}
}

export class CustomGranularityElementInit<D extends MemberNameToType> {
export class HierarchyElementInit<D extends MemberNameToType> {
constructor(public readonly name: string) {}

withDimensions<DN extends keyof D>(dimensionNames: (DN & string)[]) {
return new CustomGranularityElement<D, DN>(this.name, dimensionNames);
return new HierarchyElement<D, DN>(this.name, dimensionNames);
}
}

export function makeCustomGranularityElementInitMaker<
D extends MemberNameToType,
>() {
const fn = (name: string) => new CustomGranularityElementInit<D>(name);
export function makeHierarchyElementInitMaker<D extends MemberNameToType>() {
const fn = (name: string) => new HierarchyElementInit<D>(name);

fn.fromDimension = <DN extends keyof D>(name: DN & string) =>
new CustomGranularityElementInit<D>(name as string).withDimensions([name]);
new HierarchyElementInit<D>(name as string).withDimensions([name]);

return fn;
}
Loading

0 comments on commit 156047f

Please sign in to comment.