@thi.ng/column-store 0.2.0 → 0.4.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
@@ -63,7 +63,7 @@ delegates them to the columns.
63
63
  An example table definition looks like this (explanation of column types in next
64
64
  section below):
65
65
 
66
- ```ts
66
+ ```ts tangle:export/readme-types.ts
67
67
  import { Table, FLAG_DICT, FLAG_UNIQUE } from "@thi.ng/column-store";
68
68
 
69
69
  // define a table with the given columns
@@ -81,7 +81,7 @@ const table = new Table({
81
81
  aliases: { type: "str", cardinality: [0, 3] },
82
82
 
83
83
  // required fixed size tuples (aka vectors) of numbers
84
- latlon: { type: "num", cardinality: [2, 2] },
84
+ latlon: { type: "f32vec", cardinality: [2, 2] },
85
85
 
86
86
  // optional tuples of max. 10 strings, with default
87
87
  // the given flags (explained further below) are triggering:
@@ -105,6 +105,11 @@ table.addRow({
105
105
  });
106
106
  ```
107
107
 
108
+ > [!IMPORTANT]
109
+ > Columns can be named freely, with the exception that the `__` name prefix is
110
+ > reserved for internal use. For example, `foo` is allowed, but `__foo` is a
111
+ > reserved name.
112
+
108
113
  ### Column types
109
114
 
110
115
  The current built-in column types only support numeric or string values, though
@@ -149,12 +154,13 @@ configs are: `[0,3]` (with default given) or `[3,3]`.
149
154
  When [querying](#query-engine) vector columns using the standard
150
155
  `(n)or`/`(n)and` operators, always the entire vector is matched (by value).
151
156
 
152
- > [!IMPORTANT] For performance reasons, rows retrieved from vector columns
153
- > contain mutable data views of the underlying column storage. That means when
154
- > manipulating data in these views, the underlying data in the column would be
155
- > changed too. To avoid index corruption, always edit only copies of this vector
156
- > data and then use `table.updateRow()` to properly update the column storage
157
- > (incl. any internal indexes).
157
+ > [!IMPORTANT]
158
+ > For performance reasons, rows retrieved from vector columns contain mutable
159
+ > data views of the underlying column storage. That means when manipulating data
160
+ > in these views, the underlying data in the column would be changed too. To
161
+ > avoid index corruption, always edit only copies of this vector data and then
162
+ > use `table.updateRow()` to properly update the column storage (incl. any
163
+ > internal indexes).
158
164
 
159
165
  ### Serialization options
160
166
 
@@ -310,8 +316,8 @@ TODO see code examples below
310
316
 
311
317
  The query engine works by applying a number of [query
312
318
  terms](https://docs.thi.ng/umbrella/column-store/interfaces/QueryTerm.html) in
313
- series, with each step intersecting its results with the results of the previous
314
- step(s), thereby narrowing down the result set.
319
+ series, with each step intersecting (aka logical AND) its results with the
320
+ results of the previous step(s), thereby narrowing down the result set.
315
321
 
316
322
  By default, individual query terms operate on a single column, but can also can
317
323
  also apply to multiple. Terms are supplied either as array given to the
@@ -406,7 +412,7 @@ For Node.js REPL:
406
412
  const cs = await import("@thi.ng/column-store");
407
413
  ```
408
414
 
409
- Package sizes (brotli'd, pre-treeshake): ESM: 4.65 KB
415
+ Package sizes (brotli'd, pre-treeshake): ESM: 4.67 KB
410
416
 
411
417
  ## Dependencies
412
418
 
@@ -465,19 +471,20 @@ table.addRows([
465
471
  const unsortedImages = table.query().where("type", "img").and("tags", "unsorted");
466
472
 
467
473
  // queries are iterables and only execute when the iterator is consumed
474
+ // each query result includes a `__row` ID
468
475
  console.log([...unsortedImages]);
469
- // [ { id: 102, type: "img", tags: [ "unsorted" ] } ]
476
+ // [ { id: 102, type: "img", tags: [ "unsorted" ], __row: 2 } ]
470
477
 
471
478
  // select items with `a` OR `b` tags, intersect with those which have `c` AND `d` tags
472
479
  const complexTagQuery = table.query().or("tags", ["a", "b"]).and("tags", ["c", "d"]);
473
480
  console.log([...complexTagQuery]);
474
- // [ { id: 104, type: "img", tags: [ "b", "c", "d" ] } ]
481
+ // [ { id: 104, type: "img", tags: [ "b", "c", "d" ], __row: 4 } ]
475
482
 
476
483
  // query using custom predicates
477
484
  console.log([...table.query().matchColumn("id", (id) => id > 102)]);
478
485
  // [
479
- // { id: 103, type: "img", tags: [ "unsorted" ] },
480
- // { id: 104, type: "img", tags: [ "b", "c", "d" ] }
486
+ // { id: 103, type: "img", tags: [ "unsorted" ], __row: 3 },
487
+ // { id: 104, type: "img", tags: [ "b", "c", "d" ], __row: 4 }
481
488
  // ]
482
489
 
483
490
  // serialize table to JSON
package/api.d.ts CHANGED
@@ -89,6 +89,21 @@ export interface IColumn {
89
89
  load(spec: SerializedColumn): void;
90
90
  reindex(): void;
91
91
  validate(value: any): boolean;
92
+ /**
93
+ * Searches for `value` in the column data, optionally constrained to given
94
+ * `start`/`end` range. If found, returns row ID of first occurrence,
95
+ * otherwise -1.
96
+ *
97
+ * @param value
98
+ * @param start
99
+ * @param end
100
+ */
101
+ indexOf(value: any, start?: number, end?: number): number;
102
+ /**
103
+ * Returns value at given row.
104
+ *
105
+ * @param i
106
+ */
92
107
  getRow(i: number): any;
93
108
  /**
94
109
  * Sets the column's value at given row index. Assumes the value has been
package/bitmap.d.ts CHANGED
@@ -27,6 +27,7 @@ export declare class Bitfield {
27
27
  buffer?: Uint32Array | undefined;
28
28
  constructor(buffer?: Uint32Array | undefined);
29
29
  ones(max?: number): Generator<number, void, unknown>;
30
+ first(start?: number, end?: number): number;
30
31
  setBit(id: number): void;
31
32
  clearBit(id: number): void;
32
33
  ensure(size: number): Uint32Array<ArrayBufferLike>;
package/bitmap.js CHANGED
@@ -49,10 +49,10 @@ class Bitfield {
49
49
  this.buffer = buffer;
50
50
  }
51
51
  *ones(max = Infinity) {
52
- const buf = this.buffer;
53
- if (!buf) return;
54
- for (let i = 0, n = buf.length; i < n; i++) {
55
- let bits = buf[i];
52
+ const { buffer } = this;
53
+ if (!buffer) return;
54
+ for (let i = 0, n = buffer.length; i < n; i++) {
55
+ let bits = buffer[i];
56
56
  while (bits) {
57
57
  const lsb = bits & -bits;
58
58
  const x = (i << 5) + (Math.clz32(lsb) ^ 31);
@@ -62,6 +62,20 @@ class Bitfield {
62
62
  }
63
63
  }
64
64
  }
65
+ first(start = 0, end = (this.buffer?.length ?? -1) * 32) {
66
+ const { buffer } = this;
67
+ if (!buffer || start >= end) return -1;
68
+ for (let i = start >>> 5, n = Math.min(Math.ceil(end / 32), buffer.length); i < n; i++) {
69
+ let bits = buffer[i];
70
+ while (bits) {
71
+ const lsb = bits & -bits;
72
+ const x = (i << 5) + (Math.clz32(lsb) ^ 31);
73
+ if (x >= start && x < end) return x;
74
+ bits ^= lsb;
75
+ }
76
+ }
77
+ return -1;
78
+ }
65
79
  setBit(id) {
66
80
  const w = id >>> 5;
67
81
  this.ensure(w)[w] |= 1 << (id & 31);
@@ -19,6 +19,7 @@ export declare abstract class AColumn implements IColumn {
19
19
  abstract valueKey(x: any): any;
20
20
  abstract getRow(i: number): any;
21
21
  abstract getRowKey(i: number): any;
22
+ abstract indexOf(value: any, start?: number, end?: number): number;
22
23
  encode(value: any): any;
23
24
  decode(value: any): any;
24
25
  protected loadDict(serialized: SerializedIndex): void;
@@ -15,6 +15,7 @@ export declare class DictTupleColumn extends AColumn implements IColumn {
15
15
  getRowKey(i: number): number[] | null;
16
16
  valueKey(value: any): (number | null)[];
17
17
  removeRow(i: number): void;
18
+ indexOf(value: any, start?: number, end?: number): number;
18
19
  replaceValue(currValue: any, newValue: any): boolean;
19
20
  toJSON(): {
20
21
  dict: {
@@ -2,6 +2,7 @@ import { BidirIndex } from "@thi.ng/bidir-index";
2
2
  import { isArray } from "@thi.ng/checks/is-array";
3
3
  import { FLAG_UNIQUE } from "../api.js";
4
4
  import { __validateArrayValue } from "../internal/checks.js";
5
+ import { __indexOfTuple } from "../internal/indexof.js";
5
6
  import { __serializeDict } from "../internal/serialize.js";
6
7
  import { AColumn } from "./acolumn.js";
7
8
  class DictTupleColumn extends AColumn {
@@ -55,6 +56,15 @@ class DictTupleColumn extends AColumn {
55
56
  this.values.splice(i, 1);
56
57
  this.bitmap?.removeBit(i);
57
58
  }
59
+ indexOf(value, start = 0, end = this.table.length) {
60
+ return __indexOfTuple(
61
+ value != null ? this.encode(value) : null,
62
+ this.values,
63
+ this.table.length,
64
+ start,
65
+ end
66
+ );
67
+ }
58
68
  replaceValue(currValue, newValue) {
59
69
  const { dict, values, bitmap } = this;
60
70
  const res = dict.renameKey(currValue, newValue);
package/columns/dict.d.ts CHANGED
@@ -7,7 +7,7 @@ export declare class DictColumn extends AColumn implements IColumn {
7
7
  readonly isArray = false;
8
8
  load({ dict, values }: SerializedColumn): void;
9
9
  reindex(): void;
10
- encode(value: any): number | undefined;
10
+ encode(value: any): number | null;
11
11
  decode(value: any): any;
12
12
  validate(value: any): boolean;
13
13
  setRow(i: number, value: any): void;
@@ -15,6 +15,7 @@ export declare class DictColumn extends AColumn implements IColumn {
15
15
  getRowKey(i: number): number | null;
16
16
  valueKey(value: any): number | number[] | undefined;
17
17
  removeRow(i: number): void;
18
+ indexOf(value: any, start?: number, end?: number): number;
18
19
  replaceValue(currValue: any, newValue: any): boolean;
19
20
  toJSON(): {
20
21
  dict: {
package/columns/dict.js CHANGED
@@ -1,11 +1,12 @@
1
1
  import { BidirIndex } from "@thi.ng/bidir-index";
2
+ import { isArray } from "@thi.ng/checks/is-array";
2
3
  import { decodeBinary, encodeBinary } from "@thi.ng/rle-pack/binary";
3
4
  import { decodeSimple, encodeSimple } from "@thi.ng/rle-pack/simple";
4
5
  import { FLAG_RLE } from "../api.js";
5
6
  import { __validateValue } from "../internal/checks.js";
7
+ import { __indexOfSingle } from "../internal/indexof.js";
6
8
  import { __serializeDict } from "../internal/serialize.js";
7
9
  import { AColumn } from "./acolumn.js";
8
- import { isArray } from "@thi.ng/checks/is-array";
9
10
  class DictColumn extends AColumn {
10
11
  values = [];
11
12
  dict = new BidirIndex();
@@ -25,7 +26,7 @@ class DictColumn extends AColumn {
25
26
  super.updateBitmap();
26
27
  }
27
28
  encode(value) {
28
- return this.dict.get(value);
29
+ return this.dict.get(value) ?? null;
29
30
  }
30
31
  decode(value) {
31
32
  return this.dict.getID(value);
@@ -57,6 +58,16 @@ class DictColumn extends AColumn {
57
58
  this.values.splice(i, 1);
58
59
  this.bitmap?.removeBit(i);
59
60
  }
61
+ indexOf(value, start = 0, end = this.table.length) {
62
+ return __indexOfSingle(
63
+ this.encode(value),
64
+ this.values,
65
+ this.bitmap,
66
+ this.table.length,
67
+ start,
68
+ end
69
+ );
70
+ }
60
71
  replaceValue(currValue, newValue) {
61
72
  const { dict, values, bitmap } = this;
62
73
  const res = dict.renameKey(currValue, newValue);
@@ -10,6 +10,7 @@ export declare class PlainColumn extends AColumn implements IColumn {
10
10
  getRowKey(i: number): any;
11
11
  valueKey(x: any): any;
12
12
  removeRow(i: number): void;
13
+ indexOf(value: any, start?: number, end?: number): number;
13
14
  replaceValue(currValue: any, newValue: any): boolean;
14
15
  toJSON(): {
15
16
  values: any[];
package/columns/plain.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { decodeSimple, encodeSimple } from "@thi.ng/rle-pack/simple";
2
2
  import { FLAG_RLE } from "../api.js";
3
3
  import { __validateValue } from "../internal/checks.js";
4
+ import { __indexOfSingle } from "../internal/indexof.js";
4
5
  import { __replaceValue } from "../internal/replace.js";
5
6
  import { AColumn } from "./acolumn.js";
6
7
  class PlainColumn extends AColumn {
@@ -35,6 +36,16 @@ class PlainColumn extends AColumn {
35
36
  this.values.splice(i, 1);
36
37
  this.bitmap?.removeBit(i);
37
38
  }
39
+ indexOf(value, start = 0, end) {
40
+ return __indexOfSingle(
41
+ value,
42
+ this.values,
43
+ this.bitmap,
44
+ this.table.length,
45
+ start,
46
+ end
47
+ );
48
+ }
38
49
  replaceValue(currValue, newValue) {
39
50
  return __replaceValue(this.bitmap, this.values, currValue, newValue);
40
51
  }
@@ -12,6 +12,7 @@ export declare class TupleColumn extends AColumn implements IColumn {
12
12
  getRowKey(i: number): Nullable<number[]>;
13
13
  valueKey(value: any): any[];
14
14
  removeRow(i: number): void;
15
+ indexOf(value: any, start?: number, end?: number): number;
15
16
  replaceValue(currValue: any, newValue: any): boolean;
16
17
  toJSON(): {
17
18
  values: Nullable<number[]>[];
package/columns/tuple.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { isArray } from "@thi.ng/checks/is-array";
2
2
  import { FLAG_UNIQUE } from "../api.js";
3
3
  import { __validateArrayValue } from "../internal/checks.js";
4
+ import { __indexOfTuple } from "../internal/indexof.js";
4
5
  import { AColumn } from "./acolumn.js";
5
6
  class TupleColumn extends AColumn {
6
7
  values = [];
@@ -38,6 +39,15 @@ class TupleColumn extends AColumn {
38
39
  this.values.splice(i, 1);
39
40
  this.bitmap?.removeBit(i);
40
41
  }
42
+ indexOf(value, start = 0, end = this.table.length) {
43
+ return __indexOfTuple(
44
+ value,
45
+ this.values,
46
+ this.table.length,
47
+ start,
48
+ end
49
+ );
50
+ }
41
51
  replaceValue(currValue, newValue) {
42
52
  const { values, bitmap } = this;
43
53
  const isUnique = this.spec.flags & FLAG_UNIQUE;
@@ -9,16 +9,17 @@ export declare class TypedArrayColumn extends AColumn implements IColumn {
9
9
  protected tmp: TypedArray;
10
10
  readonly isArray = false;
11
11
  constructor(id: string, table: Table);
12
- load(spec: SerializedColumn): void;
12
+ load({ values }: SerializedColumn): void;
13
13
  validate(value: any): boolean;
14
14
  setRow(i: number, value: any): void;
15
15
  getRow(i: number): number;
16
16
  getRowKey(i: number): number;
17
17
  valueKey(value: any): number | number[];
18
18
  removeRow(i: number): void;
19
+ indexOf(value: any, start?: number, end?: number): number;
19
20
  replaceValue(currValue: any, newValue: any): boolean;
20
21
  toJSON(): {
21
- values: number[];
22
+ values: any[];
22
23
  };
23
24
  }
24
25
  //# sourceMappingURL=typedarray.d.ts.map
@@ -1,13 +1,13 @@
1
- import { SIZEOF, typedArray } from "@thi.ng/api/typedarray";
1
+ import { typedArray } from "@thi.ng/api/typedarray";
2
+ import { isArray } from "@thi.ng/checks/is-array";
2
3
  import { isNumber } from "@thi.ng/checks/is-number";
3
- import { decodeBinary, encodeBinary } from "@thi.ng/rle-pack/binary";
4
4
  import {
5
- FLAG_RLE,
6
5
  LIMITS
7
6
  } from "../api.js";
7
+ import { __indexOfSingle } from "../internal/indexof.js";
8
8
  import { __replaceValue } from "../internal/replace.js";
9
+ import { __deserializeTyped, __serializeTyped } from "../internal/serialize.js";
9
10
  import { AColumn } from "./acolumn.js";
10
- import { isArray } from "@thi.ng/checks/is-array";
11
11
  class TypedArrayColumn extends AColumn {
12
12
  values;
13
13
  type;
@@ -21,13 +21,8 @@ class TypedArrayColumn extends AColumn {
21
21
  this.values = typedArray(this.type, 8);
22
22
  this.tmp = typedArray(this.type, 1);
23
23
  }
24
- load(spec) {
25
- if (this.spec.flags & FLAG_RLE) {
26
- const values = decodeBinary(spec.values);
27
- this.values = typedArray(this.type, values.buffer);
28
- } else {
29
- this.values = typedArray(this.type, spec.values);
30
- }
24
+ load({ values }) {
25
+ this.values = __deserializeTyped(this.type, this.spec.flags, values);
31
26
  this.reindex();
32
27
  }
33
28
  validate(value) {
@@ -73,15 +68,25 @@ class TypedArrayColumn extends AColumn {
73
68
  this.values[this.table.length - 1] = 0;
74
69
  this.bitmap?.removeBit(i);
75
70
  }
71
+ indexOf(value, start = 0, end = this.table.length) {
72
+ return __indexOfSingle(
73
+ value,
74
+ this.values,
75
+ this.bitmap,
76
+ this.table.length,
77
+ start,
78
+ end
79
+ );
80
+ }
76
81
  replaceValue(currValue, newValue) {
77
82
  return __replaceValue(this.bitmap, this.values, currValue, newValue);
78
83
  }
79
84
  toJSON() {
80
- let values = this.values.subarray(0, this.table.length);
81
- if (this.spec.flags & FLAG_RLE) {
82
- values = encodeBinary(values, values.length, SIZEOF[this.type] * 8);
83
- }
84
- return { values: Array.from(values) };
85
+ return __serializeTyped(
86
+ this.values.subarray(0, this.table.length),
87
+ this.spec,
88
+ this.type
89
+ );
85
90
  }
86
91
  }
87
92
  export {
@@ -10,13 +10,14 @@ export declare class VectorColumn extends AColumn implements IColumn {
10
10
  protected tmp: TypedArray;
11
11
  readonly isArray = false;
12
12
  constructor(id: string, table: Table);
13
- load(spec: SerializedColumn): void;
13
+ load({ values }: SerializedColumn): void;
14
14
  validate(value: any): boolean;
15
15
  setRow(i: number, value: any): void;
16
- getRow(i: number): Uint8Array<ArrayBufferLike> | Float32Array<ArrayBufferLike> | Float64Array<ArrayBufferLike> | Int8Array<ArrayBufferLike> | Int16Array<ArrayBufferLike> | Int32Array<ArrayBufferLike> | Uint8ClampedArray<ArrayBufferLike> | Uint16Array<ArrayBufferLike> | Uint32Array<ArrayBufferLike>;
16
+ getRow(i: number): Float32Array<ArrayBufferLike> | Float64Array<ArrayBufferLike> | Int8Array<ArrayBufferLike> | Int16Array<ArrayBufferLike> | Int32Array<ArrayBufferLike> | Uint8Array<ArrayBufferLike> | Uint8ClampedArray<ArrayBufferLike> | Uint16Array<ArrayBufferLike> | Uint32Array<ArrayBufferLike>;
17
17
  getRowKey(i: number): string;
18
18
  valueKey(value: any): string | string[];
19
19
  removeRow(i: number): void;
20
+ indexOf(value: any, start?: number, end?: number): number;
20
21
  replaceValue(): boolean;
21
22
  toJSON(): {
22
23
  values: any[];
package/columns/vector.js CHANGED
@@ -1,14 +1,13 @@
1
- import { SIZEOF, typedArray } from "@thi.ng/api/typedarray";
1
+ import { typedArray } from "@thi.ng/api/typedarray";
2
+ import { isArray } from "@thi.ng/checks/is-array";
2
3
  import { isArrayLike } from "@thi.ng/checks/is-arraylike";
3
4
  import { isNumber } from "@thi.ng/checks/is-number";
4
5
  import { unsupportedOp } from "@thi.ng/errors/unsupported";
5
- import { decodeBinary, encodeBinary } from "@thi.ng/rle-pack/binary";
6
6
  import {
7
- FLAG_RLE,
8
7
  LIMITS
9
8
  } from "../api.js";
9
+ import { __deserializeTyped, __serializeTyped } from "../internal/serialize.js";
10
10
  import { AColumn } from "./acolumn.js";
11
- import { isArray } from "@thi.ng/checks/is-array";
12
11
  class VectorColumn extends AColumn {
13
12
  values;
14
13
  type;
@@ -24,13 +23,8 @@ class VectorColumn extends AColumn {
24
23
  this.values = typedArray(this.type, 8 * this.size);
25
24
  this.tmp = typedArray(this.type, this.size);
26
25
  }
27
- load(spec) {
28
- if (this.spec.flags & FLAG_RLE) {
29
- const values = decodeBinary(spec.values);
30
- this.values = typedArray(this.type, values.buffer);
31
- } else {
32
- this.values = typedArray(this.type, spec.values);
33
- }
26
+ load({ values }) {
27
+ this.values = __deserializeTyped(this.type, this.spec.flags, values);
34
28
  this.reindex();
35
29
  }
36
30
  validate(value) {
@@ -82,22 +76,32 @@ class VectorColumn extends AColumn {
82
76
  this.values.fill(0, (length - 1) * size);
83
77
  this.bitmap?.removeBit(i);
84
78
  }
79
+ indexOf(value, start = 0, end = this.table.length) {
80
+ const { values, bitmap, size } = this;
81
+ start = Math.max(start, 0);
82
+ end = Math.min(end, this.table.length);
83
+ if (bitmap) {
84
+ return bitmap.index.get(this.valueKey(value))?.first(start, end) ?? -1;
85
+ }
86
+ end *= size;
87
+ let i, j;
88
+ outer: for (i = start * size; i < end; i += size) {
89
+ for (j = 0; j < size; j++) {
90
+ if (values[i + j] !== value[j]) continue outer;
91
+ }
92
+ return i / size;
93
+ }
94
+ return -1;
95
+ }
85
96
  replaceValue() {
86
97
  unsupportedOp("TODO");
87
98
  }
88
99
  toJSON() {
89
- let $values = this.values.subarray(0, this.table.length * this.size);
90
- if (this.spec.flags & FLAG_RLE) {
91
- $values = encodeBinary(
92
- $values,
93
- $values.length,
94
- SIZEOF[this.type] * 8
95
- );
96
- }
97
- let values = Array.from($values);
98
- const prec = this.spec.opts?.prec;
99
- if (prec != null) values = values.map((x) => +x.toFixed(prec));
100
- return { values };
100
+ return __serializeTyped(
101
+ this.values.subarray(0, this.table.length * this.size),
102
+ this.spec,
103
+ this.type
104
+ );
101
105
  }
102
106
  }
103
107
  export {
@@ -0,0 +1,6 @@
1
+ import type { Maybe } from "@thi.ng/api";
2
+ import type { BitmapIndex } from "../bitmap.js";
3
+ /** @internal */
4
+ export declare const __indexOfSingle: (needle: any, values: ArrayLike<any>, bitmap: Maybe<BitmapIndex>, max: number, start: number, end?: number) => number;
5
+ export declare const __indexOfTuple: (needle: ArrayLike<any> | null, values: ArrayLike<any>, max: number, start: number, end?: number) => number;
6
+ //# sourceMappingURL=indexof.d.ts.map
@@ -0,0 +1,34 @@
1
+ const __indexOfSingle = (needle, values, bitmap, max, start, end = max) => {
2
+ start = Math.max(start, 0);
3
+ end = Math.min(end, max);
4
+ if (bitmap) return bitmap.index.get(needle)?.first(start, end) ?? -1;
5
+ for (let i = start; i < end; i++) {
6
+ if (values[i] === needle) return i;
7
+ }
8
+ return -1;
9
+ };
10
+ const __indexOfTuple = (needle, values, max, start, end = max) => {
11
+ start = Math.max(start, 0);
12
+ end = Math.min(end, max);
13
+ if (needle == null) {
14
+ for (let i2 = start; i2 < end; i2++) {
15
+ if (!values[i2]) return i2;
16
+ }
17
+ }
18
+ const n = needle.length;
19
+ let i, j, row;
20
+ outer: for (i = start; i < end; i++) {
21
+ row = values[i];
22
+ if (row?.length === n) {
23
+ for (j = 0; j < n; j++) {
24
+ if (row[j] !== needle[j]) continue outer;
25
+ }
26
+ return i;
27
+ }
28
+ }
29
+ return -1;
30
+ };
31
+ export {
32
+ __indexOfSingle,
33
+ __indexOfTuple
34
+ };
@@ -1,7 +1,15 @@
1
+ import { type NumericArray, type Type } from "@thi.ng/api/typedarray";
1
2
  import type { BidirIndex } from "@thi.ng/bidir-index";
3
+ import { type ColumnSpec } from "../api.js";
2
4
  /** @internal */
3
5
  export declare const __serializeDict: (dict: BidirIndex<any>) => {
4
6
  index: any;
5
7
  next: number;
6
8
  };
9
+ /** @internal */
10
+ export declare const __serializeTyped: ($values: NumericArray, spec: ColumnSpec, type: Type) => {
11
+ values: any[];
12
+ };
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>;
7
15
  //# sourceMappingURL=serialize.d.ts.map
@@ -1,8 +1,27 @@
1
+ import {
2
+ SIZEOF,
3
+ typedArray
4
+ } from "@thi.ng/api/typedarray";
5
+ import { decodeBinary, encodeBinary } from "@thi.ng/rle-pack/binary";
6
+ import { decodeSimple, encodeSimple } from "@thi.ng/rle-pack/simple";
7
+ import { FLAG_RLE } from "../api.js";
1
8
  const __serializeDict = (dict) => {
2
9
  const res = [];
3
10
  for (let [val, id] of dict.entries()) res[id] = val;
4
11
  return { index: res, next: dict.nextID };
5
12
  };
13
+ const __serializeTyped = ($values, spec, type) => {
14
+ if (spec.flags & FLAG_RLE) {
15
+ $values = type[0] === "f" ? encodeSimple($values) : encodeBinary($values, $values.length, SIZEOF[type] * 8);
16
+ }
17
+ let values = Array.from($values);
18
+ const prec = spec.opts?.prec;
19
+ if (prec != null) values = values.map((x) => +x.toFixed(prec));
20
+ return { values };
21
+ };
22
+ const __deserializeTyped = (type, flags, values) => flags & FLAG_RLE ? type[0] === "f" ? typedArray(type, decodeSimple(values)) : typedArray(type, decodeBinary(values).buffer) : typedArray(type, values);
6
23
  export {
7
- __serializeDict
24
+ __deserializeTyped,
25
+ __serializeDict,
26
+ __serializeTyped
8
27
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thi.ng/column-store",
3
- "version": "0.2.0",
3
+ "version": "0.4.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": "b90a2f41eb0b3c89391bbb7cfff940192f23a83c\n"
129
+ "gitHead": "5c620a1aa00535cc7c674794790edd87b2e5e526\n"
130
130
  }
package/query.js CHANGED
@@ -67,7 +67,7 @@ class Query {
67
67
  }
68
68
  if (ctx.bitmap) {
69
69
  for (let i of new Bitfield(ctx.bitmap).ones(table.length))
70
- yield table.getRow(i);
70
+ yield table.getRow(i, false, true);
71
71
  }
72
72
  }
73
73
  }
package/table.d.ts CHANGED
@@ -25,8 +25,9 @@ export declare class Table {
25
25
  addRows(rows: Iterable<Row>): void;
26
26
  updateRow(i: number, row: Row): void;
27
27
  removeRow(i: number): void;
28
- getRow(i: number, safe?: boolean): Row | undefined;
29
- getPartialRow(i: number, columns: string[], safe?: boolean): Row | undefined;
28
+ getRow(i: number, safe?: boolean, includeID?: boolean): Row | undefined;
29
+ getPartialRow(i: number, columns: string[], safe?: boolean, includeID?: boolean): Row | undefined;
30
+ indexOf(id: string, value: any, start?: number, end?: number): number;
30
31
  validateRow(row: Row): void;
31
32
  validateColumnSpec(id: string, spec: ColumnSpec): void;
32
33
  toJSON(): {
package/table.js CHANGED
@@ -82,22 +82,25 @@ class Table {
82
82
  }
83
83
  this.length--;
84
84
  }
85
- getRow(i, safe = true) {
85
+ getRow(i, safe = true, includeID = false) {
86
86
  if (safe && (i < 0 || i >= this.length)) return;
87
- const row = {};
87
+ const row = includeID ? { __row: i } : {};
88
88
  for (let id in this.columns) {
89
89
  row[id] = this.columns[id].getRow(i);
90
90
  }
91
91
  return row;
92
92
  }
93
- getPartialRow(i, columns, safe = true) {
93
+ getPartialRow(i, columns, safe = true, includeID = false) {
94
94
  if (safe && (i < 0 || i >= this.length)) return;
95
- const row = {};
95
+ const row = includeID ? { __row: i } : {};
96
96
  for (let id of columns) {
97
97
  row[id] = this.columns[id]?.getRow(i);
98
98
  }
99
99
  return row;
100
100
  }
101
+ indexOf(id, value, start, end) {
102
+ return this.columns[id]?.indexOf(value, start, end) ?? -1;
103
+ }
101
104
  validateRow(row) {
102
105
  const { columns } = this;
103
106
  for (let id in columns) {
@@ -114,15 +117,13 @@ class Table {
114
117
  `unsupported flags for column type: ${spec.type}`
115
118
  );
116
119
  const [min, max] = spec.cardinality;
117
- if (max < min)
120
+ if (max < min || min < def.cardinality[0] || max > def.cardinality[1] || max < 1)
118
121
  __columnError(id, `wrong cardinality: ${spec.cardinality}`);
119
- if (min < def.cardinality[0] || max > def.cardinality[1])
120
- __columnError(id, `wrong cardimality`);
121
122
  if (def.required && min === 0 && spec.default == null)
122
123
  __columnError(id, `missing default value`);
123
124
  if (spec.default != null) {
124
125
  if (max > 1 !== isArray(spec.default))
125
- __columnError(id, `default value`);
126
+ __columnError(id, `wrong default value`);
126
127
  }
127
128
  }
128
129
  toJSON() {
@@ -139,7 +140,6 @@ const $typed = {
139
140
  cardinality: [0, 1],
140
141
  required: true
141
142
  };
142
- const $float = { ...$typed, flags: FLAG_BITMAP };
143
143
  const $untyped = {
144
144
  impl: (table, id, { flags, cardinality: [_, max] }) => {
145
145
  const isDict = flags & FLAG_DICT;
@@ -159,7 +159,6 @@ const $vec = {
159
159
  cardinality: [0, -1 >>> 0],
160
160
  required: true
161
161
  };
162
- const $fvec = { ...$vec, flags: FLAG_BITMAP };
163
162
  const COLUMN_TYPES = {
164
163
  u8: $typed,
165
164
  i8: $typed,
@@ -167,14 +166,18 @@ const COLUMN_TYPES = {
167
166
  i16: $typed,
168
167
  u32: $typed,
169
168
  i32: $typed,
170
- f32: $float,
171
- f64: $float,
169
+ f32: $typed,
170
+ f64: $typed,
172
171
  num: $untyped,
173
172
  str: $untyped,
174
173
  u8vec: $vec,
175
174
  u16vec: $vec,
176
175
  u32vec: $vec,
177
- f32vec: $fvec
176
+ i8vec: $vec,
177
+ i16vec: $vec,
178
+ i32vec: $vec,
179
+ f32vec: $vec,
180
+ f64vec: $vec
178
181
  };
179
182
  const registerColumnType = (type, spec) => {
180
183
  if (COLUMN_TYPES[type])