@thi.ng/column-store 0.7.1 → 0.9.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
@@ -30,6 +30,8 @@
30
30
  - [FLAG_RLE](#flag_rle)
31
31
  - [Custom flags](#custom-flags)
32
32
  - [Query engine](#query-engine)
33
+ - [Query execution](#query-execution)
34
+ - [Optimized row iteration](#optimized-row-iteration)
33
35
  - [Built-in operators](#built-in-operators)
34
36
  - [OR](#or)
35
37
  - [AND](#and)
@@ -315,7 +317,9 @@ types](#custom-column-types).
315
317
  ## Query engine
316
318
 
317
319
  The query engine is highly extensible and can be used for executing arbitrarily
318
- complex queries.
320
+ complex queries via chaining of query operators.
321
+
322
+ ### Query execution
319
323
 
320
324
  The system allows predefining queries, which are then only evaluated and produce
321
325
  up-to-date results via the standard JS iterable mechanism (i.e. queries
@@ -328,19 +332,46 @@ const query = table.query().or("name", ["alice", "bob"]);
328
332
  // actually (re)execute query
329
333
  for(let result of query) { ... }
330
334
 
331
- // ..or using slice operator
335
+ // ..or collect result into an array using slice operator
332
336
  const results = [...query];
333
337
  ```
334
338
 
335
- TODO see code examples below
336
-
337
- ### Built-in operators
339
+ #### Optimized row iteration
338
340
 
339
341
  The query engine works by applying a number of [query
340
342
  terms](https://docs.thi.ng/umbrella/column-store/interfaces/QueryTerm.html) in
341
343
  series, with each step intersecting (aka logical AND) its results with the
342
344
  results of the previous step(s), thereby narrowing down the result set.
343
345
 
346
+ For each query term, only the rows already marked (aka pre-selected by
347
+ predecessor query terms) are visited. When a query term does not manage to
348
+ select any rows, the query is terminated. Internally, this selecting and
349
+ intersecting of partial query results is done via bitfields only. There's no
350
+ creation of interim result arrays, nor any full decoding/construction of interim
351
+ row records. The latter only happens for the final result rows and/or when using
352
+ the [`matchRow()` or `matchPartialRow()`](#predicate-based-matchers) query
353
+ operators.
354
+
355
+ When a column has an associated bitfield index (enabled via
356
+ [`FLAG_BITMAP`](#flag_bitmap)), some query operators (see below) are optimized
357
+ even further, entirely avoiding the need to visit any individual rows.
358
+
359
+ The diagram below illustrates the application of the following 3-operator query
360
+ and the resulting stepwise narrowing of the result set:
361
+
362
+ ```ts
363
+ table.query()
364
+ .matchColumn("id", inRange(100, 110))
365
+ .matchColumn("age", inRange(20, 50))
366
+ .matchColumn("name", startsWith("a"))
367
+ ```
368
+
369
+ ![Diagram showing a list of rows with object values and three columns
370
+ illustrating the narrowing effect of query operators with their partial
371
+ results](https://raw.githubusercontent.com/thi-ng/umbrella/develop/assets/column-store/query-narrowing.png)
372
+
373
+ ### Built-in operators
374
+
344
375
  By default, individual query terms operate on a single column, but can also can
345
376
  also apply to multiple. Terms are supplied either as array given to the
346
377
  [`Query`](https://docs.thi.ng/umbrella/column-store/classes/Query.html)
@@ -382,12 +413,18 @@ can be used, otherwise the behavior is:
382
413
 
383
414
  #### Predicate-based matchers
384
415
 
416
+ > [!NOTE]
417
+ > For best performance and to minimize/avoid potential decoding and construction
418
+ > of interim row objects, prefer `matchColumn` or `matchPartialRow` over
419
+ > `matchRow` if at all possible. Oftentimes, query predicates requiring multiple
420
+ > column values can be easily refactored into separate query terms.
421
+
385
422
  - [`matchColumn`](https://docs.thi.ng/umbrella/column-store/classes/Query.html#matchcolumn):
386
423
  apply predicate to column value
387
- - [`matchRow`](https://docs.thi.ng/umbrella/column-store/classes/Query.html#matchrow):
388
- apply predicate to full row
389
424
  - [`matchPartialRow`](https://docs.thi.ng/umbrella/column-store/classes/Query.html#matchpartialrow):
390
425
  apply predicate to partial row (only selected columns)
426
+ - [`matchRow`](https://docs.thi.ng/umbrella/column-store/classes/Query.html#matchrow):
427
+ apply predicate to full row
391
428
 
392
429
  #### Row ranges
393
430
 
@@ -458,7 +495,7 @@ For Node.js REPL:
458
495
  const cs = await import("@thi.ng/column-store");
459
496
  ```
460
497
 
461
- Package sizes (brotli'd, pre-treeshake): ESM: 5.71 KB
498
+ Package sizes (brotli'd, pre-treeshake): ESM: 5.78 KB
462
499
 
463
500
  ## Dependencies
464
501
 
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;
@@ -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): Uint32Array<ArrayBufferLike> | Float32Array<ArrayBufferLike> | Float64Array<ArrayBufferLike> | Int8Array<ArrayBufferLike> | Int16Array<ArrayBufferLike> | Int32Array<ArrayBufferLike> | Uint8Array<ArrayBufferLike> | Uint8ClampedArray<ArrayBufferLike> | Uint16Array<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;
package/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export * from "./api.js";
2
2
  export * from "./bitmap.js";
3
+ export * from "./predicates.js";
3
4
  export * from "./query.js";
4
5
  export * from "./table.js";
5
6
  //# sourceMappingURL=index.d.ts.map
package/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from "./api.js";
2
2
  export * from "./bitmap.js";
3
+ export * from "./predicates.js";
3
4
  export * from "./query.js";
4
5
  export * from "./table.js";
@@ -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[]) => Uint32Array<ArrayBufferLike> | 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[]) => 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.1",
3
+ "version": "0.9.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",
@@ -115,6 +115,9 @@
115
115
  "./columns/vector": {
116
116
  "default": "./columns/vector.js"
117
117
  },
118
+ "./predicates": {
119
+ "default": "./predicates.js"
120
+ },
118
121
  "./query": {
119
122
  "default": "./query.js"
120
123
  },
@@ -126,5 +129,5 @@
126
129
  "status": "alpha",
127
130
  "year": 2025
128
131
  },
129
- "gitHead": "3e53df924e859a37127070b16960c8c6242adeaa\n"
132
+ "gitHead": "1138bb64457a415733cf5c05d32c211ccfd6ee72\n"
130
133
  }
@@ -0,0 +1,25 @@
1
+ import type { NumOrString, Predicate } from "@thi.ng/api";
2
+ /**
3
+ * Higher-order query predicate for {@link Query.matchColumn}. The returned
4
+ * predicate returns true if a row value is in the semi-open interval defined by
5
+ * `[min,max)`.
6
+ *
7
+ * @param min
8
+ * @param max
9
+ */
10
+ export declare const inRange: (min: NumOrString, max: NumOrString) => Predicate<NumOrString | null>;
11
+ /**
12
+ * Higher-order query predicate for {@link Query.matchColumn}. The returned
13
+ * predicate returns true if a row value starts with given `prefix`.
14
+ *
15
+ * @param prefix
16
+ */
17
+ export declare const startsWith: (prefix: string) => Predicate<string | null>;
18
+ /**
19
+ * Higher-order query predicate for {@link Query.matchColumn}. The returned
20
+ * predicate returns true if a row value matches the given `regexp`.
21
+ *
22
+ * @param re
23
+ */
24
+ export declare const matchRegExp: (re: RegExp) => Predicate<string | null>;
25
+ //# sourceMappingURL=predicates.d.ts.map
package/predicates.js ADDED
@@ -0,0 +1,8 @@
1
+ const inRange = (min, max) => (x) => x != null && x >= min && x < max;
2
+ const startsWith = (prefix) => (x) => x?.startsWith(prefix) ?? false;
3
+ const matchRegExp = (re) => (x) => x != null ? re.test(x) : false;
4
+ export {
5
+ inRange,
6
+ matchRegExp,
7
+ startsWith
8
+ };