Skip to content

Commit 5ed14da

Browse files
feat: added support for {MEERKAT} placeholder (#59)
* added support for meerkat tableName placeholder * self review * bumped meerkat core * updated regex
1 parent 1759577 commit 5ed14da

File tree

8 files changed

+197
-4
lines changed

8 files changed

+197
-4
lines changed

meerkat-core/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@devrev/meerkat-core",
3-
"version": "0.0.63",
3+
"version": "0.0.64",
44
"dependencies": {
55
"@swc/helpers": "~0.5.0"
66
},

meerkat-core/src/cube-measure-transformer/cube-measure-transformer.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Member } from '../types/cube-types/query';
22
import { TableSchema } from '../types/cube-types/table';
3+
import { meerkatPlaceholderReplacer } from '../utils/meerkat-placeholder-replacer';
34
import { memberKeyToSafeKey } from '../utils/member-key-to-safe-key';
45

56
export const cubeMeasureToSQLSelectString = (
@@ -24,7 +25,8 @@ export const cubeMeasureToSQLSelectString = (
2425
if (i > 0) {
2526
base += ', ';
2627
}
27-
base += ` ${measureSchema.sql} AS ${aliasKey} `;
28+
const meerkatReplacedSqlString = meerkatPlaceholderReplacer(measureSchema.sql, tableSchema.name)
29+
base += ` ${meerkatReplacedSqlString} AS ${aliasKey} `;
2830
}
2931
return base
3032
};

meerkat-core/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,5 @@ export { FilterType } from './types/cube-types';
1111
export * from './types/cube-types/index';
1212
export * from './types/duckdb-serialization-types/index';
1313
export { BASE_TABLE_NAME } from './utils/base-ast';
14+
export { meerkatPlaceholderReplacer } from './utils/meerkat-placeholder-replacer';
1415
export { memberKeyToSafeKey } from './utils/member-key-to-safe-key';
15-

meerkat-core/src/utils/constants.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export const MEERKAT_OUTPUT_DELIMITER = '__';
2+
export const MEERKAT_INPUT_DELIMITER = '.';
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { meerkatPlaceholderReplacer } from './meerkat-placeholder-replacer';
2+
3+
describe("meerkatPlaceholderReplacer", () => {
4+
it("should not replace placeholders with tableName if placeholder pattern doesnt end in .", () => {
5+
const sql = "SELECT * FROM {MEERKAT}fieldName";
6+
const tableName = "customers";
7+
expect(meerkatPlaceholderReplacer(sql, tableName)).toEqual("SELECT * FROM {MEERKAT}fieldName");
8+
});
9+
10+
it("should replace multiple placeholders in the SQL query", () => {
11+
const sql = "SELECT {MEERKAT}.a, {MEERKAT}.b FROM orders";
12+
const tableName = "orders";
13+
expect(meerkatPlaceholderReplacer(sql, tableName)).toEqual('SELECT orders__a, orders__b FROM orders');
14+
});
15+
16+
it("should be case sensitive", () => {
17+
const sql = "SELECT {meerkat}.a FROM orders";
18+
const tableName = "orders";
19+
expect(meerkatPlaceholderReplacer(sql, tableName)).toEqual('SELECT {meerkat}.a FROM orders');
20+
});
21+
22+
it("should replace the correct match", () => {
23+
const sql = "SELECT {MEERKAT.{MEERKAT}.}.a FROM customers";
24+
const tableName = "customers";
25+
expect(meerkatPlaceholderReplacer(sql, tableName)).toEqual("SELECT {MEERKAT.customers__}.a FROM customers");
26+
});
27+
28+
it("should handle empty SQL queries", () => {
29+
const sql = "";
30+
const tableName = "customers";
31+
expect(meerkatPlaceholderReplacer(sql, tableName)).toEqual('');
32+
});
33+
34+
it("should handle SQL queries without placeholders", () => {
35+
const sql = "SELECT * FROM customers.";
36+
const tableName = "orders";
37+
expect(meerkatPlaceholderReplacer(sql, tableName)).toEqual('SELECT * FROM customers.');
38+
});
39+
40+
41+
});
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { MEERKAT_OUTPUT_DELIMITER } from "./constants";
2+
3+
export const meerkatPlaceholderReplacer = (sql: string, tableName: string) => {
4+
const tableNameEncapsulationRegEx =/\{MEERKAT\}\./g;
5+
return sql.replace(tableNameEncapsulationRegEx, tableName + MEERKAT_OUTPUT_DELIMITER )
6+
}
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { MEERKAT_INPUT_DELIMITER, MEERKAT_OUTPUT_DELIMITER } from "./constants";
2+
13
export const memberKeyToSafeKey = (memberKey: string) => {
2-
return memberKey.split('.').join('__');
4+
return memberKey.split(MEERKAT_INPUT_DELIMITER).join(MEERKAT_OUTPUT_DELIMITER);
35
};
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
import { cubeQueryToSQL } from "../cube-to-sql/cube-to-sql";
2+
import { duckdbExec } from "../duckdb-exec";
3+
4+
5+
const CREATE_TEST_TABLE = `CREATE TABLE orders (
6+
id VARCHAR,
7+
owned_by_id VARCHAR,
8+
stage VARCHAR,
9+
amount FLOAT
10+
);`;
11+
12+
export const INPUT_DATA_QUERY = `INSERT INTO orders VALUES
13+
('1', 'user_1', 'stage_a', 10),
14+
(2, 'user_1', 'stage_a', 20),
15+
(3, 'user_2', 'stage_a', 90),
16+
(4, 'user_3', 'stage_a', 50),
17+
(5, 'user_1', 'stage_a', 30),
18+
(6, 'user_2', 'stage_a', 100),
19+
(7, 'user_3', 'stage_b', 9000),
20+
(8, 'user_1', 'stage_b', 5),
21+
(9, 'user_2', 'stage_b', 8),
22+
(10, 'user_3', 'stage_a', 60),
23+
(11, 'user_1', 'stage_c', 80);
24+
`;
25+
26+
describe('meerkat placeholder', () => {
27+
beforeAll(async () => {
28+
//Create test table
29+
await duckdbExec(CREATE_TEST_TABLE);
30+
//Insert test data
31+
await duckdbExec(INPUT_DATA_QUERY);
32+
//Get SQL from cube query
33+
});
34+
it ('should resolve query correctly', async () => {
35+
const query = {
36+
"dimensions": [
37+
"orders.owned_by_id",
38+
"orders.stage"
39+
],
40+
"measures": [
41+
"orders.sum_amount",
42+
"orders.total_sum_amount"
43+
],
44+
"order": {
45+
"orders__total_sum_amount": "desc"
46+
},
47+
"timeDimensions": [],
48+
"type": "sql",
49+
}
50+
51+
const tableSchema = {
52+
"dimensions": [
53+
{
54+
"name": "id",
55+
"sql": "id",
56+
"type": "string"
57+
},
58+
{
59+
"name": "owned_by_id",
60+
"sql": "owned_by_id",
61+
"type": "string"
62+
},
63+
{
64+
"name": "stage",
65+
"sql": "stage",
66+
"type": "string"
67+
},
68+
],
69+
"measures": [
70+
{
71+
"name": "sum_amount",
72+
"sql": "SUM(amount)",
73+
"type": "number"
74+
},
75+
{
76+
"name": "total_sum_amount",
77+
"sql": "SUM(SUM(amount)) OVER (PARTITION BY {MEERKAT}.owned_by_id)",
78+
"type": "number"
79+
}
80+
],
81+
"name": "orders",
82+
"sql": `SELECT * FROM orders`
83+
}
84+
85+
const baseQuery = `SELECT * FROM orders`
86+
const baseOutput = await duckdbExec(baseQuery);
87+
console.log({baseOutput})
88+
89+
const sql = await cubeQueryToSQL(query, tableSchema);
90+
console.info(`SQL for Simple Cube Query: `, sql);
91+
expect(sql).toEqual(
92+
`SELECT SUM(amount) AS orders__sum_amount , SUM(SUM(amount)) OVER (PARTITION BY orders__owned_by_id) AS orders__total_sum_amount , orders__owned_by_id, orders__stage FROM (SELECT *, owned_by_id AS orders__owned_by_id, stage AS orders__stage FROM (SELECT * FROM orders) AS orders) AS orders GROUP BY orders__owned_by_id, orders__stage ORDER BY orders__total_sum_amount DESC`
93+
);
94+
const output = await duckdbExec(sql);
95+
expect(output).toEqual([
96+
{
97+
orders__sum_amount: 110,
98+
orders__total_sum_amount: 9110,
99+
orders__owned_by_id: 'user_3',
100+
orders__stage: 'stage_a'
101+
},
102+
{
103+
orders__sum_amount: 9000,
104+
orders__total_sum_amount: 9110,
105+
orders__owned_by_id: 'user_3',
106+
orders__stage: 'stage_b'
107+
},
108+
{
109+
orders__sum_amount: 190,
110+
orders__total_sum_amount: 198,
111+
orders__owned_by_id: 'user_2',
112+
orders__stage: 'stage_a'
113+
},
114+
{
115+
orders__sum_amount: 8,
116+
orders__total_sum_amount: 198,
117+
orders__owned_by_id: 'user_2',
118+
orders__stage: 'stage_b'
119+
},
120+
{
121+
orders__sum_amount: 60,
122+
orders__total_sum_amount: 145,
123+
orders__owned_by_id: 'user_1',
124+
orders__stage: 'stage_a'
125+
},
126+
{
127+
orders__sum_amount: 5,
128+
orders__total_sum_amount: 145,
129+
orders__owned_by_id: 'user_1',
130+
orders__stage: 'stage_b'
131+
},
132+
{
133+
orders__sum_amount: 80,
134+
orders__total_sum_amount: 145,
135+
orders__owned_by_id: 'user_1',
136+
orders__stage: 'stage_c'
137+
}
138+
])
139+
})
140+
})

0 commit comments

Comments
 (0)