-
Notifications
You must be signed in to change notification settings - Fork 62
/
Copy pathfield-info.js
83 lines (74 loc) · 2.2 KB
/
field-info.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
import { AggregateNode, Query, asTableRef, count, isNull, max, min, sql } from '@uwdata/mosaic-sql';
import { jsType } from './js-type.js';
export const Count = 'count';
export const Nulls = 'nulls';
export const Max = 'max';
export const Min = 'min';
export const Distinct = 'distinct';
export const Stats = { Count, Nulls, Max, Min, Distinct };
/**
* @type {Record<string, (column: string) => AggregateNode>}
*/
const statMap = {
[Count]: count,
[Distinct]: column => count(column).distinct(),
[Max]: max,
[Min]: min,
[Nulls]: column => count().where(isNull(column))
};
/**
*
* @param {string} table
* @param {string} column
* @param {string[]|Set<string>} stats
* @returns
*/
function summarize(table, column, stats) {
return Query
.from(table)
.select(Array.from(stats, s => ({[s]: statMap[s](column)})));
}
export async function queryFieldInfo(mc, fields) {
if (fields.length === 1 && fields[0].column === '*') {
return getTableInfo(mc, fields[0].table);
} else {
return (await Promise
.all(fields.map(f => getFieldInfo(mc, f))))
.filter(x => x);
}
}
async function getFieldInfo(mc, { table, column, stats }) {
// generate and issue a query for field metadata info
// use GROUP BY ALL to differentiate & consolidate aggregates
const q = Query
.from({ source: table })
.select({ column })
.groupby(column.aggregate ? sql`ALL` : []);
const [desc] = Array.from(await mc.query(Query.describe(q)));
const info = {
table,
column: `${column}`,
sqlType: desc.column_type,
type: jsType(desc.column_type),
nullable: desc.null === 'YES'
};
// no need for summary statistics
if (!(stats?.length || stats?.size)) return info;
// query for summary stats
const [result] = await mc.query(
summarize(table, column, stats),
{ persist: true }
);
// extract summary stats, copy to field info, and return
return Object.assign(info, result);
}
async function getTableInfo(mc, table) {
const result = await mc.query(`DESCRIBE ${asTableRef(table)}`);
return Array.from(result).map(desc => ({
table,
column: desc.column_name,
sqlType: desc.column_type,
type: jsType(desc.column_type),
nullable: desc.null === 'YES'
}));
}