@thi.ng/column-store 0.6.1 → 0.7.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.
- package/README.md +13 -3
- package/api.d.ts +3 -1
- package/columns/acolumn.d.ts +1 -0
- package/columns/dict-tuple.d.ts +1 -0
- package/columns/dict-tuple.js +14 -0
- package/columns/dict.d.ts +1 -0
- package/columns/dict.js +12 -0
- package/columns/plain.d.ts +1 -0
- package/columns/plain.js +13 -1
- package/columns/tuple.d.ts +1 -0
- package/columns/tuple.js +13 -0
- package/columns/typedarray.d.ts +2 -0
- package/columns/typedarray.js +16 -7
- package/columns/vector.d.ts +3 -1
- package/columns/vector.js +22 -9
- package/internal/serialize.d.ts +1 -1
- package/package.json +2 -2
- package/query.d.ts +5 -0
- package/query.js +54 -38
- package/table.d.ts +1 -1
- package/table.js +5 -1
package/README.md
CHANGED
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
- [Serialization options](#serialization-options)
|
|
22
22
|
- [Custom column types](#custom-column-types)
|
|
23
23
|
- [Cardinality](#cardinality)
|
|
24
|
+
- [Differences between tuple and vector column types](#differences-between-tuple-and-vector-column-types)
|
|
24
25
|
- [Default values](#default-values)
|
|
25
26
|
- [Flags](#flags)
|
|
26
27
|
- [FLAG_BITMAP](#flag_bitmap)
|
|
@@ -143,10 +144,11 @@ Note: Booleans and `BigInt`s are still unsupported, but being worked on...
|
|
|
143
144
|
| `i16` | 16bit signed int | ❌ | ✅ |
|
|
144
145
|
| `u32` | 32bit unsigned int | ❌ | ✅ |
|
|
145
146
|
| `i32` | 32bit signed int | ❌ | ✅ |
|
|
146
|
-
| `f32` | 32bit float | ❌ |
|
|
147
|
-
| `f64` | 64bit float | ❌ |
|
|
147
|
+
| `f32` | 32bit float | ❌ | ✅ <sup>(2)</sup> |
|
|
148
|
+
| `f64` | 64bit float | ❌ | ✅ <sup>(2)</sup> |
|
|
148
149
|
|
|
149
150
|
- <sup>(1)</sup> only if max. cardinality is 1, [further information](#flag_rle)
|
|
151
|
+
- <sup>(2)</sup> simple RLE only, [further information](#flag_rle)
|
|
150
152
|
|
|
151
153
|
### Vector column types
|
|
152
154
|
|
|
@@ -223,6 +225,14 @@ key of the column spec. The following presets are provided:
|
|
|
223
225
|
| `ONE_PLUS` | `[1, (2**32)-1]` | One or more values (always expects tuples) |
|
|
224
226
|
| `ZERO_PLUS` | `[0, (2**32)-1]` | Zero or more values (always expects tuples) |
|
|
225
227
|
|
|
228
|
+
#### Differences between tuple and vector column types
|
|
229
|
+
|
|
230
|
+
| **Feature** | **Tuples** | **Vectors** |
|
|
231
|
+
|----------------------------------|-----------------------|-------------|
|
|
232
|
+
| Optional (without default value) | ✅ | ❌ |
|
|
233
|
+
| Flexible size | ✅ | ❌ |
|
|
234
|
+
| Query ops match... | Individual components | Full vector |
|
|
235
|
+
|
|
226
236
|
### Default values
|
|
227
237
|
|
|
228
238
|
Default values can be specified for columns with a minimum cardinality of 1 or
|
|
@@ -448,7 +458,7 @@ For Node.js REPL:
|
|
|
448
458
|
const cs = await import("@thi.ng/column-store");
|
|
449
459
|
```
|
|
450
460
|
|
|
451
|
-
Package sizes (brotli'd, pre-treeshake): ESM: 5.
|
|
461
|
+
Package sizes (brotli'd, pre-treeshake): ESM: 5.71 KB
|
|
452
462
|
|
|
453
463
|
## Dependencies
|
|
454
464
|
|
package/api.d.ts
CHANGED
|
@@ -149,6 +149,8 @@ export interface IColumn {
|
|
|
149
149
|
*/
|
|
150
150
|
setRow(i: number, value: any): void;
|
|
151
151
|
removeRow(i: number): void;
|
|
152
|
+
/** @internal */
|
|
153
|
+
ensureRows(): void;
|
|
152
154
|
encode(value: any): any;
|
|
153
155
|
decode(value: any): any;
|
|
154
156
|
replaceValue(currValue: any, newValue: any): boolean;
|
|
@@ -178,7 +180,7 @@ export interface QueryTerm<T extends Row> {
|
|
|
178
180
|
value: any;
|
|
179
181
|
params?: any;
|
|
180
182
|
}
|
|
181
|
-
export type QueryTermOp = Fn3<QueryCtx<any>, QueryTerm<any>, Maybe<IColumn>,
|
|
183
|
+
export type QueryTermOp = Fn3<QueryCtx<any>, QueryTerm<any>, Maybe<IColumn>, boolean>;
|
|
182
184
|
export interface QueryTermOpSpec {
|
|
183
185
|
/**
|
|
184
186
|
* Default mode is: "col";
|
package/columns/acolumn.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ export declare abstract class AColumn<T extends Row = Row> implements IColumn {
|
|
|
16
16
|
abstract validate(value: any): boolean;
|
|
17
17
|
abstract setRow(i: number, value: any): void;
|
|
18
18
|
abstract removeRow(i: number): void;
|
|
19
|
+
abstract ensureRows(): void;
|
|
19
20
|
abstract replaceValue(currValue: any, newValue: any): boolean;
|
|
20
21
|
abstract valueKey(x: any): any;
|
|
21
22
|
abstract getRow(i: number): any;
|
package/columns/dict-tuple.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export declare class DictTupleColumn<T extends Row = Row> extends AColumn<T> {
|
|
|
10
10
|
encode(value: any): (number | null)[];
|
|
11
11
|
decode(value: any[]): any[];
|
|
12
12
|
validate(value: any): boolean;
|
|
13
|
+
ensureRows(): void;
|
|
13
14
|
setRow(i: number, value: any[]): void;
|
|
14
15
|
getRow(i: number): any[] | null;
|
|
15
16
|
getRowKey(i: number): number[] | null;
|
package/columns/dict-tuple.js
CHANGED
|
@@ -32,6 +32,20 @@ class DictTupleColumn extends AColumn {
|
|
|
32
32
|
validate(value) {
|
|
33
33
|
return __validateArrayValue(this.spec, value);
|
|
34
34
|
}
|
|
35
|
+
ensureRows() {
|
|
36
|
+
const {
|
|
37
|
+
bitmap,
|
|
38
|
+
spec: { default: value },
|
|
39
|
+
table: { length: n },
|
|
40
|
+
values
|
|
41
|
+
} = this;
|
|
42
|
+
const $value = value != null ? this.dict.addAll(value) : null;
|
|
43
|
+
values.length = n;
|
|
44
|
+
values.fill($value, 0, n);
|
|
45
|
+
if (bitmap && $value) {
|
|
46
|
+
for (let x of $value) bitmap.ensure(x).fill(1, 0, n);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
35
49
|
setRow(i, value) {
|
|
36
50
|
value = this.ensureValue(value);
|
|
37
51
|
const { values, dict, bitmap } = this;
|
package/columns/dict.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export declare class DictColumn<T extends Row = Row> extends AColumn<T> {
|
|
|
10
10
|
encode(value: any): number | null;
|
|
11
11
|
decode(value: any): any;
|
|
12
12
|
validate(value: any): boolean;
|
|
13
|
+
ensureRows(): void;
|
|
13
14
|
setRow(i: number, value: any): void;
|
|
14
15
|
getRow(i: number): any;
|
|
15
16
|
getRowKey(i: number): number | null;
|
package/columns/dict.js
CHANGED
|
@@ -34,6 +34,18 @@ class DictColumn extends AColumn {
|
|
|
34
34
|
validate(value) {
|
|
35
35
|
return __validateValue(this.spec, value);
|
|
36
36
|
}
|
|
37
|
+
ensureRows() {
|
|
38
|
+
const {
|
|
39
|
+
bitmap,
|
|
40
|
+
spec: { default: value },
|
|
41
|
+
table: { length: n },
|
|
42
|
+
values
|
|
43
|
+
} = this;
|
|
44
|
+
const $value = value != null ? this.dict.add(value) : null;
|
|
45
|
+
values.length = n;
|
|
46
|
+
values.fill($value, 0, n);
|
|
47
|
+
if (bitmap && $value != null) bitmap.ensure($value).fill(1, 0, n);
|
|
48
|
+
}
|
|
37
49
|
setRow(i, value) {
|
|
38
50
|
value = this.ensureValue(value);
|
|
39
51
|
const { values, bitmap } = this;
|
package/columns/plain.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export declare class PlainColumn<T extends Row = Row> extends AColumn<T> {
|
|
|
4
4
|
values: any[];
|
|
5
5
|
readonly isArray = false;
|
|
6
6
|
load({ values }: SerializedColumn): void;
|
|
7
|
+
ensureRows(): void;
|
|
7
8
|
validate(value: any): boolean;
|
|
8
9
|
setRow(i: number, value: any): void;
|
|
9
10
|
getRow(i: number): any;
|
package/columns/plain.js
CHANGED
|
@@ -11,13 +11,25 @@ class PlainColumn extends AColumn {
|
|
|
11
11
|
this.values = this.spec.flags & FLAG_RLE ? Array.from(decodeSimple(values)) : values;
|
|
12
12
|
this.reindex();
|
|
13
13
|
}
|
|
14
|
+
ensureRows() {
|
|
15
|
+
const {
|
|
16
|
+
bitmap,
|
|
17
|
+
spec: { default: value },
|
|
18
|
+
table: { length: n },
|
|
19
|
+
values
|
|
20
|
+
} = this;
|
|
21
|
+
values.length = n;
|
|
22
|
+
values.fill(value ?? null, 0, n);
|
|
23
|
+
if (bitmap && value != null) bitmap.ensure(value).fill(1, 0, n);
|
|
24
|
+
}
|
|
14
25
|
validate(value) {
|
|
15
26
|
return __validateValue(this.spec, value);
|
|
16
27
|
}
|
|
17
28
|
setRow(i, value) {
|
|
18
29
|
const { values, bitmap } = this;
|
|
19
30
|
const old = values[i];
|
|
20
|
-
|
|
31
|
+
value = this.ensureValue(value);
|
|
32
|
+
values[i] = value;
|
|
21
33
|
if (bitmap) {
|
|
22
34
|
if (old != null) bitmap.clearBit(old, i);
|
|
23
35
|
if (value != null) bitmap.setBit(value, i);
|
package/columns/tuple.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ export declare class TupleColumn<T extends Row = Row> extends AColumn<T> {
|
|
|
7
7
|
load(spec: SerializedColumn): void;
|
|
8
8
|
encode(value: any): any[];
|
|
9
9
|
validate(value: any): boolean;
|
|
10
|
+
ensureRows(): void;
|
|
10
11
|
setRow(i: number, value: any[]): void;
|
|
11
12
|
getRow(i: number): Nullable<number[]>;
|
|
12
13
|
getRowKey(i: number): Nullable<number[]>;
|
package/columns/tuple.js
CHANGED
|
@@ -16,6 +16,19 @@ class TupleColumn extends AColumn {
|
|
|
16
16
|
validate(value) {
|
|
17
17
|
return __validateArrayValue(this.spec, value);
|
|
18
18
|
}
|
|
19
|
+
ensureRows() {
|
|
20
|
+
const {
|
|
21
|
+
bitmap,
|
|
22
|
+
spec: { default: value },
|
|
23
|
+
table: { length: n },
|
|
24
|
+
values
|
|
25
|
+
} = this;
|
|
26
|
+
values.length = n;
|
|
27
|
+
values.fill(value ?? null, 0, n);
|
|
28
|
+
if (bitmap && value) {
|
|
29
|
+
for (let x of value) bitmap.ensure(x).fill(1, 0, n);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
19
32
|
setRow(i, value) {
|
|
20
33
|
value = this.ensureValue(value);
|
|
21
34
|
const { values, bitmap } = this;
|
package/columns/typedarray.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export declare class TypedArrayColumn<T extends Row = Row> extends AColumn<T> {
|
|
|
11
11
|
constructor(id: ColumnID<T>, table: Table<T>);
|
|
12
12
|
load({ values }: SerializedColumn): void;
|
|
13
13
|
validate(value: any): boolean;
|
|
14
|
+
ensureRows(): void;
|
|
14
15
|
setRow(i: number, value: any): void;
|
|
15
16
|
getRow(i: number): number;
|
|
16
17
|
getRowKey(i: number): number;
|
|
@@ -22,5 +23,6 @@ export declare class TypedArrayColumn<T extends Row = Row> extends AColumn<T> {
|
|
|
22
23
|
toJSON(): {
|
|
23
24
|
values: any[];
|
|
24
25
|
};
|
|
26
|
+
protected ensureCapacity(capacity: number, copy?: boolean): void;
|
|
25
27
|
}
|
|
26
28
|
//# sourceMappingURL=typedarray.d.ts.map
|
package/columns/typedarray.js
CHANGED
|
@@ -28,15 +28,15 @@ class TypedArrayColumn extends AColumn {
|
|
|
28
28
|
validate(value) {
|
|
29
29
|
return isNumber(value) && value >= this.limit[0] && value <= this.limit[1] || value == null && this.spec.default != null;
|
|
30
30
|
}
|
|
31
|
+
ensureRows() {
|
|
32
|
+
const n = this.table.length;
|
|
33
|
+
this.ensureCapacity(n, false);
|
|
34
|
+
this.values.fill(this.spec.default, 0, n);
|
|
35
|
+
this.bitmap?.ensure(this.spec.default).fill(1, 0, n);
|
|
36
|
+
}
|
|
31
37
|
setRow(i, value) {
|
|
32
38
|
value = this.ensureValue(value);
|
|
33
|
-
|
|
34
|
-
if (i >= len) {
|
|
35
|
-
while (i >= len) len <<= 1;
|
|
36
|
-
const tmp = typedArray(this.type, len);
|
|
37
|
-
tmp.set(this.values);
|
|
38
|
-
this.values = tmp;
|
|
39
|
-
}
|
|
39
|
+
this.ensureCapacity(i);
|
|
40
40
|
const { values, bitmap } = this;
|
|
41
41
|
const old = values[i];
|
|
42
42
|
values[i] = value;
|
|
@@ -98,6 +98,15 @@ class TypedArrayColumn extends AColumn {
|
|
|
98
98
|
this.type
|
|
99
99
|
);
|
|
100
100
|
}
|
|
101
|
+
ensureCapacity(capacity, copy = true) {
|
|
102
|
+
let len = this.values.length;
|
|
103
|
+
if (capacity >= len) {
|
|
104
|
+
while (capacity >= len) len <<= 1;
|
|
105
|
+
const tmp = typedArray(this.type, len);
|
|
106
|
+
copy && tmp.set(this.values);
|
|
107
|
+
this.values = tmp;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
101
110
|
}
|
|
102
111
|
export {
|
|
103
112
|
TypedArrayColumn
|
package/columns/vector.d.ts
CHANGED
|
@@ -12,8 +12,9 @@ export declare class VectorColumn<T extends Row = Row> extends AColumn<T> {
|
|
|
12
12
|
constructor(id: ColumnID<T>, table: Table<T>);
|
|
13
13
|
load({ values }: SerializedColumn): void;
|
|
14
14
|
validate(value: any): boolean;
|
|
15
|
+
ensureRows(): void;
|
|
15
16
|
setRow(i: number, value: any): void;
|
|
16
|
-
getRow(i: number): Float32Array<ArrayBufferLike> | Float64Array<ArrayBufferLike> | Int8Array<ArrayBufferLike> | Int16Array<ArrayBufferLike> | Int32Array<ArrayBufferLike> | Uint8Array<ArrayBufferLike> | Uint8ClampedArray<ArrayBufferLike> | Uint16Array<ArrayBufferLike
|
|
17
|
+
getRow(i: number): Uint32Array<ArrayBufferLike> | Float32Array<ArrayBufferLike> | Float64Array<ArrayBufferLike> | Int8Array<ArrayBufferLike> | Int16Array<ArrayBufferLike> | Int32Array<ArrayBufferLike> | Uint8Array<ArrayBufferLike> | Uint8ClampedArray<ArrayBufferLike> | Uint16Array<ArrayBufferLike>;
|
|
17
18
|
getRowKey(i: number): string;
|
|
18
19
|
valueKey(value: any): string | string[];
|
|
19
20
|
removeRow(i: number): void;
|
|
@@ -23,5 +24,6 @@ export declare class VectorColumn<T extends Row = Row> extends AColumn<T> {
|
|
|
23
24
|
toJSON(): {
|
|
24
25
|
values: any[];
|
|
25
26
|
};
|
|
27
|
+
protected ensureCapacity(capacity: number, copy?: boolean): void;
|
|
26
28
|
}
|
|
27
29
|
//# sourceMappingURL=vector.d.ts.map
|
package/columns/vector.js
CHANGED
|
@@ -31,22 +31,25 @@ class VectorColumn extends AColumn {
|
|
|
31
31
|
validate(value) {
|
|
32
32
|
return isArrayLike(value) && value.length == this.size || value == null && this.spec.default != null;
|
|
33
33
|
}
|
|
34
|
+
ensureRows() {
|
|
35
|
+
const {
|
|
36
|
+
size,
|
|
37
|
+
spec: { default: value },
|
|
38
|
+
table: { length: n }
|
|
39
|
+
} = this;
|
|
40
|
+
this.ensureCapacity(n, false);
|
|
41
|
+
for (let i = 0; i < n; i++) this.values.set(value, i * size);
|
|
42
|
+
this.bitmap?.ensure(this.valueKey(value)).fill(1, 0, n);
|
|
43
|
+
}
|
|
34
44
|
setRow(i, value) {
|
|
35
45
|
value = this.ensureValue(value);
|
|
36
|
-
|
|
37
|
-
let len = this.values.length;
|
|
38
|
-
if (j >= len) {
|
|
39
|
-
while (j >= len) len <<= 1;
|
|
40
|
-
const tmp = typedArray(this.type, len);
|
|
41
|
-
tmp.set(this.values);
|
|
42
|
-
this.values = tmp;
|
|
43
|
-
}
|
|
46
|
+
this.ensureCapacity(i);
|
|
44
47
|
const { values, bitmap } = this;
|
|
45
48
|
if (bitmap) {
|
|
46
49
|
bitmap.clearBit(this.getRowKey(i), i);
|
|
47
50
|
bitmap.setBit(this.valueKey(value), i);
|
|
48
51
|
}
|
|
49
|
-
values.set(value,
|
|
52
|
+
values.set(value, i * this.size);
|
|
50
53
|
}
|
|
51
54
|
getRow(i) {
|
|
52
55
|
const { size } = this;
|
|
@@ -121,6 +124,16 @@ class VectorColumn extends AColumn {
|
|
|
121
124
|
this.type
|
|
122
125
|
);
|
|
123
126
|
}
|
|
127
|
+
ensureCapacity(capacity, copy = true) {
|
|
128
|
+
capacity *= this.size;
|
|
129
|
+
let len = this.values.length;
|
|
130
|
+
if (capacity >= len) {
|
|
131
|
+
while (capacity >= len) len <<= 1;
|
|
132
|
+
const tmp = typedArray(this.type, len);
|
|
133
|
+
copy && tmp.set(this.values);
|
|
134
|
+
this.values = tmp;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
124
137
|
}
|
|
125
138
|
export {
|
|
126
139
|
VectorColumn
|
package/internal/serialize.d.ts
CHANGED
|
@@ -11,5 +11,5 @@ export declare const __serializeTyped: ($values: NumericArray, spec: ColumnSpec,
|
|
|
11
11
|
values: any[];
|
|
12
12
|
};
|
|
13
13
|
/** @internal */
|
|
14
|
-
export declare const __deserializeTyped: (type: Type, flags: number, values: number[]) => Float32Array<ArrayBufferLike> | Float64Array<ArrayBufferLike> | Int8Array<ArrayBufferLike> | Int16Array<ArrayBufferLike> | Int32Array<ArrayBufferLike> | Uint8Array<ArrayBufferLike> | Uint8ClampedArray<ArrayBufferLike> | Uint16Array<ArrayBufferLike
|
|
14
|
+
export declare const __deserializeTyped: (type: Type, flags: number, values: number[]) => Uint32Array<ArrayBufferLike> | Float32Array<ArrayBufferLike> | Float64Array<ArrayBufferLike> | Int8Array<ArrayBufferLike> | Int16Array<ArrayBufferLike> | Int32Array<ArrayBufferLike> | Uint8Array<ArrayBufferLike> | Uint8ClampedArray<ArrayBufferLike> | Uint16Array<ArrayBufferLike>;
|
|
15
15
|
//# sourceMappingURL=serialize.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thi.ng/column-store",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"description": "In-memory column store database with customizable column types, extensible query engine, bitfield indexing for query acceleration, JSON serialization with optional RLE compression",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "./index.js",
|
|
@@ -126,5 +126,5 @@
|
|
|
126
126
|
"status": "alpha",
|
|
127
127
|
"year": 2025
|
|
128
128
|
},
|
|
129
|
-
"gitHead": "
|
|
129
|
+
"gitHead": "3e53df924e859a37127070b16960c8c6242adeaa\n"
|
|
130
130
|
}
|
package/query.d.ts
CHANGED
|
@@ -27,6 +27,11 @@ export declare class QueryCtx<T extends Row> {
|
|
|
27
27
|
readonly size: number;
|
|
28
28
|
bitmap?: Uint32Array;
|
|
29
29
|
constructor(query: Query<T>);
|
|
30
|
+
/**
|
|
31
|
+
* If a bitmap is already present, yield iterator of only currently selected
|
|
32
|
+
* row IDs, otherwise yields row IDs in `[0,table.length)` range.
|
|
33
|
+
*/
|
|
34
|
+
[Symbol.iterator](): Generator<number, void, unknown>;
|
|
30
35
|
clear(): void;
|
|
31
36
|
makeMask(seed?: Uint32Array): Uint32Array<ArrayBuffer>;
|
|
32
37
|
/**
|
package/query.js
CHANGED
|
@@ -78,11 +78,10 @@ class Query {
|
|
|
78
78
|
`query op: ${term.type} requires a column name given`
|
|
79
79
|
);
|
|
80
80
|
}
|
|
81
|
-
op.fn(ctx, term, column);
|
|
81
|
+
if (!op.fn(ctx, term, column)) return;
|
|
82
82
|
}
|
|
83
83
|
if (ctx.bitmap) {
|
|
84
|
-
for (let i of
|
|
85
|
-
yield table.getRow(i, false, true);
|
|
84
|
+
for (let i of ctx) yield table.getRow(i, false, true);
|
|
86
85
|
}
|
|
87
86
|
}
|
|
88
87
|
}
|
|
@@ -95,6 +94,18 @@ class QueryCtx {
|
|
|
95
94
|
table;
|
|
96
95
|
size;
|
|
97
96
|
bitmap;
|
|
97
|
+
/**
|
|
98
|
+
* If a bitmap is already present, yield iterator of only currently selected
|
|
99
|
+
* row IDs, otherwise yields row IDs in `[0,table.length)` range.
|
|
100
|
+
*/
|
|
101
|
+
*[Symbol.iterator]() {
|
|
102
|
+
const n = this.table.length;
|
|
103
|
+
if (this.bitmap) {
|
|
104
|
+
yield* new Bitfield(this.bitmap).ones(n);
|
|
105
|
+
} else {
|
|
106
|
+
for (let i = 0; i < n; i++) yield i;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
98
109
|
clear() {
|
|
99
110
|
this.bitmap = void 0;
|
|
100
111
|
}
|
|
@@ -148,7 +159,7 @@ const execBitOr = (ctx, term, column) => {
|
|
|
148
159
|
const b = bitmap.index.get(k)?.buffer;
|
|
149
160
|
if (!b) continue;
|
|
150
161
|
if (mask) {
|
|
151
|
-
for (let i = 0
|
|
162
|
+
for (let i = 0, n = b.length; i < n; i++) mask[i] |= b[i];
|
|
152
163
|
} else mask = ctx.makeMask(b);
|
|
153
164
|
}
|
|
154
165
|
} else {
|
|
@@ -157,15 +168,16 @@ const execBitOr = (ctx, term, column) => {
|
|
|
157
168
|
}
|
|
158
169
|
if (mask) {
|
|
159
170
|
term.type === "nor" ? ctx.mergeInvMask(mask) : ctx.mergeMask(mask);
|
|
171
|
+
return true;
|
|
160
172
|
}
|
|
173
|
+
return false;
|
|
161
174
|
};
|
|
162
175
|
const execOr = (ctx, term, column) => {
|
|
163
|
-
const n = ctx.table.length;
|
|
164
176
|
const key = column.valueKey(term.value);
|
|
165
177
|
const pred = column.isArray ? (row, k) => row.includes(k) : (row, k) => row === k;
|
|
166
178
|
let mask;
|
|
167
179
|
for (let k of isArray(key) ? key : [key]) {
|
|
168
|
-
for (let i
|
|
180
|
+
for (let i of ctx) {
|
|
169
181
|
if (pred(column.getRowKey(i), k)) {
|
|
170
182
|
if (!mask) mask = ctx.makeMask();
|
|
171
183
|
mask[i >>> 5] |= 1 << (i & 31);
|
|
@@ -174,15 +186,15 @@ const execOr = (ctx, term, column) => {
|
|
|
174
186
|
}
|
|
175
187
|
if (mask) {
|
|
176
188
|
term.type === "nor" ? ctx.mergeInvMask(mask) : ctx.mergeMask(mask);
|
|
189
|
+
return true;
|
|
177
190
|
}
|
|
191
|
+
return false;
|
|
178
192
|
};
|
|
179
|
-
const delegateOr = (ctx, term, column) =>
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
);
|
|
185
|
-
};
|
|
193
|
+
const delegateOr = (ctx, term, column) => (term.value != null && column.bitmap ? execBitOr : execOr)(
|
|
194
|
+
ctx,
|
|
195
|
+
term,
|
|
196
|
+
column
|
|
197
|
+
);
|
|
186
198
|
const execBitAnd = (ctx, term, column) => {
|
|
187
199
|
const bitmap = column.bitmap;
|
|
188
200
|
const key = column.valueKey(term.value);
|
|
@@ -191,16 +203,14 @@ const execBitAnd = (ctx, term, column) => {
|
|
|
191
203
|
const colBitmaps = [];
|
|
192
204
|
for (let k of key) {
|
|
193
205
|
const b = bitmap.index.get(k)?.buffer;
|
|
194
|
-
if (!b)
|
|
195
|
-
if (term.type === "and") ctx.clear();
|
|
196
|
-
return;
|
|
197
|
-
}
|
|
206
|
+
if (!b) return false;
|
|
198
207
|
colBitmaps.push(b);
|
|
199
208
|
}
|
|
200
209
|
for (let b of colBitmaps) {
|
|
201
210
|
if (mask) {
|
|
202
|
-
|
|
203
|
-
|
|
211
|
+
const n = b.length;
|
|
212
|
+
for (let i = 0; i < n; i++) mask[i] &= b[i];
|
|
213
|
+
mask.fill(0, n);
|
|
204
214
|
} else mask = ctx.makeMask(b);
|
|
205
215
|
}
|
|
206
216
|
} else {
|
|
@@ -209,7 +219,9 @@ const execBitAnd = (ctx, term, column) => {
|
|
|
209
219
|
}
|
|
210
220
|
if (mask) {
|
|
211
221
|
term.type === "nand" ? ctx.mergeInvMask(mask) : ctx.mergeMask(mask);
|
|
212
|
-
|
|
222
|
+
return true;
|
|
223
|
+
}
|
|
224
|
+
return false;
|
|
213
225
|
};
|
|
214
226
|
const execAnd = (ctx, term, column) => {
|
|
215
227
|
const n = ctx.table.length;
|
|
@@ -218,7 +230,7 @@ const execAnd = (ctx, term, column) => {
|
|
|
218
230
|
let mask;
|
|
219
231
|
for (let k of isArray(key) ? key : [key]) {
|
|
220
232
|
let m;
|
|
221
|
-
for (let i
|
|
233
|
+
for (let i of ctx) {
|
|
222
234
|
if (pred(column.getRowKey(i), k)) {
|
|
223
235
|
if (!m) m = ctx.makeMask();
|
|
224
236
|
m[i >>> 5] |= 1 << (i & 31);
|
|
@@ -229,21 +241,20 @@ const execAnd = (ctx, term, column) => {
|
|
|
229
241
|
for (let i = 0; i < n; i++) mask[i] &= m[i];
|
|
230
242
|
} else mask = m;
|
|
231
243
|
} else {
|
|
232
|
-
|
|
233
|
-
return;
|
|
244
|
+
return false;
|
|
234
245
|
}
|
|
235
246
|
}
|
|
236
247
|
if (mask) {
|
|
237
248
|
term.type === "nand" ? ctx.mergeInvMask(mask) : ctx.mergeMask(mask);
|
|
249
|
+
return true;
|
|
238
250
|
}
|
|
251
|
+
return false;
|
|
239
252
|
};
|
|
240
|
-
const delegateAnd = (ctx, term, column) =>
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
);
|
|
246
|
-
};
|
|
253
|
+
const delegateAnd = (ctx, term, column) => (term.value != null && column.bitmap ? execBitAnd : execAnd)(
|
|
254
|
+
ctx,
|
|
255
|
+
term,
|
|
256
|
+
column
|
|
257
|
+
);
|
|
247
258
|
const QUERY_OPS = {
|
|
248
259
|
or: { fn: delegateOr },
|
|
249
260
|
nor: { fn: delegateOr },
|
|
@@ -253,13 +264,14 @@ const QUERY_OPS = {
|
|
|
253
264
|
fn: (ctx, term, column) => {
|
|
254
265
|
const pred = term.value;
|
|
255
266
|
let mask;
|
|
256
|
-
for (let i
|
|
267
|
+
for (let i of ctx) {
|
|
257
268
|
if (pred(column.getRow(i))) {
|
|
258
269
|
if (!mask) mask = ctx.makeMask();
|
|
259
270
|
mask[i >>> 5] |= 1 << (i & 31);
|
|
260
271
|
}
|
|
261
272
|
}
|
|
262
273
|
if (mask) ctx.mergeMask(mask);
|
|
274
|
+
return !!mask;
|
|
263
275
|
}
|
|
264
276
|
},
|
|
265
277
|
matchPartialRow: {
|
|
@@ -269,13 +281,14 @@ const QUERY_OPS = {
|
|
|
269
281
|
const columns = term.params;
|
|
270
282
|
const pred = term.value;
|
|
271
283
|
let mask;
|
|
272
|
-
for (let i
|
|
284
|
+
for (let i of ctx) {
|
|
273
285
|
if (pred(table.getPartialRow(i, columns, false))) {
|
|
274
286
|
if (!mask) mask = ctx.makeMask();
|
|
275
287
|
mask[i >>> 5] |= 1 << (i & 31);
|
|
276
288
|
}
|
|
277
289
|
}
|
|
278
290
|
if (mask) ctx.mergeMask(mask);
|
|
291
|
+
return !!mask;
|
|
279
292
|
}
|
|
280
293
|
},
|
|
281
294
|
matchRow: {
|
|
@@ -284,13 +297,14 @@ const QUERY_OPS = {
|
|
|
284
297
|
const table = ctx.table;
|
|
285
298
|
const pred = term.value;
|
|
286
299
|
let mask;
|
|
287
|
-
for (let i
|
|
300
|
+
for (let i of ctx) {
|
|
288
301
|
if (pred(table.getRow(i, false))) {
|
|
289
302
|
if (!mask) mask = ctx.makeMask();
|
|
290
303
|
mask[i >>> 5] |= 1 << (i & 31);
|
|
291
304
|
}
|
|
292
305
|
}
|
|
293
306
|
if (mask) ctx.mergeMask(mask);
|
|
307
|
+
return !!mask;
|
|
294
308
|
}
|
|
295
309
|
},
|
|
296
310
|
valueRange: {
|
|
@@ -302,7 +316,7 @@ const QUERY_OPS = {
|
|
|
302
316
|
if (start != null) $start = column.findIndex((x) => x >= start);
|
|
303
317
|
if (end != null)
|
|
304
318
|
$end = column.findLastIndex((x) => x <= end, $start);
|
|
305
|
-
__fillMask(ctx, $start, $end >= 0 ? $end + 1 : $end);
|
|
319
|
+
return __fillMask(ctx, $start, $end >= 0 ? $end + 1 : $end, max);
|
|
306
320
|
}
|
|
307
321
|
},
|
|
308
322
|
rowRange: {
|
|
@@ -311,17 +325,19 @@ const QUERY_OPS = {
|
|
|
311
325
|
const max = ctx.table.length;
|
|
312
326
|
let { start = 0, end = max } = term.value;
|
|
313
327
|
[start, end] = __clampRange(max, start, end);
|
|
314
|
-
__fillMask(ctx, start, end);
|
|
328
|
+
return __fillMask(ctx, start, end, max);
|
|
315
329
|
}
|
|
316
330
|
}
|
|
317
331
|
};
|
|
318
|
-
const __fillMask = (ctx, start, end, fill = 1) => {
|
|
319
|
-
if (start >= 0 && end >= 0) {
|
|
332
|
+
const __fillMask = (ctx, start, end, max, fill = 1) => {
|
|
333
|
+
if (start >= 0 && start < max && end >= 0) {
|
|
320
334
|
const mask = ctx.makeMask();
|
|
321
335
|
const bitmap = new Bitfield(mask);
|
|
322
336
|
bitmap.fill(fill, start, end);
|
|
323
337
|
ctx.mergeMask(mask);
|
|
324
|
-
|
|
338
|
+
return true;
|
|
339
|
+
}
|
|
340
|
+
return false;
|
|
325
341
|
};
|
|
326
342
|
const registerQueryOp = (type, spec) => {
|
|
327
343
|
if (QUERY_OPS[type]) illegalArgs(`query op ${type} already registered`);
|
package/table.d.ts
CHANGED
|
@@ -18,7 +18,7 @@ export declare class Table<T extends Row> {
|
|
|
18
18
|
query(terms?: QueryTerm<T>[]): Query<T>;
|
|
19
19
|
addColumn(id: ColumnID<T>, spec: Partial<ColumnSpec> & {
|
|
20
20
|
type: ColumnSpec["type"];
|
|
21
|
-
}):
|
|
21
|
+
}): IColumn;
|
|
22
22
|
removeColumn(id: ColumnID<T>): boolean;
|
|
23
23
|
[Symbol.iterator](): Generator<Maybe<T>, void, unknown>;
|
|
24
24
|
reindex(): void;
|
package/table.js
CHANGED
|
@@ -43,11 +43,13 @@ class Table {
|
|
|
43
43
|
};
|
|
44
44
|
this.validateColumnSpec(id, $spec);
|
|
45
45
|
this.schema[id] = $spec;
|
|
46
|
-
this.columns[id] = COLUMN_TYPES[spec.type].impl(
|
|
46
|
+
const column = this.columns[id] = COLUMN_TYPES[$spec.type].impl(
|
|
47
47
|
this,
|
|
48
48
|
String(id),
|
|
49
49
|
$spec
|
|
50
50
|
);
|
|
51
|
+
if (this.length) column.ensureRows();
|
|
52
|
+
return column;
|
|
51
53
|
}
|
|
52
54
|
removeColumn(id) {
|
|
53
55
|
if (this.columns[id]) return false;
|
|
@@ -132,6 +134,8 @@ class Table {
|
|
|
132
134
|
if (max > 1 !== isArray(spec.default))
|
|
133
135
|
__columnError(id, `wrong default value`);
|
|
134
136
|
}
|
|
137
|
+
if (this.length && spec.default == null && (def.required || min > 0))
|
|
138
|
+
__columnError(id, `missing default value to fill existing rows`);
|
|
135
139
|
}
|
|
136
140
|
toJSON() {
|
|
137
141
|
return {
|