@tanstack/optimistic
Version:
Core optimistic updates library
1 lines • 6.32 kB
Source Map (JSON)
{"version":3,"file":"group-by.cjs","sources":["../../../src/query/group-by.ts"],"sourcesContent":["import { groupBy, groupByOperators, map } from \"@electric-sql/d2ts\"\nimport {\n evaluateOperandOnNestedRow,\n extractValueFromNestedRow,\n} from \"./extractors\"\nimport { isAggregateFunctionCall } from \"./utils\"\nimport type { ConditionOperand, FunctionCall, Query } from \"./schema\"\nimport type { IStreamBuilder } from \"@electric-sql/d2ts\"\n\nconst { sum, count, avg, min, max, median, mode } = groupByOperators\n\n/**\n * Process the groupBy clause in a D2QL query\n */\nexport function processGroupBy(\n pipeline: IStreamBuilder<Record<string, unknown>>,\n query: Query,\n mainTableAlias: string\n) {\n // Normalize groupBy to an array of column references\n const groupByColumns = Array.isArray(query.groupBy)\n ? query.groupBy\n : [query.groupBy]\n\n // Create a key extractor function for the groupBy operator\n const keyExtractor = (nestedRow: Record<string, unknown>) => {\n const key: Record<string, unknown> = {}\n\n // Extract each groupBy column value\n for (const column of groupByColumns) {\n if (typeof column === `string` && (column as string).startsWith(`@`)) {\n const columnRef = (column as string).substring(1)\n const columnName = columnRef.includes(`.`)\n ? columnRef.split(`.`)[1]\n : columnRef\n\n key[columnName!] = extractValueFromNestedRow(\n nestedRow,\n columnRef,\n mainTableAlias\n )\n }\n }\n\n return key\n }\n\n // Create aggregate functions for any aggregated columns in the SELECT clause\n const aggregates: Record<string, any> = {}\n\n // Scan the SELECT clause for aggregate functions\n for (const item of query.select) {\n if (typeof item === `object`) {\n for (const [alias, expr] of Object.entries(item)) {\n if (typeof expr === `object` && isAggregateFunctionCall(expr)) {\n // Get the function name (the only key in the object)\n const functionName = Object.keys(expr)[0]\n // Get the column reference or expression to aggregate\n const columnRef = (expr as FunctionCall)[\n functionName as keyof FunctionCall\n ]\n\n // Add the aggregate function to our aggregates object\n aggregates[alias] = getAggregateFunction(\n functionName!,\n columnRef,\n mainTableAlias\n )\n }\n }\n }\n }\n\n // Apply the groupBy operator if we have any aggregates\n if (Object.keys(aggregates).length > 0) {\n pipeline = pipeline.pipe(\n groupBy(keyExtractor, aggregates),\n // Convert KeyValue<string, ResultType> to Record<string, unknown>\n map(([_key, value]) => {\n // After groupBy, the value already contains both the key fields and aggregate results\n // We need to return it as is, not wrapped in a nested structure\n return value as Record<string, unknown>\n })\n )\n }\n\n return pipeline\n}\n\n/**\n * Helper function to get an aggregate function based on the function name\n */\nexport function getAggregateFunction(\n functionName: string,\n columnRef: string | ConditionOperand,\n mainTableAlias: string\n) {\n // Create a value extractor function for the column to aggregate\n const valueExtractor = (nestedRow: Record<string, unknown>) => {\n let value: unknown\n if (typeof columnRef === `string` && columnRef.startsWith(`@`)) {\n value = extractValueFromNestedRow(\n nestedRow,\n columnRef.substring(1),\n mainTableAlias\n )\n } else {\n value = evaluateOperandOnNestedRow(\n nestedRow,\n columnRef as ConditionOperand,\n mainTableAlias\n )\n }\n // Ensure we return a number for aggregate functions\n return typeof value === `number` ? value : 0\n }\n\n // Return the appropriate aggregate function\n switch (functionName.toUpperCase()) {\n case `SUM`:\n return sum(valueExtractor)\n case `COUNT`:\n return count() // count() doesn't need a value extractor\n case `AVG`:\n return avg(valueExtractor)\n case `MIN`:\n return min(valueExtractor)\n case `MAX`:\n return max(valueExtractor)\n case `MEDIAN`:\n return median(valueExtractor)\n case `MODE`:\n return mode(valueExtractor)\n default:\n throw new Error(`Unsupported aggregate function: ${functionName}`)\n }\n}\n"],"names":["groupByOperators","extractValueFromNestedRow","isAggregateFunctionCall","groupBy","map","evaluateOperandOnNestedRow"],"mappings":";;;;;AASA,MAAM,EAAE,KAAK,OAAO,KAAK,KAAK,KAAK,QAAQ,SAASA,KAAA;AAKpC,SAAA,eACd,UACA,OACA,gBACA;AAEM,QAAA,iBAAiB,MAAM,QAAQ,MAAM,OAAO,IAC9C,MAAM,UACN,CAAC,MAAM,OAAO;AAGZ,QAAA,eAAe,CAAC,cAAuC;AAC3D,UAAM,MAA+B,CAAC;AAGtC,eAAW,UAAU,gBAAgB;AACnC,UAAI,OAAO,WAAW,YAAa,OAAkB,WAAW,GAAG,GAAG;AAC9D,cAAA,YAAa,OAAkB,UAAU,CAAC;AAC1C,cAAA,aAAa,UAAU,SAAS,GAAG,IACrC,UAAU,MAAM,GAAG,EAAE,CAAC,IACtB;AAEJ,YAAI,UAAW,IAAIC,WAAA;AAAA,UACjB;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MAAA;AAAA,IACF;AAGK,WAAA;AAAA,EACT;AAGA,QAAM,aAAkC,CAAC;AAG9B,aAAA,QAAQ,MAAM,QAAQ;AAC3B,QAAA,OAAO,SAAS,UAAU;AAC5B,iBAAW,CAAC,OAAO,IAAI,KAAK,OAAO,QAAQ,IAAI,GAAG;AAChD,YAAI,OAAO,SAAS,YAAYC,MAAA,wBAAwB,IAAI,GAAG;AAE7D,gBAAM,eAAe,OAAO,KAAK,IAAI,EAAE,CAAC;AAElC,gBAAA,YAAa,KACjB,YACF;AAGA,qBAAW,KAAK,IAAI;AAAA,YAClB;AAAA,YACA;AAAA,YACA;AAAA,UACF;AAAA,QAAA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAIF,MAAI,OAAO,KAAK,UAAU,EAAE,SAAS,GAAG;AACtC,eAAW,SAAS;AAAA,MAClBC,KAAA,QAAQ,cAAc,UAAU;AAAA;AAAA,MAEhCC,KAAAA,IAAI,CAAC,CAAC,MAAM,KAAK,MAAM;AAGd,eAAA;AAAA,MACR,CAAA;AAAA,IACH;AAAA,EAAA;AAGK,SAAA;AACT;AAKgB,SAAA,qBACd,cACA,WACA,gBACA;AAEM,QAAA,iBAAiB,CAAC,cAAuC;AACzD,QAAA;AACJ,QAAI,OAAO,cAAc,YAAY,UAAU,WAAW,GAAG,GAAG;AACtD,cAAAH,WAAA;AAAA,QACN;AAAA,QACA,UAAU,UAAU,CAAC;AAAA,QACrB;AAAA,MACF;AAAA,IAAA,OACK;AACG,cAAAI,WAAA;AAAA,QACN;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IAAA;AAGK,WAAA,OAAO,UAAU,WAAW,QAAQ;AAAA,EAC7C;AAGQ,UAAA,aAAa,YAAe,GAAA;AAAA,IAClC,KAAK;AACH,aAAO,IAAI,cAAc;AAAA,IAC3B,KAAK;AACH,aAAO,MAAM;AAAA;AAAA,IACf,KAAK;AACH,aAAO,IAAI,cAAc;AAAA,IAC3B,KAAK;AACH,aAAO,IAAI,cAAc;AAAA,IAC3B,KAAK;AACH,aAAO,IAAI,cAAc;AAAA,IAC3B,KAAK;AACH,aAAO,OAAO,cAAc;AAAA,IAC9B,KAAK;AACH,aAAO,KAAK,cAAc;AAAA,IAC5B;AACE,YAAM,IAAI,MAAM,mCAAmC,YAAY,EAAE;AAAA,EAAA;AAEvE;;;"}