@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 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.50 KB
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>, void>;
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";
@@ -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;
@@ -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;
@@ -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;
@@ -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
- values[i] = this.ensureValue(value);
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);
@@ -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;
@@ -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
@@ -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
- let len = this.values.length;
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
@@ -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> | Uint32Array<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
- const j = i * this.size;
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, j);
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
@@ -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> | Uint32Array<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.6.1",
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": "c048d3c6149769309feff3fa943dcd3767b5c6b2\n"
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 new Bitfield(ctx.bitmap).ones(table.length))
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; i < b.length; i++) mask[i] |= b[i];
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 = 0; i < n; 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
- (term.value != null && column.bitmap ? execBitOr : execOr)(
181
- ctx,
182
- term,
183
- column
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
- for (let i = 0; i < b.length; i++) mask[i] &= b[i];
203
- mask.fill(0, b.length);
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
- } else ctx.clear();
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 = 0; i < n; 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
- if (term.type === "and") ctx.clear();
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
- (term.value != null && column.bitmap ? execBitAnd : execAnd)(
242
- ctx,
243
- term,
244
- column
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 = 0, n = ctx.table.length; i < n; 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 = 0, n = table.length; i < n; 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 = 0, n = table.length; i < n; 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
- } else ctx.clear();
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
- }): void;
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 {