From 8d73ce03cb4aae4a0e0da1952cd88e72117964c5 Mon Sep 17 00:00:00 2001 From: Pranjal Bhagat Date: Mon, 17 Feb 2025 01:09:23 +0530 Subject: [PATCH 1/4] if measure is * select all the tables in joinPath --- .../get-used-table-schema.spec.ts | 355 +++++++++++++++ .../get-used-table-schema.ts | 100 +++++ meerkat-core/src/joins/joins.spec.ts | 419 +++++++++++++++++- meerkat-core/src/joins/joins.ts | 21 +- 4 files changed, 879 insertions(+), 16 deletions(-) create mode 100644 meerkat-core/src/get-used-table-schema/get-used-table-schema.spec.ts create mode 100644 meerkat-core/src/get-used-table-schema/get-used-table-schema.ts diff --git a/meerkat-core/src/get-used-table-schema/get-used-table-schema.spec.ts b/meerkat-core/src/get-used-table-schema/get-used-table-schema.spec.ts new file mode 100644 index 00000000..ff17003b --- /dev/null +++ b/meerkat-core/src/get-used-table-schema/get-used-table-schema.spec.ts @@ -0,0 +1,355 @@ +import { Query, TableSchema } from '../types/cube-types'; +import { getUsedTableSchema } from './get-used-table-schema'; + +describe('getUsedTableSchema', () => { + const sampleTableSchema: TableSchema[] = [ + { + name: 'table1', + sql: 'SELECT * FROM table1', + measures: [ + { + name: 'measure1', + sql: 'SUM(value1)', + type: 'string', + }, + ], + dimensions: [ + { + name: 'dimension1', + sql: 'dim1', + type: 'string', + }, + ], + joins: [], + }, + { + name: 'table2', + sql: 'SELECT * FROM table2', + measures: [ + { + name: 'measure2', + sql: 'COUNT(*)', + type: 'string', + }, + ], + dimensions: [ + { + name: 'dimension2', + sql: 'dim2', + type: 'string', + }, + ], + joins: [], + }, + { + name: 'table3', + sql: 'SELECT * FROM table3', + measures: [ + { + name: 'measure3', + sql: 'AVG(value3)', + type: 'string', + }, + ], + dimensions: [ + { + name: 'dimension3', + sql: 'dim3', + type: 'string', + }, + ], + joins: [], + }, + { + name: 'table4', + sql: 'SELECT * FROM table4', + measures: [ + { + name: 'measure4', + sql: 'SUM(value4)', + type: 'string', + }, + ], + dimensions: [ + { + name: 'dimension1', + sql: 'dim4', + type: 'string', + }, + ], + joins: [], + }, + { + name: 'table5', + sql: 'SELECT * FROM table5', + measures: [ + { + name: 'measure5', + sql: 'COUNT(*)', + type: 'string', + }, + ], + dimensions: [ + { + name: 'dimension2', + sql: 'dim5', + type: 'string', + }, + ], + joins: [], + }, + ]; + + it('should filter tables based on simple filter', () => { + const query: Query = { + filters: [ + { + member: 'table1.measure1', + operator: 'equals', + values: ['100'], + }, + ], + measures: [], + }; + const result = getUsedTableSchema(sampleTableSchema, query); + expect(result).toHaveLength(1); + expect(result[0].name).toBe('table1'); + }); + + it('should filter tables based on AND conditions', () => { + const query: Query = { + filters: [ + { + and: [ + { + member: 'table1.measure1', + operator: 'equals', + values: ['100'], + }, + { + member: 'table2.dimension2', + operator: 'equals', + values: ['value'], + }, + ], + }, + ], + measures: [], + }; + const result = getUsedTableSchema(sampleTableSchema, query); + expect(result).toHaveLength(2); + expect(result.map((schema) => schema.name).sort()).toEqual([ + 'table1', + 'table2', + ]); + }); + + it('should filter tables based on OR conditions', () => { + const query: Query = { + filters: [ + { + or: [ + { + member: 'table1.measure1', + operator: 'equals', + values: ['100'], + }, + { + member: 'table3.dimension3', + operator: 'equals', + values: ['value'], + }, + ], + }, + ], + measures: [], + }; + const result = getUsedTableSchema(sampleTableSchema, query); + expect(result).toHaveLength(2); + expect(result.map((schema) => schema.name).sort()).toEqual([ + 'table1', + 'table3', + ]); + }); + + it('should handle nested AND-OR conditions', () => { + const query: Query = { + filters: [ + { + and: [ + { + member: 'table1.measure1', + operator: 'equals', + values: ['100'], + }, + { + or: [ + { + member: 'table2.dimension2', + operator: 'equals', + values: ['value1'], + }, + { + member: 'table3.dimension3', + operator: 'equals', + values: ['value2'], + }, + ], + }, + ], + }, + ], + measures: [], + }; + const result = getUsedTableSchema(sampleTableSchema, query); + expect(result).toHaveLength(3); + expect(result.map((schema) => schema.name).sort()).toEqual([ + 'table1', + 'table2', + 'table3', + ]); + }); + + it('should handle multiple top-level filters', () => { + const query: Query = { + filters: [ + { + member: 'table1.measure1', + operator: 'equals', + values: ['100'], + }, + { + member: 'table2.measure2', + operator: 'equals', + values: ['200'], + }, + ], + measures: [], + }; + const result = getUsedTableSchema(sampleTableSchema, query); + expect(result).toHaveLength(2); + expect(result.map((schema) => schema.name).sort()).toEqual([ + 'table1', + 'table2', + ]); + }); + + it('should handle complex nested filters', () => { + const query: Query = { + filters: [ + { + and: [ + { + or: [ + { + member: 'table1.measure1', + operator: 'equals', + values: ['100'], + }, + { + member: 'table2.measure2', + operator: 'equals', + values: ['200'], + }, + ], + }, + { + member: 'table3.measure3', + operator: 'equals', + values: ['300'], + }, + ], + }, + ], + measures: [], + }; + const result = getUsedTableSchema(sampleTableSchema, query); + expect(result).toHaveLength(3); + expect(result.map((schema) => schema.name).sort()).toEqual([ + 'table1', + 'table2', + 'table3', + ]); + }); + + it('should filter tables based on measures', () => { + const query: Query = { + measures: ['table1.measure1', 'table2.measure2'], + }; + const result = getUsedTableSchema(sampleTableSchema, query); + expect(result).toHaveLength(2); + expect(result.map((schema) => schema.name).sort()).toEqual([ + 'table1', + 'table2', + ]); + }); + + it('should filter tables based on dimensions', () => { + const query: Query = { + measures: [], + dimensions: ['table1.dimension1', 'table3.dimension3'], + }; + const result = getUsedTableSchema(sampleTableSchema, query); + expect(result).toHaveLength(2); + expect(result.map((schema) => schema.name).sort()).toEqual([ + 'table1', + 'table3', + ]); + }); + + it('should filter tables based on order', () => { + const query: Query = { + measures: [], + order: { + 'table1.dimension1': 'asc', + 'table2.dimension2': 'desc', + }, + }; + const result = getUsedTableSchema(sampleTableSchema, query); + expect(result).toHaveLength(2); + expect(result.map((schema) => schema.name).sort()).toEqual([ + 'table1', + 'table2', + ]); + }); + + it('should filter tables based on joinPaths', () => { + const query: Query = { + measures: [], + joinPaths: [ + [ + { left: 'table1.dimension1', right: 'table2.dimension2', on: 'id' }, + { left: 'table2.dimension2', right: 'table3.dimension3', on: 'id' }, + ], + ], + }; + const result = getUsedTableSchema(sampleTableSchema, query); + expect(result).toHaveLength(3); + expect(result.map((schema) => schema.name).sort()).toEqual([ + 'table1', + 'table2', + 'table3', + ]); + }); + it('should filter tables based on a combination of measures, dimensions, order, and unique joinPaths', () => { + const query: Query = { + measures: ['table1.measure1'], + dimensions: ['table2.dimension2'], + order: { + 'table3.dimension3': 'asc', + }, + joinPaths: [ + [{ left: 'table4.dimension1', right: 'table5.dimension2', on: 'id' }], + ], + }; + const result = getUsedTableSchema(sampleTableSchema, query); + expect(result).toHaveLength(5); + expect(result.map((schema) => schema.name).sort()).toEqual([ + 'table1', + 'table2', + 'table3', + 'table4', + 'table5', + ]); + }); +}); diff --git a/meerkat-core/src/get-used-table-schema/get-used-table-schema.ts b/meerkat-core/src/get-used-table-schema/get-used-table-schema.ts new file mode 100644 index 00000000..e97bcc53 --- /dev/null +++ b/meerkat-core/src/get-used-table-schema/get-used-table-schema.ts @@ -0,0 +1,100 @@ +import { MeerkatQueryFilter, Query, TableSchema } from '../types/cube-types'; + +export const getUsedTableSchema = ( + tableSchema: TableSchema[], + cubeQuery: Query +): TableSchema[] => { + const getTableFromMember = (member: string): string => { + return member.split('.')[0]; + }; + + const getTablesFromFilter = (filter: MeerkatQueryFilter): Set => { + const tables = new Set(); + if ('and' in filter) { + filter.and.forEach((filtr) => { + if ('or' in filtr) { + filtr.or.forEach((orFilter) => { + const orTables = getTablesFromFilter(orFilter); + orTables.forEach((table) => tables.add(table)); + }); + } else { + tables.add(getTableFromMember(filtr.member)); + } + }); + } else if ('or' in filter) { + filter.or.forEach((filtr) => { + const orTables = getTablesFromFilter(filtr); + orTables.forEach((table) => tables.add(table)); + }); + } else { + tables.add(getTableFromMember(filter.member)); + } + return tables; + }; + + // Get all tables mentioned in filters + const usedTables = new Set(); + + // Add tables from filters + if (cubeQuery.filters && cubeQuery.filters.length > 0) { + cubeQuery.filters.forEach((filter) => { + const filterTables = getTablesFromFilter(filter); + filterTables.forEach((table) => usedTables.add(table)); + }); + } + + // Add tables from measures + if (cubeQuery.measures && cubeQuery.measures.length > 0) { + if (cubeQuery.measures.includes('*')) { + // If measures includes '*', include all tables from join paths + if (cubeQuery.joinPaths) { + cubeQuery.joinPaths.forEach((joinPath) => { + joinPath.forEach((node) => { + usedTables.add(node.left); + if ('right' in node) { + usedTables.add(node.right); + } + }); + }); + } + } else { + cubeQuery.measures.forEach((measure) => { + usedTables.add(getTableFromMember(measure)); + }); + } + } + + // Add tables from dimensions + if (cubeQuery.dimensions && cubeQuery.dimensions.length > 0) { + cubeQuery.dimensions.forEach((dimension) => { + usedTables.add(getTableFromMember(dimension)); + }); + } + + // Add tables from order + if (cubeQuery.order) { + Object.keys(cubeQuery.order).forEach((orderKey) => { + usedTables.add(getTableFromMember(orderKey)); + }); + } + + // Add tables from joinPaths + if (cubeQuery.joinPaths && cubeQuery.joinPaths.length > 0) { + cubeQuery.joinPaths.forEach((joinPath) => { + joinPath.forEach((node) => { + // Extract table name from the left side + usedTables.add(getTableFromMember(node.left)); + // If it's a join node with a right side, add that table too + if ('right' in node) { + usedTables.add(getTableFromMember(node.right)); + } + }); + }); + } + + // Filter table schema to only include used tables + const filteredSchema = tableSchema.filter((schema) => + usedTables.has(schema.name) + ); + return filteredSchema; +}; diff --git a/meerkat-core/src/joins/joins.spec.ts b/meerkat-core/src/joins/joins.spec.ts index b68231de..b171cff1 100644 --- a/meerkat-core/src/joins/joins.spec.ts +++ b/meerkat-core/src/joins/joins.spec.ts @@ -1,7 +1,10 @@ +import { getUsedTableSchema } from '../get-used-table-schema/get-used-table-schema'; +import { TableSchema } from '../types/cube-types'; import { checkLoopInJoinPath, createDirectedGraph, generateSqlQuery, + getCombinedTableSchema, } from './joins'; describe('Table schema functions', () => { @@ -96,7 +99,7 @@ describe('Table schema functions', () => { ], ]; expect(checkLoopInJoinPath(joinPath)).toBe(false); - }) + }); it('should return true if there is a loop in the join path', () => { const joinPath = [ [ @@ -106,15 +109,413 @@ describe('Table schema functions', () => { ], ]; expect(checkLoopInJoinPath(joinPath)).toBe(true); - }) + }); it('should return false for single node', () => { - const joinPath = [ - [ - { left: 'table1', }, - { left: 'table1' }, + const joinPath = [[{ left: 'table1' }, { left: 'table1' }]]; + expect(checkLoopInJoinPath(joinPath)).toBe(false); + }); + }); + describe('getCombinedTableSchema', () => { + it('should return single table schema when only one table is provided', async () => { + const tableSchema = [ + { + name: 'orders', + sql: 'select * from orders', + measures: [ + { name: 'total_amount', sql: 'SUM(amount)' }, + { name: 'order_count', sql: 'COUNT(*)' }, + ], + dimensions: [ + { name: 'order_date', sql: 'created_at' }, + { name: 'status', sql: 'order_status' }, + ], + joins: [], + }, + ]; + const cubeQuery = { + joinPaths: [], + }; + const result = await getCombinedTableSchema(tableSchema, cubeQuery); + expect(result).toEqual(tableSchema[0]); + }); + + it('should combine multiple table schemas correctly', async () => { + const tableSchema = [ + { + name: 'orders', + sql: 'select * from orders', + measures: [ + { name: 'total_amount', sql: 'SUM(amount)' }, + { name: 'order_count', sql: 'COUNT(*)' }, + { name: 'avg_order_value', sql: 'AVG(amount)' }, + ], + dimensions: [ + { name: 'order_date', sql: 'created_at' }, + { name: 'status', sql: 'order_status' }, + { name: 'payment_method', sql: 'payment_type' }, + ], + joins: [{ sql: 'orders.customer_id = customers.id' }], + }, + { + name: 'customers', + sql: 'select * from customers', + measures: [ + { name: 'customer_count', sql: 'COUNT(DISTINCT id)' }, + { name: 'total_customers', sql: 'COUNT(*)' }, + { name: 'avg_customer_age', sql: 'AVG(age)' }, + ], + dimensions: [ + { name: 'join_date', sql: 'created_at' }, + { name: 'customer_type', sql: 'type' }, + { name: 'region', sql: 'region' }, + ], + joins: [], + }, + ]; + const cubeQuery = { + joinPaths: [ + [ + { + left: 'orders', + right: 'customers', + on: 'customer_id', + }, + ], + ], + }; + const result = await getCombinedTableSchema(tableSchema, cubeQuery); + expect(result).toEqual({ + name: 'MEERKAT_GENERATED_TABLE', + sql: 'select * from orders LEFT JOIN (select * from customers) AS customers ON orders.customer_id = customers.id', + measures: [ + { name: 'total_amount', sql: 'SUM(amount)' }, + { name: 'order_count', sql: 'COUNT(*)' }, + { name: 'avg_order_value', sql: 'AVG(amount)' }, + { name: 'customer_count', sql: 'COUNT(DISTINCT id)' }, + { name: 'total_customers', sql: 'COUNT(*)' }, + { name: 'avg_customer_age', sql: 'AVG(age)' }, ], + dimensions: [ + { name: 'order_date', sql: 'created_at' }, + { name: 'status', sql: 'order_status' }, + { name: 'payment_method', sql: 'payment_type' }, + { name: 'join_date', sql: 'created_at' }, + { name: 'customer_type', sql: 'type' }, + { name: 'region', sql: 'region' }, + ], + joins: [], + }); + }); + + it('should throw error when loop is detected in join paths', async () => { + const tableSchema = [ + { + name: 'orders', + sql: 'select * from orders', + measures: [{ name: 'total_amount', sql: 'SUM(amount)' }], + dimensions: [{ name: 'order_id', sql: 'id' }], + joins: [{ sql: 'orders.customer_id = customers.id' }], + }, + { + name: 'customers', + sql: 'select * from customers', + measures: [{ name: 'customer_count', sql: 'COUNT(*)' }], + dimensions: [{ name: 'customer_id', sql: 'id' }], + joins: [{ sql: 'customers.order_id = orders.id' }], + }, ]; - expect(checkLoopInJoinPath(joinPath)).toBe(false); - }) - }) + const cubeQuery = { + joinPaths: [ + [ + { left: 'orders', right: 'customers', on: 'id' }, + { left: 'customers', right: 'orders', on: 'id' }, + ], + ], + }; + await expect( + getCombinedTableSchema(tableSchema, cubeQuery) + ).rejects.toThrow(/A loop was detected in the joins/); + }); + + it('should handle empty measures and dimensions', async () => { + const tableSchema = [ + { + name: 'table1', + sql: 'select * from table1', + measures: [], + dimensions: [], + joins: [{ sql: 'table1.id = table2.id' }], + }, + { + name: 'table2', + sql: 'select * from table2', + measures: [], + dimensions: [], + joins: [], + }, + ]; + const cubeQuery = { + joinPaths: [[{ left: 'table1', right: 'table2', on: 'id' }]], + }; + const result = await getCombinedTableSchema(tableSchema, cubeQuery); + expect(result).toEqual({ + name: 'MEERKAT_GENERATED_TABLE', + sql: 'select * from table1 LEFT JOIN (select * from table2) AS table2 ON table1.id = table2.id', + measures: [], + dimensions: [], + joins: [], + }); + }); + + it('should filter table schema based on filters, measures, and dimensions', () => { + const tableSchema: TableSchema[] = [ + { + name: 'products', + sql: 'SELECT * FROM products', + measures: [ + { + name: 'total_sales', + sql: 'SUM(sales)', + type: 'string', + }, + { + name: 'inventory_count', + sql: 'SUM(stock)', + type: 'string', + }, + ], + dimensions: [ + { + name: 'category', + sql: 'category', + type: 'string', + }, + { + name: 'brand', + sql: 'brand_name', + type: 'string', + }, + ], + joins: [], + }, + { + name: 'sales', + sql: 'SELECT * FROM sales', + measures: [ + { + name: 'revenue', + sql: 'SUM(amount)', + type: 'string', + }, + { + name: 'transaction_count', + sql: 'COUNT(*)', + type: 'string', + }, + ], + dimensions: [ + { + name: 'sale_date', + sql: 'created_at', + type: 'string', + }, + { + name: 'store_location', + sql: 'location', + type: 'string', + }, + ], + joins: [], + }, + { + name: 'inventory', + sql: 'SELECT * FROM inventory', + measures: [ + { + name: 'stock_value', + sql: 'SUM(value)', + type: 'string', + }, + { + name: 'reorder_count', + sql: 'COUNT(reorder_flag)', + type: 'string', + }, + ], + dimensions: [ + { + name: 'warehouse', + sql: 'warehouse_id', + type: 'string', + }, + { + name: 'stock_status', + sql: 'status', + type: 'string', + }, + ], + joins: [], + }, + ]; + const cubeQuery: Query = { + measures: ['products.total_sales'], + dimensions: ['sales.store_location'], + filters: [ + { + member: 'inventory.stock_status', + operator: 'equals', + values: ['LOW'], + }, + ], + }; + const result = getUsedTableSchema(tableSchema, cubeQuery); + expect(result).toEqual(tableSchema); + }); + + it('should filter table schema based on filters, measures, dimensions, order, and joinPaths', () => { + const tableSchema: TableSchema[] = [ + { + name: 'users', + sql: 'SELECT * FROM users', + measures: [ + { + name: 'user_count', + sql: 'COUNT(DISTINCT id)', + type: 'string', + }, + { + name: 'active_users', + sql: 'SUM(is_active)', + type: 'string', + }, + ], + dimensions: [ + { + name: 'age_group', + sql: 'age_bracket', + type: 'string', + }, + { + name: 'user_type', + sql: 'type', + type: 'string', + }, + ], + joins: [], + }, + { + name: 'sessions', + sql: 'SELECT * FROM sessions', + measures: [ + { + name: 'session_duration', + sql: 'AVG(duration)', + type: 'string', + }, + { + name: 'session_count', + sql: 'COUNT(*)', + type: 'string', + }, + ], + dimensions: [ + { + name: 'device_type', + sql: 'device', + type: 'string', + }, + { + name: 'platform', + sql: 'os', + type: 'string', + }, + ], + joins: [], + }, + { + name: 'events', + sql: 'SELECT * FROM events', + measures: [ + { + name: 'event_count', + sql: 'COUNT(*)', + type: 'string', + }, + { + name: 'unique_events', + sql: 'COUNT(DISTINCT event_type)', + type: 'string', + }, + ], + dimensions: [ + { + name: 'event_category', + sql: 'category', + type: 'string', + }, + { + name: 'event_source', + sql: 'source', + type: 'string', + }, + ], + joins: [], + }, + { + name: 'conversions', + sql: 'SELECT * FROM conversions', + measures: [ + { + name: 'conversion_rate', + sql: 'AVG(is_converted)', + type: 'string', + }, + { + name: 'total_value', + sql: 'SUM(value)', + type: 'string', + }, + ], + dimensions: [ + { + name: 'conversion_type', + sql: 'type', + type: 'string', + }, + { + name: 'funnel_stage', + sql: 'stage', + type: 'string', + }, + ], + joins: [], + }, + ]; + const cubeQuery: Query = { + measures: ['users.user_count'], + dimensions: ['sessions.device_type'], + filters: [ + { + member: 'events.event_category', + operator: 'equals', + values: ['purchase'], + }, + ], + order: { + 'conversions.conversion_rate': 'asc', + }, + joinPaths: [ + [ + { + left: 'users', + right: 'sessions', + on: 'user_id', + }, + ], + ], + }; + const result = getUsedTableSchema(tableSchema, cubeQuery); + expect(result).toEqual(tableSchema); + }); + }); }); diff --git a/meerkat-core/src/joins/joins.ts b/meerkat-core/src/joins/joins.ts index a8f9e9be..67980822 100644 --- a/meerkat-core/src/joins/joins.ts +++ b/meerkat-core/src/joins/joins.ts @@ -1,3 +1,4 @@ +import { getUsedTableSchema } from '../get-used-table-schema/get-used-table-schema'; import { JoinPath, Query, TableSchema, isJoinNode } from '../types/cube-types'; export type Graph = { @@ -57,9 +58,11 @@ export function generateSqlQuery( // If visitedFrom is undefined, this is the first visit to the node visitedNodes.set(currentEdge.right, currentEdge); - query += ` LEFT JOIN (${tableSchemaSqlMap[currentEdge.right]}) AS ${currentEdge.right - } ON ${directedGraph[currentEdge.left][currentEdge.right][currentEdge.on] - }`; + query += ` LEFT JOIN (${tableSchemaSqlMap[currentEdge.right]}) AS ${ + currentEdge.right + } ON ${ + directedGraph[currentEdge.left][currentEdge.right][currentEdge.on] + }`; } } @@ -150,7 +153,6 @@ export const createDirectedGraph = ( return directedGraph; }; - export const checkLoopInJoinPath = (joinPath: JoinPath[]) => { for (let i = 0; i < joinPath.length; i++) { const visitedNodes = new Set(); @@ -166,8 +168,8 @@ export const checkLoopInJoinPath = (joinPath: JoinPath[]) => { } } } - return false -} + return false; +}; export const getCombinedTableSchema = async ( tableSchema: TableSchema[], @@ -176,6 +178,7 @@ export const getCombinedTableSchema = async ( if (tableSchema.length === 1) { return tableSchema[0]; } + tableSchema = getUsedTableSchema(tableSchema, cubeQuery); const tableSchemaSqlMap = tableSchema.reduce( (acc: { [key: string]: string }, schema: TableSchema) => { @@ -187,7 +190,11 @@ export const getCombinedTableSchema = async ( const directedGraph = createDirectedGraph(tableSchema, tableSchemaSqlMap); const hasLoop = checkLoopInJoinPath(cubeQuery.joinPaths || []); if (hasLoop) { - throw new Error(`A loop was detected in the joins. ${JSON.stringify(cubeQuery.joinPaths || [])}`); + throw new Error( + `A loop was detected in the joins. ${JSON.stringify( + cubeQuery.joinPaths || [] + )}` + ); } const baseSql = generateSqlQuery( From f7eb71fcec261c78f141c995737347ab7f79fb8a Mon Sep 17 00:00:00 2001 From: Pranjal Bhagat Date: Thu, 20 Feb 2025 00:36:08 +0530 Subject: [PATCH 2/4] getusedtable schema corrected --- .../get-used-table-schema.ts | 36 +++++++------------ meerkat-core/src/joins/joins.spec.ts | 3 +- 2 files changed, 15 insertions(+), 24 deletions(-) diff --git a/meerkat-core/src/get-used-table-schema/get-used-table-schema.ts b/meerkat-core/src/get-used-table-schema/get-used-table-schema.ts index e97bcc53..8692702f 100644 --- a/meerkat-core/src/get-used-table-schema/get-used-table-schema.ts +++ b/meerkat-core/src/get-used-table-schema/get-used-table-schema.ts @@ -1,11 +1,17 @@ -import { MeerkatQueryFilter, Query, TableSchema } from '../types/cube-types'; +import { + isJoinNode, + MeerkatQueryFilter, + Member, + Query, + TableSchema, +} from '../types/cube-types'; export const getUsedTableSchema = ( tableSchema: TableSchema[], cubeQuery: Query ): TableSchema[] => { - const getTableFromMember = (member: string): string => { - return member.split('.')[0]; + const getTableFromMember = (member: Member): string => { + return member.toString().split('.')[0]; }; const getTablesFromFilter = (filter: MeerkatQueryFilter): Set => { @@ -45,23 +51,9 @@ export const getUsedTableSchema = ( // Add tables from measures if (cubeQuery.measures && cubeQuery.measures.length > 0) { - if (cubeQuery.measures.includes('*')) { - // If measures includes '*', include all tables from join paths - if (cubeQuery.joinPaths) { - cubeQuery.joinPaths.forEach((joinPath) => { - joinPath.forEach((node) => { - usedTables.add(node.left); - if ('right' in node) { - usedTables.add(node.right); - } - }); - }); - } - } else { - cubeQuery.measures.forEach((measure) => { - usedTables.add(getTableFromMember(measure)); - }); - } + cubeQuery.measures.forEach((measure) => { + usedTables.add(getTableFromMember(measure)); + }); } // Add tables from dimensions @@ -82,10 +74,8 @@ export const getUsedTableSchema = ( if (cubeQuery.joinPaths && cubeQuery.joinPaths.length > 0) { cubeQuery.joinPaths.forEach((joinPath) => { joinPath.forEach((node) => { - // Extract table name from the left side usedTables.add(getTableFromMember(node.left)); - // If it's a join node with a right side, add that table too - if ('right' in node) { + if (isJoinNode(node)) { usedTables.add(getTableFromMember(node.right)); } }); diff --git a/meerkat-core/src/joins/joins.spec.ts b/meerkat-core/src/joins/joins.spec.ts index b171cff1..e6c99df5 100644 --- a/meerkat-core/src/joins/joins.spec.ts +++ b/meerkat-core/src/joins/joins.spec.ts @@ -1,5 +1,5 @@ import { getUsedTableSchema } from '../get-used-table-schema/get-used-table-schema'; -import { TableSchema } from '../types/cube-types'; +import { Query, TableSchema } from '../types/cube-types'; import { checkLoopInJoinPath, createDirectedGraph, @@ -115,6 +115,7 @@ describe('Table schema functions', () => { expect(checkLoopInJoinPath(joinPath)).toBe(false); }); }); + describe('getCombinedTableSchema', () => { it('should return single table schema when only one table is provided', async () => { const tableSchema = [ From bc9ef81570bb22f0bd5669b3c78b841ae7434e38 Mon Sep 17 00:00:00 2001 From: Pranjal Bhagat Date: Fri, 21 Feb 2025 12:42:49 +0530 Subject: [PATCH 3/4] made tableschema handling in another variable --- meerkat-core/src/joins/joins.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/meerkat-core/src/joins/joins.ts b/meerkat-core/src/joins/joins.ts index 67980822..7304800a 100644 --- a/meerkat-core/src/joins/joins.ts +++ b/meerkat-core/src/joins/joins.ts @@ -178,16 +178,19 @@ export const getCombinedTableSchema = async ( if (tableSchema.length === 1) { return tableSchema[0]; } - tableSchema = getUsedTableSchema(tableSchema, cubeQuery); + const newTableSchema: TableSchema[] = getUsedTableSchema( + tableSchema, + cubeQuery + ); - const tableSchemaSqlMap = tableSchema.reduce( + const tableSchemaSqlMap = newTableSchema.reduce( (acc: { [key: string]: string }, schema: TableSchema) => { return { ...acc, [schema.name]: schema.sql }; }, {} ); - const directedGraph = createDirectedGraph(tableSchema, tableSchemaSqlMap); + const directedGraph = createDirectedGraph(newTableSchema, tableSchemaSqlMap); const hasLoop = checkLoopInJoinPath(cubeQuery.joinPaths || []); if (hasLoop) { throw new Error( @@ -203,7 +206,7 @@ export const getCombinedTableSchema = async ( directedGraph ); - const combinedTableSchema = tableSchema.reduce( + const combinedTableSchema = newTableSchema.reduce( (acc: TableSchema, schema: TableSchema) => { return { name: 'MEERKAT_GENERATED_TABLE', From 0f04bf4a3547a01f80ed241a92c7067162c2fde1 Mon Sep 17 00:00:00 2001 From: Pranjal Bhagat Date: Fri, 21 Feb 2025 13:21:51 +0530 Subject: [PATCH 4/4] package version updated --- meerkat-browser/package.json | 2 +- meerkat-core/package.json | 2 +- meerkat-node/package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/meerkat-browser/package.json b/meerkat-browser/package.json index 81633645..e36c4677 100644 --- a/meerkat-browser/package.json +++ b/meerkat-browser/package.json @@ -1,6 +1,6 @@ { "name": "@devrev/meerkat-browser", - "version": "0.0.90", + "version": "0.0.91", "dependencies": { "@swc/helpers": "~0.5.0", "@devrev/meerkat-core": "*", diff --git a/meerkat-core/package.json b/meerkat-core/package.json index d95d265f..3523e954 100644 --- a/meerkat-core/package.json +++ b/meerkat-core/package.json @@ -1,6 +1,6 @@ { "name": "@devrev/meerkat-core", - "version": "0.0.90", + "version": "0.0.91", "dependencies": { "@swc/helpers": "~0.5.0" }, diff --git a/meerkat-node/package.json b/meerkat-node/package.json index a52e90c7..dfab0a6e 100644 --- a/meerkat-node/package.json +++ b/meerkat-node/package.json @@ -1,6 +1,6 @@ { "name": "@devrev/meerkat-node", - "version": "0.0.90", + "version": "0.0.91", "dependencies": { "@swc/helpers": "~0.5.0", "@devrev/meerkat-core": "*",