@thi.ng/column-store 0.7.0 → 0.8.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/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
@@ -84,7 +84,7 @@ export declare const FLAG_UNIQUE: number;
84
84
  export declare const FLAG_RLE: number;
85
85
  /** @internal */
86
86
  export declare const LIMITS: Record<NumericType, [number, number]>;
87
- export interface IColumn {
87
+ export interface IColumn extends Iterable<any> {
88
88
  bitmap?: BitmapIndex;
89
89
  readonly isArray: boolean;
90
90
  load(spec: SerializedColumn): void;
@@ -180,7 +180,7 @@ export interface QueryTerm<T extends Row> {
180
180
  value: any;
181
181
  params?: any;
182
182
  }
183
- export type QueryTermOp = Fn3<QueryCtx<any>, QueryTerm<any>, Maybe<IColumn>, void>;
183
+ export type QueryTermOp = Fn3<QueryCtx<any>, QueryTerm<any>, Maybe<IColumn>, boolean>;
184
184
  export interface QueryTermOpSpec {
185
185
  /**
186
186
  * Default mode is: "col";
@@ -11,8 +11,9 @@ export declare abstract class AColumn<T extends Row = Row> implements IColumn {
11
11
  dict?: BidirIndex<any>;
12
12
  abstract isArray: boolean;
13
13
  constructor(id: ColumnID<T>, table: Table<T>);
14
- abstract load(spec: SerializedColumn): void;
14
+ [Symbol.iterator](): Generator<any, void, unknown>;
15
15
  reindex(): void;
16
+ abstract load(spec: SerializedColumn): void;
16
17
  abstract validate(value: any): boolean;
17
18
  abstract setRow(i: number, value: any): void;
18
19
  abstract removeRow(i: number): void;
@@ -14,6 +14,9 @@ class AColumn {
14
14
  spec;
15
15
  bitmap;
16
16
  dict;
17
+ *[Symbol.iterator]() {
18
+ for (let i = 0, n = this.table.length; i < n; i++) yield this.getRow(i);
19
+ }
17
20
  reindex() {
18
21
  this.updateBitmap();
19
22
  }
@@ -14,7 +14,7 @@ export declare class VectorColumn<T extends Row = Row> extends AColumn<T> {
14
14
  validate(value: any): boolean;
15
15
  ensureRows(): void;
16
16
  setRow(i: number, value: any): void;
17
- getRow(i: number): Uint8Array<ArrayBufferLike> | Float32Array<ArrayBufferLike> | Float64Array<ArrayBufferLike> | Int8Array<ArrayBufferLike> | Int16Array<ArrayBufferLike> | Int32Array<ArrayBufferLike> | Uint8ClampedArray<ArrayBufferLike> | Uint16Array<ArrayBufferLike> | Uint32Array<ArrayBufferLike>;
17
+ getRow(i: number): Float32Array<ArrayBufferLike> | Float64Array<ArrayBufferLike> | Int8Array<ArrayBufferLike> | Int16Array<ArrayBufferLike> | Int32Array<ArrayBufferLike> | Uint8Array<ArrayBufferLike> | Uint8ClampedArray<ArrayBufferLike> | Uint16Array<ArrayBufferLike> | Uint32Array<ArrayBufferLike>;
18
18
  getRowKey(i: number): string;
19
19
  valueKey(value: any): string | string[];
20
20
  removeRow(i: number): void;
@@ -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[]) => Uint8Array<ArrayBufferLike> | Float32Array<ArrayBufferLike> | Float64Array<ArrayBufferLike> | Int8Array<ArrayBufferLike> | Int16Array<ArrayBufferLike> | Int32Array<ArrayBufferLike> | Uint8ClampedArray<ArrayBufferLike> | Uint16Array<ArrayBufferLike> | Uint32Array<ArrayBufferLike>;
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>;
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.7.0",
3
+ "version": "0.8.0",
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": "3bb968f9005a82c2890510903aab375d2cc99fae\n"
129
+ "gitHead": "63ca02b09367c07f6cb785642bc3f2b7df2e5804\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`);