@uwdata/mosaic-core 0.9.0 → 0.11.0
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.
- package/dist/mosaic-core.js +11493 -19779
- package/dist/mosaic-core.min.js +6 -15
- package/package.json +8 -8
- package/src/Coordinator.js +225 -55
- package/src/DataCubeIndexer.js +304 -141
- package/src/MosaicClient.js +12 -3
- package/src/QueryConsolidator.js +15 -11
- package/src/QueryManager.js +13 -5
- package/src/Selection.js +64 -12
- package/src/SelectionClause.js +22 -8
- package/src/connectors/rest.js +3 -3
- package/src/connectors/socket.js +4 -3
- package/src/connectors/wasm.js +20 -4
- package/src/index.js +10 -6
- package/src/util/AsyncDispatch.js +15 -5
- package/src/util/decode-ipc.js +11 -0
- package/src/util/field-info.js +3 -11
- package/src/util/index-columns.js +79 -80
- package/src/util/is-arrow-table.js +10 -0
- package/src/util/priority-queue.js +75 -76
- package/src/util/query-result.js +41 -8
- package/src/util/throttle.js +11 -1
- package/src/util/to-data-columns.js +60 -0
- package/src/FilterGroup.js +0 -81
- package/src/util/convert-arrow.js +0 -145
package/src/Selection.js
CHANGED
|
@@ -10,6 +10,13 @@ export function isSelection(x) {
|
|
|
10
10
|
return x instanceof Selection;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
function create(options, include) {
|
|
14
|
+
return new Selection(
|
|
15
|
+
new SelectionResolver(options),
|
|
16
|
+
include ? [include].flat() : include
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
13
20
|
/**
|
|
14
21
|
* Represents a dynamic set of query filter predicates.
|
|
15
22
|
*/
|
|
@@ -22,10 +29,16 @@ export class Selection extends Param {
|
|
|
22
29
|
* @param {boolean} [options.cross=false] Boolean flag indicating
|
|
23
30
|
* cross-filtered resolution. If true, selection clauses will not
|
|
24
31
|
* be applied to the clients they are associated with.
|
|
32
|
+
* @param {boolean} [options.empty=false] Boolean flag indicating if a lack
|
|
33
|
+
* of clauses should correspond to an empty selection with no records. This
|
|
34
|
+
* setting determines the default selection state.
|
|
35
|
+
* @param {Selection|Selection[]} [options.include] Upstream selections whose
|
|
36
|
+
* clauses should be included as part of the new selection. Any clauses
|
|
37
|
+
* published to upstream selections will be relayed to the new selection.
|
|
25
38
|
* @returns {Selection} The new Selection instance.
|
|
26
39
|
*/
|
|
27
|
-
static intersect({ cross = false } = {}) {
|
|
28
|
-
return
|
|
40
|
+
static intersect({ cross = false, empty = false, include = [] } = {}) {
|
|
41
|
+
return create({ cross, empty }, include);
|
|
29
42
|
}
|
|
30
43
|
|
|
31
44
|
/**
|
|
@@ -35,10 +48,16 @@ export class Selection extends Param {
|
|
|
35
48
|
* @param {boolean} [options.cross=false] Boolean flag indicating
|
|
36
49
|
* cross-filtered resolution. If true, selection clauses will not
|
|
37
50
|
* be applied to the clients they are associated with.
|
|
51
|
+
* @param {boolean} [options.empty=false] Boolean flag indicating if a lack
|
|
52
|
+
* of clauses should correspond to an empty selection with no records. This
|
|
53
|
+
* setting determines the default selection state.
|
|
54
|
+
* @param {Selection|Selection[]} [options.include] Upstream selections whose
|
|
55
|
+
* clauses should be included as part of the new selection. Any clauses
|
|
56
|
+
* published to upstream selections will be relayed to the new selection.
|
|
38
57
|
* @returns {Selection} The new Selection instance.
|
|
39
58
|
*/
|
|
40
|
-
static union({ cross = false } = {}) {
|
|
41
|
-
return
|
|
59
|
+
static union({ cross = false, empty = false, include = [] } = {}) {
|
|
60
|
+
return create({ cross, empty, union: true }, include);
|
|
42
61
|
}
|
|
43
62
|
|
|
44
63
|
/**
|
|
@@ -48,30 +67,53 @@ export class Selection extends Param {
|
|
|
48
67
|
* @param {boolean} [options.cross=false] Boolean flag indicating
|
|
49
68
|
* cross-filtered resolution. If true, selection clauses will not
|
|
50
69
|
* be applied to the clients they are associated with.
|
|
70
|
+
* @param {boolean} [options.empty=false] Boolean flag indicating if a lack
|
|
71
|
+
* of clauses should correspond to an empty selection with no records. This
|
|
72
|
+
* setting determines the default selection state.
|
|
73
|
+
* @param {Selection|Selection[]} [options.include] Upstream selections whose
|
|
74
|
+
* clauses should be included as part of the new selection. Any clauses
|
|
75
|
+
* published to upstream selections will be relayed to the new selection.
|
|
51
76
|
* @returns {Selection} The new Selection instance.
|
|
52
77
|
*/
|
|
53
|
-
static single({ cross = false } = {}) {
|
|
54
|
-
return
|
|
78
|
+
static single({ cross = false, empty = false, include = [] } = {}) {
|
|
79
|
+
return create({ cross, empty, single: true }, include);
|
|
55
80
|
}
|
|
56
81
|
|
|
57
82
|
/**
|
|
58
83
|
* Create a new Selection instance with a
|
|
59
84
|
* cross-filtered intersect resolution strategy.
|
|
85
|
+
* @param {object} [options] The selection options.
|
|
86
|
+
* @param {boolean} [options.empty=false] Boolean flag indicating if a lack
|
|
87
|
+
* of clauses should correspond to an empty selection with no records. This
|
|
88
|
+
* setting determines the default selection state.
|
|
89
|
+
* @param {Selection|Selection[]} [options.include] Upstream selections whose
|
|
90
|
+
* clauses should be included as part of the new selection. Any clauses
|
|
91
|
+
* published to upstream selections will be relayed to the new selection.
|
|
60
92
|
* @returns {Selection} The new Selection instance.
|
|
61
93
|
*/
|
|
62
|
-
static crossfilter() {
|
|
63
|
-
return
|
|
94
|
+
static crossfilter({ empty = false, include = [] } = {}) {
|
|
95
|
+
return create({ cross: true, empty }, include);
|
|
64
96
|
}
|
|
65
97
|
|
|
66
98
|
/**
|
|
67
99
|
* Create a new Selection instance.
|
|
68
|
-
* @param {SelectionResolver} resolver The selection resolution
|
|
100
|
+
* @param {SelectionResolver} [resolver] The selection resolution
|
|
69
101
|
* strategy to apply.
|
|
102
|
+
* @param {Selection[]} [include] Upstream selections whose clauses
|
|
103
|
+
* should be included as part of this selection. Any clauses published
|
|
104
|
+
* to these upstream selections will be relayed to this selection.
|
|
70
105
|
*/
|
|
71
|
-
constructor(resolver = new SelectionResolver()) {
|
|
106
|
+
constructor(resolver = new SelectionResolver(), include = []) {
|
|
72
107
|
super([]);
|
|
73
108
|
this._resolved = this._value;
|
|
74
109
|
this._resolver = resolver;
|
|
110
|
+
/** @type {Set<Selection>} */
|
|
111
|
+
this._relay = new Set;
|
|
112
|
+
if (Array.isArray(include)) {
|
|
113
|
+
for (const sel of include) {
|
|
114
|
+
sel._relay.add(this);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
75
117
|
}
|
|
76
118
|
|
|
77
119
|
/**
|
|
@@ -148,6 +190,7 @@ export class Selection extends Param {
|
|
|
148
190
|
*/
|
|
149
191
|
activate(clause) {
|
|
150
192
|
this.emit('activate', clause);
|
|
193
|
+
this._relay.forEach(sel => sel.activate(clause));
|
|
151
194
|
}
|
|
152
195
|
|
|
153
196
|
/**
|
|
@@ -160,6 +203,7 @@ export class Selection extends Param {
|
|
|
160
203
|
// this ensures consistent clause state across unemitted event values
|
|
161
204
|
this._resolved = this._resolver.resolve(this._resolved, clause, true);
|
|
162
205
|
this._resolved.active = clause;
|
|
206
|
+
this._relay.forEach(sel => sel.update(clause));
|
|
163
207
|
return super.update(this._resolved);
|
|
164
208
|
}
|
|
165
209
|
|
|
@@ -232,11 +276,15 @@ export class SelectionResolver {
|
|
|
232
276
|
* If false, an intersection strategy is used.
|
|
233
277
|
* @param {boolean} [options.cross=false] Boolean flag to indicate cross-filtering.
|
|
234
278
|
* @param {boolean} [options.single=false] Boolean flag to indicate single clauses only.
|
|
279
|
+
* @param {boolean} [options.empty=false] Boolean flag indicating if a lack
|
|
280
|
+
* of clauses should correspond to an empty selection with no records. This
|
|
281
|
+
* setting determines the default selection state.
|
|
235
282
|
*/
|
|
236
|
-
constructor({ union, cross, single } = {}) {
|
|
283
|
+
constructor({ union, cross, single, empty } = {}) {
|
|
237
284
|
this.union = !!union;
|
|
238
285
|
this.cross = !!cross;
|
|
239
286
|
this.single = !!single;
|
|
287
|
+
this.empty = !!empty;
|
|
240
288
|
}
|
|
241
289
|
|
|
242
290
|
/**
|
|
@@ -274,7 +322,11 @@ export class SelectionResolver {
|
|
|
274
322
|
* based on the current state of this selection.
|
|
275
323
|
*/
|
|
276
324
|
predicate(clauseList, active, client) {
|
|
277
|
-
const { union } = this;
|
|
325
|
+
const { empty, union } = this;
|
|
326
|
+
|
|
327
|
+
if (empty && !clauseList.length) {
|
|
328
|
+
return ['FALSE'];
|
|
329
|
+
}
|
|
278
330
|
|
|
279
331
|
// do nothing if cross-filtering and client is currently active
|
|
280
332
|
if (this.skip(client, active)) return undefined;
|
package/src/SelectionClause.js
CHANGED
|
@@ -24,7 +24,10 @@ import { MosaicClient } from './MosaicClient.js';
|
|
|
24
24
|
* cross-filtering contexts.
|
|
25
25
|
* @returns {SelectionClause} The generated selection clause.
|
|
26
26
|
*/
|
|
27
|
-
export function
|
|
27
|
+
export function clausePoint(field, value, {
|
|
28
|
+
source,
|
|
29
|
+
clients = source ? new Set([source]) : undefined
|
|
30
|
+
}) {
|
|
28
31
|
/** @type {SQLExpression | null} */
|
|
29
32
|
const predicate = value !== undefined
|
|
30
33
|
? isNotDistinct(field, literal(value))
|
|
@@ -50,7 +53,10 @@ export function point(field, value, { source, clients = undefined }) {
|
|
|
50
53
|
* cross-filtering contexts.
|
|
51
54
|
* @returns {SelectionClause} The generated selection clause.
|
|
52
55
|
*/
|
|
53
|
-
export function
|
|
56
|
+
export function clausePoints(fields, value, {
|
|
57
|
+
source,
|
|
58
|
+
clients = source ? new Set([source]) : undefined
|
|
59
|
+
}) {
|
|
54
60
|
/** @type {SQLExpression | null} */
|
|
55
61
|
let predicate = null;
|
|
56
62
|
if (value) {
|
|
@@ -83,13 +89,17 @@ export function points(fields, value, { source, clients = undefined }) {
|
|
|
83
89
|
* @param {number} [options.pixelSize=1] The interactive pixel size.
|
|
84
90
|
* @returns {SelectionClause} The generated selection clause.
|
|
85
91
|
*/
|
|
86
|
-
export function
|
|
87
|
-
source,
|
|
92
|
+
export function clauseInterval(field, value, {
|
|
93
|
+
source,
|
|
94
|
+
clients = source ? new Set([source]) : undefined,
|
|
95
|
+
bin,
|
|
96
|
+
scale,
|
|
97
|
+
pixelSize = 1
|
|
88
98
|
}) {
|
|
89
99
|
/** @type {SQLExpression | null} */
|
|
90
100
|
const predicate = value != null ? isBetween(field, value) : null;
|
|
91
101
|
/** @type {import('./util/selection-types.js').IntervalMetadata} */
|
|
92
|
-
const meta = { type: 'interval', scales: [scale], bin, pixelSize };
|
|
102
|
+
const meta = { type: 'interval', scales: scale && [scale], bin, pixelSize };
|
|
93
103
|
return { meta, source, clients, value, predicate };
|
|
94
104
|
}
|
|
95
105
|
|
|
@@ -108,8 +118,12 @@ export function interval(field, value, {
|
|
|
108
118
|
* @param {number} [options.pixelSize=1] The interactive pixel size.
|
|
109
119
|
* @returns {SelectionClause} The generated selection clause.
|
|
110
120
|
*/
|
|
111
|
-
export function
|
|
112
|
-
source,
|
|
121
|
+
export function clauseIntervals(fields, value, {
|
|
122
|
+
source,
|
|
123
|
+
clients = source ? new Set([source]) : undefined,
|
|
124
|
+
bin,
|
|
125
|
+
scales = [],
|
|
126
|
+
pixelSize = 1
|
|
113
127
|
}) {
|
|
114
128
|
/** @type {SQLExpression | null} */
|
|
115
129
|
const predicate = value != null
|
|
@@ -135,7 +149,7 @@ const MATCH_METHODS = { contains, prefix, suffix, regexp: regexp_matches };
|
|
|
135
149
|
* text matching method to use. Defaults to `'contains'`.
|
|
136
150
|
* @returns {SelectionClause} The generated selection clause.
|
|
137
151
|
*/
|
|
138
|
-
export function
|
|
152
|
+
export function clauseMatch(field, value, {
|
|
139
153
|
source, clients = undefined, method = 'contains'
|
|
140
154
|
}) {
|
|
141
155
|
let fn = MATCH_METHODS[method];
|
package/src/connectors/rest.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { decodeIPC } from '../util/decode-ipc.js';
|
|
2
2
|
|
|
3
3
|
export function restConnector(uri = 'http://localhost:3000/') {
|
|
4
4
|
return {
|
|
5
5
|
/**
|
|
6
6
|
* Query the DuckDB server.
|
|
7
7
|
* @param {object} query
|
|
8
|
-
* @param {'exec' | 'arrow' | 'json'} [query.type] The query type
|
|
8
|
+
* @param {'exec' | 'arrow' | 'json' | 'create-bundle' | 'load-bundle'} [query.type] The query type.
|
|
9
9
|
* @param {string} query.sql A SQL query string.
|
|
10
10
|
* @returns the query result
|
|
11
11
|
*/
|
|
@@ -20,7 +20,7 @@ export function restConnector(uri = 'http://localhost:3000/') {
|
|
|
20
20
|
});
|
|
21
21
|
|
|
22
22
|
return query.type === 'exec' ? req
|
|
23
|
-
: query.type === 'arrow' ?
|
|
23
|
+
: query.type === 'arrow' ? decodeIPC(await (await req).arrayBuffer())
|
|
24
24
|
: (await req).json();
|
|
25
25
|
}
|
|
26
26
|
};
|
package/src/connectors/socket.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { decodeIPC } from '../util/decode-ipc.js';
|
|
2
2
|
|
|
3
3
|
export function socketConnector(uri = 'ws://localhost:3000/') {
|
|
4
4
|
const queue = [];
|
|
@@ -47,7 +47,7 @@ export function socketConnector(uri = 'ws://localhost:3000/') {
|
|
|
47
47
|
} else if (query.type === 'exec') {
|
|
48
48
|
resolve();
|
|
49
49
|
} else if (query.type === 'arrow') {
|
|
50
|
-
resolve(
|
|
50
|
+
resolve(decodeIPC(data));
|
|
51
51
|
} else {
|
|
52
52
|
throw new Error(`Unexpected socket data: ${data}`);
|
|
53
53
|
}
|
|
@@ -59,6 +59,7 @@ export function socketConnector(uri = 'ws://localhost:3000/') {
|
|
|
59
59
|
|
|
60
60
|
function init() {
|
|
61
61
|
ws = new WebSocket(uri);
|
|
62
|
+
ws.binaryType = 'arraybuffer';
|
|
62
63
|
for (const type in events) {
|
|
63
64
|
ws.addEventListener(type, events[type]);
|
|
64
65
|
}
|
|
@@ -84,7 +85,7 @@ export function socketConnector(uri = 'ws://localhost:3000/') {
|
|
|
84
85
|
/**
|
|
85
86
|
* Query the DuckDB server.
|
|
86
87
|
* @param {object} query
|
|
87
|
-
* @param {'exec' | 'arrow' | 'json'} [query.type] The query type
|
|
88
|
+
* @param {'exec' | 'arrow' | 'json' | 'create-bundle' | 'load-bundle'} [query.type] The query type.
|
|
88
89
|
* @param {string} query.sql A SQL query string.
|
|
89
90
|
* @returns the query result
|
|
90
91
|
*/
|
package/src/connectors/wasm.js
CHANGED
|
@@ -1,4 +1,20 @@
|
|
|
1
1
|
import * as duckdb from '@duckdb/duckdb-wasm';
|
|
2
|
+
import { decodeIPC } from '../util/decode-ipc.js';
|
|
3
|
+
|
|
4
|
+
// bypass duckdb-wasm query method to get Arrow IPC bytes directly
|
|
5
|
+
// https://github.com/duckdb/duckdb-wasm/issues/267#issuecomment-2252749509
|
|
6
|
+
function getArrowIPC(con, query) {
|
|
7
|
+
return new Promise((resolve, reject) => {
|
|
8
|
+
con.useUnsafe(async (bindings, conn) => {
|
|
9
|
+
try {
|
|
10
|
+
const buffer = await bindings.runQuery(conn, query);
|
|
11
|
+
resolve(buffer);
|
|
12
|
+
} catch (error) {
|
|
13
|
+
reject(error);
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
}
|
|
2
18
|
|
|
3
19
|
export function wasmConnector(options = {}) {
|
|
4
20
|
const { duckdb, connection, ...opts } = options;
|
|
@@ -45,17 +61,17 @@ export function wasmConnector(options = {}) {
|
|
|
45
61
|
/**
|
|
46
62
|
* Query the DuckDB-WASM instance.
|
|
47
63
|
* @param {object} query
|
|
48
|
-
* @param {'exec' | 'arrow' | 'json'} [query.type] The query type
|
|
64
|
+
* @param {'exec' | 'arrow' | 'json' | 'create-bundle' | 'load-bundle'} [query.type] The query type.
|
|
49
65
|
* @param {string} query.sql A SQL query string.
|
|
50
66
|
* @returns the query result
|
|
51
67
|
*/
|
|
52
68
|
query: async query => {
|
|
53
69
|
const { type, sql } = query;
|
|
54
70
|
const con = await getConnection();
|
|
55
|
-
const result = await con
|
|
71
|
+
const result = await getArrowIPC(con, sql);
|
|
56
72
|
return type === 'exec' ? undefined
|
|
57
|
-
: type === 'arrow' ? result
|
|
58
|
-
: result.toArray();
|
|
73
|
+
: type === 'arrow' ? decodeIPC(result)
|
|
74
|
+
: decodeIPC(result).toArray();
|
|
59
75
|
}
|
|
60
76
|
};
|
|
61
77
|
}
|
package/src/index.js
CHANGED
|
@@ -3,18 +3,22 @@ export { Coordinator, coordinator } from './Coordinator.js';
|
|
|
3
3
|
export { Selection, isSelection } from './Selection.js';
|
|
4
4
|
export { Param, isParam } from './Param.js';
|
|
5
5
|
export { Priority } from './QueryManager.js';
|
|
6
|
-
export { point, points, interval, intervals, match } from './SelectionClause.js';
|
|
7
6
|
|
|
8
7
|
export { restConnector } from './connectors/rest.js';
|
|
9
8
|
export { socketConnector } from './connectors/socket.js';
|
|
10
9
|
export { wasmConnector } from './connectors/wasm.js';
|
|
11
10
|
|
|
12
11
|
export {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
12
|
+
clauseInterval,
|
|
13
|
+
clauseIntervals,
|
|
14
|
+
clausePoint,
|
|
15
|
+
clausePoints,
|
|
16
|
+
clauseMatch
|
|
17
|
+
} from './SelectionClause.js';
|
|
18
|
+
|
|
19
|
+
export { decodeIPC } from './util/decode-ipc.js';
|
|
18
20
|
export { distinct } from './util/distinct.js';
|
|
21
|
+
export { isArrowTable } from './util/is-arrow-table.js';
|
|
19
22
|
export { synchronizer } from './util/synchronizer.js';
|
|
20
23
|
export { throttle } from './util/throttle.js';
|
|
24
|
+
export { toDataColumns } from './util/to-data-columns.js';
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Event dispatcher supporting asynchronous updates.
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* of the same type.
|
|
2
|
+
* Event dispatcher supporting asynchronous updates. If an event handler
|
|
3
|
+
* callback returns a Promise, the dispatcher waits for all such Promises
|
|
4
|
+
* to settle before dispatching future events of the same type.
|
|
6
5
|
*/
|
|
7
6
|
export class AsyncDispatch {
|
|
8
7
|
|
|
@@ -63,7 +62,7 @@ export class AsyncDispatch {
|
|
|
63
62
|
* queue of unemitted event values prior to enqueueing a new value.
|
|
64
63
|
* This default implementation simply returns null, indicating that
|
|
65
64
|
* any other unemitted event values should be dropped (that is, all
|
|
66
|
-
* queued events are filtered)
|
|
65
|
+
* queued events are filtered).
|
|
67
66
|
* @param {string} type The event type.
|
|
68
67
|
* @param {*} value The new event value that will be enqueued.
|
|
69
68
|
* @returns {(value: *) => boolean|null} A dispatch queue filter
|
|
@@ -83,6 +82,17 @@ export class AsyncDispatch {
|
|
|
83
82
|
entry?.queue.clear();
|
|
84
83
|
}
|
|
85
84
|
|
|
85
|
+
/**
|
|
86
|
+
* Returns a promise that resolves when any pending updates complete for
|
|
87
|
+
* the event of the given type currently being processed. The Promise will
|
|
88
|
+
* resolve immediately if the queue for the given event type is empty.
|
|
89
|
+
* @param {string} type The event type to wait for.
|
|
90
|
+
* @returns {Promise} A pending event promise.
|
|
91
|
+
*/
|
|
92
|
+
async pending(type) {
|
|
93
|
+
await this._callbacks.get(type)?.pending;
|
|
94
|
+
}
|
|
95
|
+
|
|
86
96
|
/**
|
|
87
97
|
* Emit an event value to listeners for the given event type.
|
|
88
98
|
* If a previous emit has not yet resolved, the event value
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { tableFromIPC } from '@uwdata/flechette';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Decode Arrow IPC bytes to a table instance, with an option to map date and
|
|
5
|
+
* timestamp values to JS Date objects.
|
|
6
|
+
* @param {ArrayBuffer | Uint8Array} data Arrow IPC bytes.
|
|
7
|
+
* @returns {import('@uwdata/flechette').Table} A table instance.
|
|
8
|
+
*/
|
|
9
|
+
export function decodeIPC(data) {
|
|
10
|
+
return tableFromIPC(data, { useDate: true });
|
|
11
|
+
}
|
package/src/util/field-info.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Query, asRelation, count, isNull, max, min, sql } from '@uwdata/mosaic-sql';
|
|
2
2
|
import { jsType } from './js-type.js';
|
|
3
|
-
import { convertArrowValue } from './convert-arrow.js';
|
|
4
3
|
|
|
5
4
|
export const Count = 'count';
|
|
6
5
|
export const Nulls = 'nulls';
|
|
@@ -52,20 +51,13 @@ async function getFieldInfo(mc, { table, column, stats }) {
|
|
|
52
51
|
if (!(stats?.length || stats?.size)) return info;
|
|
53
52
|
|
|
54
53
|
// query for summary stats
|
|
55
|
-
const result = await mc.query(
|
|
54
|
+
const [result] = await mc.query(
|
|
56
55
|
summarize(table, column, stats),
|
|
57
56
|
{ persist: true }
|
|
58
57
|
);
|
|
59
58
|
|
|
60
|
-
// extract summary stats, copy to field info
|
|
61
|
-
|
|
62
|
-
const { name } = result.schema.fields[i];
|
|
63
|
-
const child = result.getChildAt(i);
|
|
64
|
-
const convert = convertArrowValue(child.type);
|
|
65
|
-
info[name] = convert(child.get(0));
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
return info;
|
|
59
|
+
// extract summary stats, copy to field info, and return
|
|
60
|
+
return Object.assign(info, result);
|
|
69
61
|
}
|
|
70
62
|
|
|
71
63
|
async function getTableInfo(mc, table) {
|