@uwdata/mosaic-core 0.11.0 → 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/README.md +3 -1
  2. package/dist/mosaic-core.js +11613 -10856
  3. package/dist/mosaic-core.min.js +7 -7
  4. package/dist/types/Coordinator.d.ts +169 -0
  5. package/dist/types/MosaicClient.d.ts +94 -0
  6. package/dist/types/Param.d.ts +47 -0
  7. package/dist/types/QueryConsolidator.d.ts +9 -0
  8. package/dist/types/QueryManager.d.ts +64 -0
  9. package/dist/types/Selection.d.ts +224 -0
  10. package/dist/types/SelectionClause.d.ts +105 -0
  11. package/dist/types/connectors/rest.d.ts +17 -0
  12. package/dist/types/connectors/socket.d.ts +18 -0
  13. package/dist/types/connectors/wasm.d.ts +16 -0
  14. package/dist/types/index.d.ts +25 -0
  15. package/dist/types/preagg/PreAggregator.d.ts +178 -0
  16. package/dist/types/preagg/preagg-columns.d.ts +14 -0
  17. package/dist/types/preagg/sufficient-statistics.d.ts +13 -0
  18. package/dist/types/util/AsyncDispatch.d.ts +100 -0
  19. package/dist/types/util/cache.d.ts +13 -0
  20. package/dist/types/util/decode-ipc.d.ts +7 -0
  21. package/dist/types/util/distinct.d.ts +2 -0
  22. package/dist/types/util/field-info.d.ts +13 -0
  23. package/dist/types/util/hash.d.ts +1 -0
  24. package/dist/types/util/is-arrow-table.d.ts +8 -0
  25. package/dist/types/util/js-type.d.ts +1 -0
  26. package/dist/types/util/priority-queue.d.ts +37 -0
  27. package/dist/types/util/query-result.d.ts +44 -0
  28. package/dist/types/util/selection-types.d.ts +114 -0
  29. package/dist/types/util/synchronizer.d.ts +29 -0
  30. package/dist/types/util/throttle.d.ts +11 -0
  31. package/dist/types/util/to-data-columns.d.ts +29 -0
  32. package/dist/types/util/void-logger.d.ts +7 -0
  33. package/jsconfig.json +11 -0
  34. package/package.json +10 -8
  35. package/src/Coordinator.js +14 -14
  36. package/src/MosaicClient.js +5 -4
  37. package/src/QueryConsolidator.js +22 -33
  38. package/src/QueryManager.js +76 -45
  39. package/src/Selection.js +8 -5
  40. package/src/SelectionClause.js +20 -23
  41. package/src/connectors/rest.js +3 -1
  42. package/src/connectors/socket.js +3 -1
  43. package/src/connectors/wasm.js +1 -1
  44. package/src/index.js +13 -0
  45. package/src/preagg/PreAggregator.js +407 -0
  46. package/src/preagg/preagg-columns.js +103 -0
  47. package/src/preagg/sufficient-statistics.js +439 -0
  48. package/src/util/field-info.js +16 -5
  49. package/src/util/hash.js +1 -1
  50. package/src/util/query-result.js +44 -2
  51. package/src/util/selection-types.ts +3 -3
  52. package/src/util/throttle.js +11 -9
  53. package/src/util/void-logger.js +6 -5
  54. package/tsconfig.json +11 -0
  55. package/src/DataCubeIndexer.js +0 -378
  56. package/src/util/index-columns.js +0 -537
@@ -0,0 +1,114 @@
1
+ import { ExprNode } from '@uwdata/mosaic-sql';
2
+ import { MosaicClient } from '../MosaicClient.js';
3
+ /**
4
+ * Selection clause metadata to guide possible query optimizations.
5
+ * Sub-interfaces provide more information about the specifics of a
6
+ * given selection based on the selection type.
7
+ */
8
+ export interface ClauseMetadata {
9
+ /**
10
+ * The selection type, such as `'point'`, `'interval'`, or `'match'`.
11
+ */
12
+ type: string;
13
+ }
14
+ /**
15
+ * Selection clause metadata indicating selection of one or more discrete
16
+ * point values, typically based on equality or is distinctiveness checks.
17
+ */
18
+ export interface PointMetadata extends ClauseMetadata {
19
+ type: 'point';
20
+ }
21
+ /** Text search matching methods. */
22
+ export type MatchMethod = 'contains' | 'prefix' | 'suffix' | 'regexp' | (string & {});
23
+ /**
24
+ * Selection clause metadata indicating text search matching.
25
+ */
26
+ export interface MatchMetadata extends ClauseMetadata {
27
+ type: MatchMethod;
28
+ /** The text search matching method used. */
29
+ method?: 'contains' | 'prefix' | 'suffix' | 'regexp' | (string & {});
30
+ }
31
+ /** Quantitative scale types. */
32
+ export type ScaleType = 'identity' | 'linear' | 'log' | 'sqrt' | 'pow' | 'symlog' | 'time' | 'utc';
33
+ /** A data value interval extent. */
34
+ export type Extent = [number, number] | [Date, Date];
35
+ /**
36
+ * Descriptor for a scale that maps a data domain to screen pixels.
37
+ */
38
+ export interface Scale {
39
+ /** The scale type, such as `'linear'`, `'log'`, etc. */
40
+ type: ScaleType;
41
+ /** The scale domain, as an array of start and end data values. */
42
+ domain: Extent;
43
+ /**
44
+ * The scale range, as an array of start and end screen pixels.
45
+ * The range may be omitted for *identity* scales.
46
+ */
47
+ range?: [number, number];
48
+ /** The base of the logarithm. For `'log'` scales only. */
49
+ base?: number;
50
+ /** The constant parameter. For `'symlog'` scales only. */
51
+ constant?: number;
52
+ /** The exponent parameter. For `'pow'` scales only. */
53
+ exponent?: number;
54
+ }
55
+ /** A binning method name. */
56
+ export type BinMethod = 'floor' | 'ceil' | 'round';
57
+ /**
58
+ * Selection clause metadata for one or more selected intervals. This
59
+ * metadata can be used to determine appropriate data-space binning
60
+ * schemes that correspond to pixel-level bins in screen space.
61
+ */
62
+ export interface IntervalMetadata extends ClauseMetadata {
63
+ type: 'interval';
64
+ /**
65
+ * The interactive pixel size used by the generating component.
66
+ * Values larger than one indicate intervals that "snap-to" values
67
+ * greater than a single pixel. If unspecified, assumed to be `1`.
68
+ */
69
+ pixelSize?: number;
70
+ /**
71
+ * An array of one or more scale descriptors that describe the
72
+ * mapping from data values to screen pixels.
73
+ */
74
+ scales?: Scale[];
75
+ /**
76
+ * A hint for the binning method to use when discretizing the
77
+ * interval domain. If unspecified, the default is `'floor'`.
78
+ */
79
+ bin?: BinMethod;
80
+ }
81
+ /**
82
+ * A selection clause representing filtering criteria
83
+ * to apply within a Mosiac Selection.
84
+ */
85
+ export interface SelectionClause {
86
+ /**
87
+ * A unique identifier (according to object equality) for the source
88
+ * component that generated this clause. In many cases, this is a
89
+ * reference to the originating component itself.
90
+ */
91
+ source: any;
92
+ /**
93
+ * A set of Mosaic clients associated with this clause that should not
94
+ * be updated when this clause is applied in a cross-filtering context.
95
+ */
96
+ clients?: Set<MosaicClient>;
97
+ /**
98
+ * A selected value associated with this clause. For example, for a 1D
99
+ * interval selection clause the value may be a [lo, hi] array.
100
+ */
101
+ value: any;
102
+ /**
103
+ * A predicate SQL expression suitable for use in a query WHERE clause.
104
+ * The predicate should apply filtering criteria consistent with this
105
+ * clause's *value* property.
106
+ */
107
+ predicate: ExprNode | null;
108
+ /**
109
+ * Optional clause metadata that varies based on the selection type.
110
+ * The metadata can be used to optimize selection queries, for example
111
+ * by creating materialized views of pre-aggregated data when applicable.
112
+ */
113
+ meta?: ClauseMetadata;
114
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Create a new synchronizer instance to aid synchronization
3
+ * of updates on multiple pending operations.
4
+ */
5
+ export function synchronizer(): {
6
+ /**
7
+ * Mark an item as pending.
8
+ * @param {*} item An item to synchronize on.
9
+ */
10
+ pending(item: any): void;
11
+ /**
12
+ * Mark a pending item as ready, indicating it is
13
+ * ready for a synchronized update.
14
+ * @param {*} item An item to synchronize on.
15
+ * @returns {boolean} True if the synchronizer is ready to
16
+ * resolve, false otherwise.
17
+ */
18
+ ready(item: any): boolean;
19
+ /**
20
+ * Resolve the current synchronization cycle, causing the synchronize
21
+ * promise to resolve and thereby trigger downstream updates.
22
+ */
23
+ resolve(): void;
24
+ /**
25
+ * The promise for the current synchronization cycle.
26
+ * @return {Promise} The synchronization promise.
27
+ */
28
+ readonly promise: Promise<any>;
29
+ };
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Throttle invocations of a callback function. The callback must return
3
+ * a Promise. Upon repeated invocation, the callback will not be invoked
4
+ * until a prior Promise resolves. If multiple invocations occurs while
5
+ * waiting, only the most recent invocation will be pending.
6
+ * @param {(event: *) => Promise} callback The callback function.
7
+ * @param {boolean} [debounce=true] Flag indicating if invocations
8
+ * should also be debounced within the current animation frame.
9
+ * @returns A new function that throttles access to the callback.
10
+ */
11
+ export function throttle(callback: (event: any) => Promise<any>, debounce?: boolean): (event: any) => void;
@@ -0,0 +1,29 @@
1
+ /**
2
+ * @typedef {Array | Int8Array | Uint8Array | Uint8ClampedArray
3
+ * | Int16Array | Uint16Array | Int32Array | Uint32Array
4
+ * | Float32Array | Float64Array
5
+ * } Arrayish - an Array or TypedArray
6
+ */
7
+ /**
8
+ * @typedef {
9
+ * | { numRows: number, columns: Record<string,Arrayish> }
10
+ * | { numRows: number, values: Arrayish; }
11
+ * } DataColumns
12
+ */
13
+ /**
14
+ * Convert input data to a set of column arrays.
15
+ * @param {any} data The input data.
16
+ * @returns {DataColumns} An object with named column arrays.
17
+ */
18
+ export function toDataColumns(data: any): DataColumns;
19
+ /**
20
+ * - an Array or TypedArray
21
+ */
22
+ export type Arrayish = any[] | Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array;
23
+ export type DataColumns = any | {
24
+ numRows: number;
25
+ columns: Record<string, Arrayish>;
26
+ } | {
27
+ numRows: number;
28
+ values: Arrayish;
29
+ };
@@ -0,0 +1,7 @@
1
+ export function voidLogger(): {
2
+ debug(..._: any[]): void;
3
+ info(..._: any[]): void;
4
+ log(..._: any[]): void;
5
+ warn(..._: any[]): void;
6
+ error(..._: any[]): void;
7
+ };
package/jsconfig.json ADDED
@@ -0,0 +1,11 @@
1
+ {
2
+ "include": ["src/**/*"],
3
+ "compilerOptions": {
4
+ "checkJs": true,
5
+ "noEmit": true,
6
+ "noImplicitAny": false,
7
+ "module": "node16",
8
+ "skipLibCheck": true,
9
+ "types": []
10
+ }
11
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uwdata/mosaic-core",
3
- "version": "0.11.0",
3
+ "version": "0.12.1",
4
4
  "description": "Scalable and extensible linked data views.",
5
5
  "keywords": [
6
6
  "mosaic",
@@ -16,24 +16,26 @@
16
16
  "module": "src/index.js",
17
17
  "jsdelivr": "dist/mosaic-core.min.js",
18
18
  "unpkg": "dist/mosaic-core.min.js",
19
+ "types": "dist/types/index.d.ts",
19
20
  "repository": {
20
21
  "type": "git",
21
22
  "url": "https://github.com/uwdata/mosaic.git"
22
23
  },
23
24
  "scripts": {
24
25
  "prebuild": "rimraf dist && mkdir dist",
25
- "build": "node ../../esbuild.js mosaic-core",
26
+ "build": "npm run types && node ../../esbuild.js mosaic-core",
27
+ "types": "tsc",
26
28
  "lint": "eslint src test",
27
- "test": "vitest run --dangerouslyIgnoreUnhandledErrors",
29
+ "test": "vitest run && tsc -p jsconfig.json",
28
30
  "prepublishOnly": "npm run test && npm run lint && npm run build"
29
31
  },
30
32
  "dependencies": {
31
- "@duckdb/duckdb-wasm": "^1.28.1-dev278.0",
32
- "@uwdata/flechette": "^1.0.2",
33
- "@uwdata/mosaic-sql": "^0.11.0"
33
+ "@duckdb/duckdb-wasm": "^1.29.0",
34
+ "@uwdata/flechette": "^1.1.1",
35
+ "@uwdata/mosaic-sql": "^0.12.1"
34
36
  },
35
37
  "devDependencies": {
36
- "@uwdata/mosaic-duckdb": "^0.11.0"
38
+ "@uwdata/mosaic-duckdb": "^0.12.1"
37
39
  },
38
- "gitHead": "861d616f39926a1d2aee83b59dbdd70b0b3caf12"
40
+ "gitHead": "fe3a7c34352da54ec36a1ebf557846f46a649782"
39
41
  }
@@ -1,10 +1,10 @@
1
1
  import { socketConnector } from './connectors/socket.js';
2
- import { DataCubeIndexer } from './DataCubeIndexer.js';
3
- import { MosaicClient } from './MosaicClient.js';
4
- import { QueryManager, Priority } from './QueryManager.js';
2
+ import { PreAggregator } from './preagg/PreAggregator.js';
5
3
  import { queryFieldInfo } from './util/field-info.js';
6
4
  import { QueryResult } from './util/query-result.js';
7
5
  import { voidLogger } from './util/void-logger.js';
6
+ import { MosaicClient } from './MosaicClient.js';
7
+ import { QueryManager, Priority } from './QueryManager.js';
8
8
 
9
9
  /**
10
10
  * The singleton Coordinator instance.
@@ -33,15 +33,15 @@ export function coordinator(instance) {
33
33
  /**
34
34
  * A Mosaic Coordinator manages all database communication for clients and
35
35
  * handles selection updates. The Coordinator also performs optimizations
36
- * including query caching, consolidation, and data cube indexing.
36
+ * including query caching, consolidation, and pre-aggregation.
37
37
  * @param {*} [db] Database connector. Defaults to a web socket connection.
38
38
  * @param {object} [options] Coordinator options.
39
39
  * @param {*} [options.logger=console] The logger to use, defaults to `console`.
40
40
  * @param {*} [options.manager] The query manager to use.
41
41
  * @param {boolean} [options.cache=true] Boolean flag to enable/disable query caching.
42
42
  * @param {boolean} [options.consolidate=true] Boolean flag to enable/disable query consolidation.
43
- * @param {import('./DataCubeIndexer.js').DataCubeIndexerOptions} [options.indexes]
44
- * Data cube indexer options.
43
+ * @param {import('./preagg/PreAggregator.js').PreAggregateOptions} [options.preagg]
44
+ * Options for the Pre-aggregator.
45
45
  */
46
46
  export class Coordinator {
47
47
  constructor(db = socketConnector(), {
@@ -49,7 +49,7 @@ export class Coordinator {
49
49
  manager = new QueryManager(),
50
50
  cache = true,
51
51
  consolidate = true,
52
- indexes = {}
52
+ preagg = {}
53
53
  } = {}) {
54
54
  /** @type {QueryManager} */
55
55
  this.manager = manager;
@@ -58,7 +58,7 @@ export class Coordinator {
58
58
  this.databaseConnector(db);
59
59
  this.logger(logger);
60
60
  this.clear();
61
- this.dataCubeIndexer = new DataCubeIndexer(this, indexes);
61
+ this.preaggregator = new PreAggregator(this, preagg);
62
62
  }
63
63
 
64
64
  /**
@@ -208,12 +208,12 @@ export class Coordinator {
208
208
  /**
209
209
  * Issue a query request for a client. If the query is null or undefined,
210
210
  * the client is simply updated. Otherwise `updateClient` is called. As a
211
- * side effect, this method clears the current data cube indexer state.
211
+ * side effect, this method clears the current preaggregator state.
212
212
  * @param {MosaicClient} client The client to update.
213
213
  * @param {QueryType | null} [query] The query to issue.
214
214
  */
215
215
  requestQuery(client, query) {
216
- this.dataCubeIndexer.clear();
216
+ this.preaggregator.clear();
217
217
  return query
218
218
  ? this.updateClient(client, query)
219
219
  : Promise.resolve(client.update());
@@ -307,10 +307,10 @@ function connectSelection(mc, selection, client) {
307
307
  * selection clause representative of the activation.
308
308
  */
309
309
  function activateSelection(mc, selection, clause) {
310
- const { dataCubeIndexer, filterGroups } = mc;
310
+ const { preaggregator, filterGroups } = mc;
311
311
  const { clients } = filterGroups.get(selection);
312
312
  for (const client of clients) {
313
- dataCubeIndexer.index(client, selection, clause);
313
+ preaggregator.request(client, selection, clause);
314
314
  }
315
315
  }
316
316
 
@@ -322,11 +322,11 @@ function activateSelection(mc, selection, clause) {
322
322
  * @returns {Promise} A Promise that resolves when the update completes.
323
323
  */
324
324
  function updateSelection(mc, selection) {
325
- const { dataCubeIndexer, filterGroups } = mc;
325
+ const { preaggregator, filterGroups } = mc;
326
326
  const { clients } = filterGroups.get(selection);
327
327
  const { active } = selection;
328
328
  return Promise.allSettled(Array.from(clients, client => {
329
- const info = dataCubeIndexer.index(client, selection, active);
329
+ const info = preaggregator.request(client, selection, active);
330
330
  const filter = info ? null : selection.predicate(client);
331
331
 
332
332
  // skip due to cross-filtering
@@ -38,11 +38,12 @@ export class MosaicClient {
38
38
  }
39
39
 
40
40
  /**
41
- * Return a boolean indicating if the client query can be indexed. Should
42
- * return true if changes to the filterBy selection does not change the
43
- * groupby domain of the client query.
41
+ * Return a boolean indicating if the client query can be sped up with
42
+ * materialized views of pre-aggregated data. Should return true if changes to
43
+ * the filterBy selection does not change the groupby domain of the client
44
+ * query.
44
45
  */
45
- get filterIndexable() {
46
+ get filterStable() {
46
47
  return true;
47
48
  }
48
49
 
@@ -1,4 +1,4 @@
1
- import { Query, Ref, isDescribeQuery } from '@uwdata/mosaic-sql';
1
+ import { DescribeQuery, isAggregateExpression, isColumnRef, isDescribeQuery, isSelectQuery, Query } from '@uwdata/mosaic-sql';
2
2
  import { QueryResult } from './util/query-result.js';
3
3
 
4
4
  function wait(callback) {
@@ -13,10 +13,9 @@ function wait(callback) {
13
13
  * Create a consolidator to combine structurally compatible queries.
14
14
  * @param {*} enqueue Query manager enqueue method
15
15
  * @param {*} cache Client-side query cache (sql -> data)
16
- * @param {*} record Query recorder function
17
16
  * @returns A consolidator object
18
17
  */
19
- export function consolidator(enqueue, cache, record) {
18
+ export function consolidator(enqueue, cache) {
20
19
  let pending = [];
21
20
  let id = 0;
22
21
 
@@ -28,7 +27,7 @@ export function consolidator(enqueue, cache, record) {
28
27
 
29
28
  // build and issue consolidated queries
30
29
  for (const group of groups) {
31
- consolidate(group, enqueue, record);
30
+ consolidate(group, enqueue);
32
31
  processResults(group, cache);
33
32
  }
34
33
  }
@@ -75,18 +74,16 @@ function entryGroups(entries, cache) {
75
74
  * Queries with matching keys are conosolidation-compatible.
76
75
  * If a query is found in the cache, it is exempted from consolidation,
77
76
  * which is indicated by returning the precise query SQL as the key.
78
- * @param {*} query The input query.
77
+ * @param {Query | DescribeQuery} query The input query.
79
78
  * @param {*} cache The query cache (sql -> data).
80
79
  * @returns a key string
81
80
  */
82
81
  function consolidationKey(query, cache) {
83
82
  const sql = `${query}`;
84
- if (query instanceof Query && !cache.get(sql)) {
83
+ if (isSelectQuery(query) && !cache.get(sql)) {
85
84
  if (
86
- // @ts-ignore
87
- query.orderby().length || query.where().length ||
88
- // @ts-ignore
89
- query.qualify().length || query.having().length
85
+ query._orderby.length || query._where.length ||
86
+ query._qualify.length || query._having.length
90
87
  ) {
91
88
  // do not try to analyze if query includes clauses
92
89
  // that may refer to *derived* columns we can't resolve
@@ -94,25 +91,21 @@ function consolidationKey(query, cache) {
94
91
  }
95
92
 
96
93
  // create a derived query stripped of selections
97
- const q = query.clone().$select('*');
94
+ const q = query.clone().setSelect('*');
98
95
 
99
96
  // check group by criteria for compatibility
100
97
  // queries may refer to *derived* columns as group by criteria
101
98
  // we resolve these against the true grouping expressions
102
- const groupby = query.groupby();
103
- // @ts-ignore
99
+ const groupby = query._groupby;
104
100
  if (groupby.length) {
105
- const map = {}; // expression map (as -> expr)
106
- // @ts-ignore
107
- query.select().forEach(({ as, expr }) => map[as] = expr);
108
- // @ts-ignore
109
- q.$groupby(groupby.map(e => (e instanceof Ref && map[e.column]) || e));
101
+ const map = {}; // expression map (alias -> expr)
102
+ query._select.forEach(({ alias, expr }) => map[alias] = expr);
103
+ q.setGroupby(groupby.map(e => (isColumnRef(e) && map[e.column]) || e));
110
104
  }
111
- // @ts-ignore
112
- else if (query.select().some(({ expr }) => expr.aggregate)) {
105
+ else if (query._select.some(e => isAggregateExpression(e.expr))) {
113
106
  // if query is an ungrouped aggregate, add an explicit groupby to
114
107
  // prevent improper consolidation with non-aggregate queries
115
- q.$groupby('ALL');
108
+ q.setGroupby('ALL');
116
109
  }
117
110
 
118
111
  // key is just the transformed query as SQL
@@ -127,17 +120,15 @@ function consolidationKey(query, cache) {
127
120
  * Issue queries, consolidating where possible.
128
121
  * @param {*} group Array of bundled query entries
129
122
  * @param {*} enqueue Add entry to query queue
130
- * @param {*} record Query recorder function
131
123
  */
132
- function consolidate(group, enqueue, record) {
124
+ function consolidate(group, enqueue) {
133
125
  if (shouldConsolidate(group)) {
134
126
  // issue a single consolidated query
135
127
  enqueue({
136
128
  request: {
137
129
  type: 'arrow',
138
130
  cache: false,
139
- record: false,
140
- query: (group.query = consolidatedQuery(group, record))
131
+ query: (group.query = consolidatedQuery(group))
141
132
  },
142
133
  result: (group.result = new QueryResult())
143
134
  });
@@ -170,10 +161,9 @@ function shouldConsolidate(group) {
170
161
  /**
171
162
  * Create a consolidated query for a group.
172
163
  * @param {*} group Array of bundled query entries
173
- * @param {*} record Query recorder function
174
164
  * @returns A consolidated Query instance
175
165
  */
176
- function consolidatedQuery(group, record) {
166
+ function consolidatedQuery(group) {
177
167
  const maps = group.maps = [];
178
168
  const fields = new Map;
179
169
 
@@ -182,30 +172,29 @@ function consolidatedQuery(group, record) {
182
172
  const { query } = item.entry.request;
183
173
  const fieldMap = [];
184
174
  maps.push(fieldMap);
185
- for (const { as, expr } of query.select()) {
175
+ for (const { alias, expr } of query._select) {
186
176
  const e = `${expr}`;
187
177
  if (!fields.has(e)) {
188
178
  fields.set(e, [`col${fields.size}`, expr]);
189
179
  }
190
180
  const [name] = fields.get(e);
191
- fieldMap.push([name, as]);
181
+ fieldMap.push([name, alias]);
192
182
  }
193
- record(`${query}`);
194
183
  }
195
184
 
196
185
  // use a cloned query as a starting point
197
186
  const query = group[0].entry.request.query.clone();
198
187
 
199
188
  // update group by statement as needed
200
- const groupby = query.groupby();
189
+ const groupby = query._groupby;
201
190
  if (groupby.length) {
202
191
  const map = {};
203
192
  group.maps[0].forEach(([name, as]) => map[as] = name);
204
- query.$groupby(groupby.map(e => (e instanceof Ref && map[e.column]) || e));
193
+ query.setGroupby(groupby.map(e => (isColumnRef(e) && map[e.column]) || e));
205
194
  }
206
195
 
207
196
  // update select statement and return
208
- return query.$select(Array.from(fields.values()));
197
+ return query.setSelect(Array.from(fields.values()));
209
198
  }
210
199
 
211
200
  /**