@thi.ng/column-store 0.1.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/LICENSE +201 -0
- package/README.md +428 -0
- package/api.d.ts +101 -0
- package/api.js +18 -0
- package/bitmap.d.ts +35 -0
- package/bitmap.js +104 -0
- package/columns/acolumn.d.ts +19 -0
- package/columns/acolumn.js +55 -0
- package/columns/array.d.ts +19 -0
- package/columns/array.js +62 -0
- package/columns/dict-array.d.ts +25 -0
- package/columns/dict-array.js +79 -0
- package/columns/dict.d.ts +25 -0
- package/columns/dict.js +82 -0
- package/columns/plain.d.ts +17 -0
- package/columns/plain.js +42 -0
- package/columns/typedarray.d.ts +22 -0
- package/columns/typedarray.js +84 -0
- package/index.d.ts +5 -0
- package/index.js +4 -0
- package/internal/checks.d.ts +8 -0
- package/internal/checks.js +15 -0
- package/internal/replace.d.ts +5 -0
- package/internal/replace.js +16 -0
- package/internal/serialize.d.ts +7 -0
- package/internal/serialize.js +8 -0
- package/package.json +127 -0
- package/query.d.ts +43 -0
- package/query.js +271 -0
- package/table.d.ts +51 -0
- package/table.js +176 -0
package/table.js
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { isArray } from "@thi.ng/checks/is-array";
|
|
2
|
+
import { illegalArgs } from "@thi.ng/errors/illegal-arguments";
|
|
3
|
+
import {
|
|
4
|
+
FLAG_BITMAP,
|
|
5
|
+
FLAG_DICT,
|
|
6
|
+
FLAG_RLE,
|
|
7
|
+
FLAG_UNIQUE
|
|
8
|
+
} from "./api.js";
|
|
9
|
+
import { ArrayColumn } from "./columns/array.js";
|
|
10
|
+
import { DictArrayColumn } from "./columns/dict-array.js";
|
|
11
|
+
import { DictColumn } from "./columns/dict.js";
|
|
12
|
+
import { PlainColumn } from "./columns/plain.js";
|
|
13
|
+
import { TypedArrayColumn } from "./columns/typedarray.js";
|
|
14
|
+
import { __columnError } from "./internal/checks.js";
|
|
15
|
+
import { Query } from "./query.js";
|
|
16
|
+
class Table {
|
|
17
|
+
opts;
|
|
18
|
+
schema = {};
|
|
19
|
+
columns = {};
|
|
20
|
+
length = 0;
|
|
21
|
+
static load(serialized, opts) {
|
|
22
|
+
const table = new Table(serialized.schema, opts);
|
|
23
|
+
table.length = serialized.length;
|
|
24
|
+
for (let id in table.columns) {
|
|
25
|
+
table.columns[id].load(serialized.columns[id]);
|
|
26
|
+
}
|
|
27
|
+
return table;
|
|
28
|
+
}
|
|
29
|
+
constructor(schema, opts) {
|
|
30
|
+
this.opts = { ...opts };
|
|
31
|
+
for (let id in schema) this.addColumn(id, schema[id]);
|
|
32
|
+
}
|
|
33
|
+
query(terms) {
|
|
34
|
+
return new Query(this, terms);
|
|
35
|
+
}
|
|
36
|
+
addColumn(id, spec) {
|
|
37
|
+
if (this.columns[id]) __columnError(id, "already exists");
|
|
38
|
+
const $spec = {
|
|
39
|
+
cardinality: [1, 1],
|
|
40
|
+
flags: 0,
|
|
41
|
+
...spec
|
|
42
|
+
};
|
|
43
|
+
this.validateColumnSpec(id, $spec);
|
|
44
|
+
this.schema[id] = $spec;
|
|
45
|
+
this.columns[id] = COLUMN_TYPES[spec.type].impl(this, id, $spec);
|
|
46
|
+
}
|
|
47
|
+
removeColumn(id) {
|
|
48
|
+
if (this.columns[id]) return false;
|
|
49
|
+
delete this.columns[id];
|
|
50
|
+
delete this.schema[id];
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
*[Symbol.iterator]() {
|
|
54
|
+
for (let i = 0; i < this.length; i++) yield this.getRow(i);
|
|
55
|
+
}
|
|
56
|
+
reindex() {
|
|
57
|
+
for (let id in this.columns) this.columns[id].reindex();
|
|
58
|
+
}
|
|
59
|
+
addRow(row) {
|
|
60
|
+
this.validateRow(row);
|
|
61
|
+
const { columns, length: rowID } = this;
|
|
62
|
+
for (let id in columns) {
|
|
63
|
+
columns[id].setRow(rowID, row[id]);
|
|
64
|
+
}
|
|
65
|
+
this.length++;
|
|
66
|
+
}
|
|
67
|
+
addRows(rows) {
|
|
68
|
+
for (let row of rows) this.addRow(row);
|
|
69
|
+
}
|
|
70
|
+
updateRow(i, row) {
|
|
71
|
+
if (i < 0 || i >= this.length) illegalArgs(`row ID: ${i}`);
|
|
72
|
+
this.validateRow(row);
|
|
73
|
+
for (let id in this.columns) {
|
|
74
|
+
this.columns[id].setRow(i, row[id]);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
removeRow(i) {
|
|
78
|
+
if (i < 0 || i >= this.length) illegalArgs(`row ID: ${i}`);
|
|
79
|
+
for (let id in this.columns) {
|
|
80
|
+
this.columns[id].removeRow(i);
|
|
81
|
+
}
|
|
82
|
+
this.length--;
|
|
83
|
+
}
|
|
84
|
+
getRow(i, safe = true) {
|
|
85
|
+
if (safe && (i < 0 || i >= this.length)) return;
|
|
86
|
+
const row = {};
|
|
87
|
+
for (let id in this.columns) {
|
|
88
|
+
row[id] = this.columns[id].getRow(i);
|
|
89
|
+
}
|
|
90
|
+
return row;
|
|
91
|
+
}
|
|
92
|
+
getPartialRow(i, columns, safe = true) {
|
|
93
|
+
if (safe && (i < 0 || i >= this.length)) return;
|
|
94
|
+
const row = {};
|
|
95
|
+
for (let id of columns) {
|
|
96
|
+
row[id] = this.columns[id]?.getRow(i);
|
|
97
|
+
}
|
|
98
|
+
return row;
|
|
99
|
+
}
|
|
100
|
+
validateRow(row) {
|
|
101
|
+
const { columns } = this;
|
|
102
|
+
for (let id in columns) {
|
|
103
|
+
if (!columns[id].validate(row[id]))
|
|
104
|
+
__columnError(id, `invalid value`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
validateColumnSpec(id, spec) {
|
|
108
|
+
const def = COLUMN_TYPES[spec.type];
|
|
109
|
+
if (!def) __columnError(id, `unknown type: ${spec.type}`);
|
|
110
|
+
if (spec.flags & ~(def.flags ?? 0))
|
|
111
|
+
__columnError(
|
|
112
|
+
id,
|
|
113
|
+
`unsupported flags for column type: ${spec.type}`
|
|
114
|
+
);
|
|
115
|
+
const [min, max] = spec.cardinality;
|
|
116
|
+
if (max < min)
|
|
117
|
+
__columnError(id, `wrong cardinality: ${spec.cardinality}`);
|
|
118
|
+
if (min < def.cardinality[0] || max > def.cardinality[1])
|
|
119
|
+
__columnError(id, `wrong cardimality`);
|
|
120
|
+
if (def.required && min === 0 && spec.default == null)
|
|
121
|
+
__columnError(id, `missing default value`);
|
|
122
|
+
if (spec.default != null) {
|
|
123
|
+
if (max > 1 !== isArray(spec.default))
|
|
124
|
+
__columnError(id, `default value`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
toJSON() {
|
|
128
|
+
return {
|
|
129
|
+
schema: this.schema,
|
|
130
|
+
columns: this.columns,
|
|
131
|
+
length: this.length
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
const $typed = {
|
|
136
|
+
impl: (table, id) => new TypedArrayColumn(id, table),
|
|
137
|
+
flags: FLAG_BITMAP | FLAG_RLE,
|
|
138
|
+
cardinality: [0, 1],
|
|
139
|
+
required: true
|
|
140
|
+
};
|
|
141
|
+
const $float = { ...$typed, flags: FLAG_BITMAP };
|
|
142
|
+
const $untyped = {
|
|
143
|
+
impl: (table, id, { flags, cardinality: [min, max], default: d }) => {
|
|
144
|
+
const isDict = flags & FLAG_DICT;
|
|
145
|
+
if (flags & FLAG_RLE) {
|
|
146
|
+
if (!isDict || max > 1 || min === 0 && d == null) {
|
|
147
|
+
__columnError(id, `RLE encoding not supported`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return max > 1 ? new (isDict ? DictArrayColumn : ArrayColumn)(id, table) : new (isDict ? DictColumn : PlainColumn)(id, table);
|
|
151
|
+
},
|
|
152
|
+
flags: FLAG_BITMAP | FLAG_DICT | FLAG_UNIQUE | FLAG_RLE,
|
|
153
|
+
cardinality: [0, -1 >>> 0]
|
|
154
|
+
};
|
|
155
|
+
const COLUMN_TYPES = {
|
|
156
|
+
u8: $typed,
|
|
157
|
+
i8: $typed,
|
|
158
|
+
u16: $typed,
|
|
159
|
+
i16: $typed,
|
|
160
|
+
u32: $typed,
|
|
161
|
+
i32: $typed,
|
|
162
|
+
f32: $float,
|
|
163
|
+
f64: $float,
|
|
164
|
+
num: $untyped,
|
|
165
|
+
str: $untyped
|
|
166
|
+
};
|
|
167
|
+
const registerColumnType = (type, spec) => {
|
|
168
|
+
if (COLUMN_TYPES[type])
|
|
169
|
+
illegalArgs(`column type ${type} already registered`);
|
|
170
|
+
COLUMN_TYPES[type] = spec;
|
|
171
|
+
};
|
|
172
|
+
export {
|
|
173
|
+
COLUMN_TYPES,
|
|
174
|
+
Table,
|
|
175
|
+
registerColumnType
|
|
176
|
+
};
|