@thi.ng/column-store 0.9.0 → 0.10.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/api.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { FloatType, Fn3, IntType, Maybe, Predicate, UintType } from "@thi.ng/api";
1
+ import type { FloatType, Fn3, IClear, IntType, Maybe, Predicate, UintType } from "@thi.ng/api";
2
2
  import type { BitmapIndex } from "./bitmap.js";
3
3
  import type { QueryCtx } from "./query.js";
4
4
  import type { Table } from "./table.js";
@@ -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 extends Iterable<any> {
87
+ export interface IColumn extends Iterable<any>, IClear {
88
88
  bitmap?: BitmapIndex;
89
89
  readonly isArray: boolean;
90
90
  load(spec: SerializedColumn): void;
@@ -192,4 +192,10 @@ export interface QueryTermOpSpec {
192
192
  */
193
193
  fn: QueryTermOp;
194
194
  }
195
+ /**
196
+ * Initial capacity for typedarray and vector columns
197
+ *
198
+ * @internal
199
+ */
200
+ export declare const INITIAL_CAPACITY = 8;
195
201
  //# sourceMappingURL=api.d.ts.map
package/api.js CHANGED
@@ -17,11 +17,13 @@ const LIMITS = {
17
17
  f32: [-Infinity, Infinity],
18
18
  f64: [-Infinity, Infinity]
19
19
  };
20
+ const INITIAL_CAPACITY = 8;
20
21
  export {
21
22
  FLAG_BITMAP,
22
23
  FLAG_DICT,
23
24
  FLAG_RLE,
24
25
  FLAG_UNIQUE,
26
+ INITIAL_CAPACITY,
25
27
  LIMITS,
26
28
  ONE_PLUS,
27
29
  OPTIONAL,
@@ -13,6 +13,7 @@ export declare abstract class AColumn<T extends Row = Row> implements IColumn {
13
13
  constructor(id: ColumnID<T>, table: Table<T>);
14
14
  [Symbol.iterator](): Generator<any, void, unknown>;
15
15
  reindex(): void;
16
+ abstract clear(): void;
16
17
  abstract load(spec: SerializedColumn): void;
17
18
  abstract validate(value: any): boolean;
18
19
  abstract setRow(i: number, value: any): void;
@@ -5,6 +5,7 @@ export declare class DictTupleColumn<T extends Row = Row> extends AColumn<T> {
5
5
  values: (number[] | null)[];
6
6
  dict: BidirIndex<any>;
7
7
  readonly isArray = true;
8
+ clear(): void;
8
9
  load({ dict, values }: SerializedColumn): void;
9
10
  reindex(): void;
10
11
  encode(value: any): (number | null)[];
@@ -9,6 +9,11 @@ class DictTupleColumn extends AColumn {
9
9
  values = [];
10
10
  dict = new BidirIndex();
11
11
  isArray = true;
12
+ clear() {
13
+ this.values = [];
14
+ this.dict.clear();
15
+ this.bitmap?.clear();
16
+ }
12
17
  load({ dict, values }) {
13
18
  this.values = values;
14
19
  super.loadDict(dict);
package/columns/dict.d.ts CHANGED
@@ -5,6 +5,7 @@ export declare class DictColumn<T extends Row = Row> extends AColumn<T> {
5
5
  values: (number | null)[];
6
6
  dict: BidirIndex<any>;
7
7
  readonly isArray = false;
8
+ clear(): void;
8
9
  load({ dict, values }: SerializedColumn): void;
9
10
  reindex(): void;
10
11
  encode(value: any): number | null;
package/columns/dict.js CHANGED
@@ -11,6 +11,11 @@ class DictColumn extends AColumn {
11
11
  values = [];
12
12
  dict = new BidirIndex();
13
13
  isArray = false;
14
+ clear() {
15
+ this.values = [];
16
+ this.dict.clear();
17
+ this.bitmap?.clear();
18
+ }
14
19
  load({ dict, values }) {
15
20
  this.values = this.spec.flags & FLAG_RLE ? this.spec.cardinality[0] === 0 && this.spec.default == null ? decodeSimple(values) : Array.from(decodeBinary(values)) : values;
16
21
  super.loadDict(dict);
@@ -3,6 +3,7 @@ import { AColumn } from "./acolumn.js";
3
3
  export declare class PlainColumn<T extends Row = Row> extends AColumn<T> {
4
4
  values: any[];
5
5
  readonly isArray = false;
6
+ clear(): void;
6
7
  load({ values }: SerializedColumn): void;
7
8
  ensureRows(): void;
8
9
  validate(value: any): boolean;
package/columns/plain.js CHANGED
@@ -7,6 +7,10 @@ import { AColumn } from "./acolumn.js";
7
7
  class PlainColumn extends AColumn {
8
8
  values = [];
9
9
  isArray = false;
10
+ clear() {
11
+ this.values = [];
12
+ this.bitmap?.clear();
13
+ }
10
14
  load({ values }) {
11
15
  this.values = this.spec.flags & FLAG_RLE ? Array.from(decodeSimple(values)) : values;
12
16
  this.reindex();
@@ -4,6 +4,7 @@ import { AColumn } from "./acolumn.js";
4
4
  export declare class TupleColumn<T extends Row = Row> extends AColumn<T> {
5
5
  values: Nullable<number[]>[];
6
6
  readonly isArray = true;
7
+ clear(): void;
7
8
  load(spec: SerializedColumn): void;
8
9
  encode(value: any): any[];
9
10
  validate(value: any): boolean;
package/columns/tuple.js CHANGED
@@ -6,6 +6,10 @@ import { AColumn } from "./acolumn.js";
6
6
  class TupleColumn extends AColumn {
7
7
  values = [];
8
8
  isArray = true;
9
+ clear() {
10
+ this.values = [];
11
+ this.bitmap?.clear();
12
+ }
9
13
  load(spec) {
10
14
  this.values = spec.values;
11
15
  this.reindex();
@@ -9,6 +9,7 @@ export declare class TypedArrayColumn<T extends Row = Row> extends AColumn<T> {
9
9
  protected tmp: TypedArray;
10
10
  readonly isArray = false;
11
11
  constructor(id: ColumnID<T>, table: Table<T>);
12
+ clear(): void;
12
13
  load({ values }: SerializedColumn): void;
13
14
  validate(value: any): boolean;
14
15
  ensureRows(): void;
@@ -2,6 +2,7 @@ import { typedArray } from "@thi.ng/api/typedarray";
2
2
  import { isArray } from "@thi.ng/checks/is-array";
3
3
  import { isNumber } from "@thi.ng/checks/is-number";
4
4
  import {
5
+ INITIAL_CAPACITY,
5
6
  LIMITS
6
7
  } from "../api.js";
7
8
  import { __indexOfSingle, __lastIndexOfSingle } from "../internal/indexof.js";
@@ -18,9 +19,13 @@ class TypedArrayColumn extends AColumn {
18
19
  super(id, table);
19
20
  this.type = table.schema[id].type;
20
21
  this.limit = LIMITS[this.type];
21
- this.values = typedArray(this.type, 8);
22
+ this.values = typedArray(this.type, INITIAL_CAPACITY);
22
23
  this.tmp = typedArray(this.type, 1);
23
24
  }
25
+ clear() {
26
+ this.values = typedArray(this.type, INITIAL_CAPACITY);
27
+ this.bitmap?.clear();
28
+ }
24
29
  load({ values }) {
25
30
  this.values = __deserializeTyped(this.type, this.spec.flags, values);
26
31
  this.reindex();
@@ -10,11 +10,12 @@ export declare class VectorColumn<T extends Row = Row> extends AColumn<T> {
10
10
  protected tmp: TypedArray;
11
11
  readonly isArray = false;
12
12
  constructor(id: ColumnID<T>, table: Table<T>);
13
+ clear(): void;
13
14
  load({ values }: SerializedColumn): void;
14
15
  validate(value: any): boolean;
15
16
  ensureRows(): void;
16
17
  setRow(i: number, value: any): void;
17
- getRow(i: number): Float32Array<ArrayBufferLike> | Float64Array<ArrayBufferLike> | Int8Array<ArrayBufferLike> | Int16Array<ArrayBufferLike> | Int32Array<ArrayBufferLike> | Uint8Array<ArrayBufferLike> | Uint8ClampedArray<ArrayBufferLike> | Uint16Array<ArrayBufferLike> | Uint32Array<ArrayBufferLike>;
18
+ getRow(i: number): Uint32Array<ArrayBufferLike> | Float32Array<ArrayBufferLike> | Float64Array<ArrayBufferLike> | Int8Array<ArrayBufferLike> | Int16Array<ArrayBufferLike> | Int32Array<ArrayBufferLike> | Uint8Array<ArrayBufferLike> | Uint8ClampedArray<ArrayBufferLike> | Uint16Array<ArrayBufferLike>;
18
19
  getRowKey(i: number): string;
19
20
  valueKey(value: any): string | string[];
20
21
  removeRow(i: number): void;
package/columns/vector.js CHANGED
@@ -4,6 +4,7 @@ import { isArrayLike } from "@thi.ng/checks/is-arraylike";
4
4
  import { isNumber } from "@thi.ng/checks/is-number";
5
5
  import { unsupportedOp } from "@thi.ng/errors/unsupported";
6
6
  import {
7
+ INITIAL_CAPACITY,
7
8
  LIMITS
8
9
  } from "../api.js";
9
10
  import { __clampRange } from "../internal/indexof.js";
@@ -21,9 +22,13 @@ class VectorColumn extends AColumn {
21
22
  this.type = this.spec.type.split("v")[0];
22
23
  this.size = this.spec.cardinality[1];
23
24
  this.limit = LIMITS[this.type];
24
- this.values = typedArray(this.type, 8 * this.size);
25
+ this.values = typedArray(this.type, INITIAL_CAPACITY * this.size);
25
26
  this.tmp = typedArray(this.type, this.size);
26
27
  }
28
+ clear() {
29
+ this.values = typedArray(this.type, INITIAL_CAPACITY * this.size);
30
+ this.bitmap?.clear();
31
+ }
27
32
  load({ values }) {
28
33
  this.values = __deserializeTyped(this.type, this.spec.flags, values);
29
34
  this.reindex();
@@ -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.9.0",
3
+ "version": "0.10.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",
@@ -129,5 +129,5 @@
129
129
  "status": "alpha",
130
130
  "year": 2025
131
131
  },
132
- "gitHead": "1138bb64457a415733cf5c05d32c211ccfd6ee72\n"
132
+ "gitHead": "f2700429ff95ad80d2b4487f6bde4d7e220204c3\n"
133
133
  }
package/query.d.ts CHANGED
@@ -33,7 +33,7 @@ export declare class QueryCtx<T extends Row> {
33
33
  */
34
34
  [Symbol.iterator](): Generator<number, void, unknown>;
35
35
  clear(): void;
36
- makeMask(seed?: Uint32Array): Uint32Array<ArrayBuffer>;
36
+ makeMask(seed?: number | Uint32Array): Uint32Array<ArrayBuffer>;
37
37
  /**
38
38
  * Combines the `mask` with the context's mask (combined using bitwise AND).
39
39
  * If the context mask is still undefined, assigns `mask` as the initial
package/query.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { isArray } from "@thi.ng/checks/is-array";
2
+ import { isNumber } from "@thi.ng/checks/is-number";
2
3
  import { illegalArgs } from "@thi.ng/errors/illegal-arguments";
3
4
  import { unsupportedOp } from "@thi.ng/errors/unsupported";
4
5
  import { Bitfield } from "./bitmap.js";
@@ -111,7 +112,9 @@ class QueryCtx {
111
112
  }
112
113
  makeMask(seed) {
113
114
  const mask = new Uint32Array(this.size);
114
- if (seed) mask.set(seed);
115
+ if (seed) {
116
+ isNumber(seed) ? mask.fill(seed) : mask.set(seed);
117
+ }
115
118
  return mask;
116
119
  }
117
120
  /**
@@ -166,11 +169,7 @@ const execBitOr = (ctx, term, column) => {
166
169
  const b = bitmap.index.get(key)?.buffer;
167
170
  if (b) mask = ctx.makeMask(b);
168
171
  }
169
- if (mask) {
170
- term.type === "nor" ? ctx.mergeInvMask(mask) : ctx.mergeMask(mask);
171
- return true;
172
- }
173
- return false;
172
+ return __finalizeMask(ctx, mask, term.type === "nor");
174
173
  };
175
174
  const execOr = (ctx, term, column) => {
176
175
  const key = column.valueKey(term.value);
@@ -184,11 +183,7 @@ const execOr = (ctx, term, column) => {
184
183
  }
185
184
  }
186
185
  }
187
- if (mask) {
188
- term.type === "nor" ? ctx.mergeInvMask(mask) : ctx.mergeMask(mask);
189
- return true;
190
- }
191
- return false;
186
+ return __finalizeMask(ctx, mask, term.type === "nor");
192
187
  };
193
188
  const delegateOr = (ctx, term, column) => (term.value != null && column.bitmap ? execBitOr : execOr)(
194
189
  ctx,
@@ -198,15 +193,18 @@ const delegateOr = (ctx, term, column) => (term.value != null && column.bitmap ?
198
193
  const execBitAnd = (ctx, term, column) => {
199
194
  const bitmap = column.bitmap;
200
195
  const key = column.valueKey(term.value);
196
+ const isNeg = term.type === "nand";
201
197
  let mask;
202
198
  if (isArray(key)) {
203
- const colBitmaps = [];
204
199
  for (let k of key) {
205
200
  const b = bitmap.index.get(k)?.buffer;
206
- if (!b) return false;
207
- colBitmaps.push(b);
208
- }
209
- for (let b of colBitmaps) {
201
+ if (!b) {
202
+ if (isNeg) {
203
+ if (mask) mask.fill(0);
204
+ else mask = ctx.makeMask();
205
+ continue;
206
+ } else return false;
207
+ }
210
208
  if (mask) {
211
209
  const n = b.length;
212
210
  for (let i = 0; i < n; i++) mask[i] &= b[i];
@@ -217,16 +215,13 @@ const execBitAnd = (ctx, term, column) => {
217
215
  const b = bitmap.index.get(key)?.buffer;
218
216
  if (b) mask = ctx.makeMask(b);
219
217
  }
220
- if (mask) {
221
- term.type === "nand" ? ctx.mergeInvMask(mask) : ctx.mergeMask(mask);
222
- return true;
223
- }
224
- return false;
218
+ return __finalizeMask(ctx, mask, isNeg);
225
219
  };
226
220
  const execAnd = (ctx, term, column) => {
227
221
  const n = ctx.table.length;
228
222
  const key = column.valueKey(term.value) ?? null;
229
223
  const pred = column.isArray ? (row, v) => row.includes(v) : (row, v) => row === v;
224
+ const isNeg = term.type === "nand";
230
225
  let mask;
231
226
  for (let k of isArray(key) ? key : [key]) {
232
227
  let m;
@@ -241,20 +236,29 @@ const execAnd = (ctx, term, column) => {
241
236
  for (let i = 0; i < n; i++) mask[i] &= m[i];
242
237
  } else mask = m;
243
238
  } else {
244
- return false;
239
+ if (isNeg) {
240
+ if (mask) mask.fill(0);
241
+ else mask = ctx.makeMask();
242
+ } else return false;
245
243
  }
246
244
  }
247
- if (mask) {
248
- term.type === "nand" ? ctx.mergeInvMask(mask) : ctx.mergeMask(mask);
249
- return true;
250
- }
251
- return false;
245
+ return __finalizeMask(ctx, mask, isNeg);
252
246
  };
253
247
  const delegateAnd = (ctx, term, column) => (term.value != null && column.bitmap ? execBitAnd : execAnd)(
254
248
  ctx,
255
249
  term,
256
250
  column
257
251
  );
252
+ const __finalizeMask = (ctx, mask, isNeg) => {
253
+ if (mask) {
254
+ isNeg ? ctx.mergeInvMask(mask) : ctx.mergeMask(mask);
255
+ return true;
256
+ } else if (isNeg) {
257
+ if (!ctx.bitmap) ctx.bitmap = ctx.makeMask(-1);
258
+ return true;
259
+ }
260
+ return false;
261
+ };
258
262
  const QUERY_OPS = {
259
263
  or: { fn: delegateOr },
260
264
  nor: { fn: delegateOr },
package/table.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { Maybe } from "@thi.ng/api";
1
+ import type { IClear, ICopy, IEmpty, Maybe } from "@thi.ng/api";
2
2
  import { type ColumnID, type ColumnSchema, type ColumnSpec, type ColumnTypeSpec, type IColumn, type QueryTerm, type Row, type RowWithMeta, type SerializedTable } from "./api.js";
3
3
  import { Query } from "./query.js";
4
4
  /**
@@ -6,7 +6,7 @@ import { Query } from "./query.js";
6
6
  */
7
7
  export interface TableOpts {
8
8
  }
9
- export declare class Table<T extends Row> {
9
+ export declare class Table<T extends Row> implements IClear, ICopy<Table<T>>, IEmpty<Table<T>> {
10
10
  opts: TableOpts;
11
11
  schema: ColumnSchema<T>;
12
12
  columns: Record<ColumnID<T>, IColumn>;
@@ -15,12 +15,15 @@ export declare class Table<T extends Row> {
15
15
  constructor(schema: Record<ColumnID<T>, Partial<ColumnSpec> & {
16
16
  type: ColumnSpec["type"];
17
17
  }>, opts?: Partial<TableOpts>);
18
+ clear(): void;
19
+ copy(): Table<T>;
20
+ empty(): Table<T>;
18
21
  query(terms?: QueryTerm<T>[]): Query<T>;
19
22
  addColumn(id: ColumnID<T>, spec: Partial<ColumnSpec> & {
20
23
  type: ColumnSpec["type"];
21
24
  }): IColumn;
22
25
  removeColumn(id: ColumnID<T>): boolean;
23
- [Symbol.iterator](): Generator<Maybe<T>, void, unknown>;
26
+ [Symbol.iterator](): Generator<T, void, unknown>;
24
27
  reindex(): void;
25
28
  addRow(row: Partial<T>): void;
26
29
  addRows(rows: Iterable<Partial<T>>): void;
@@ -32,6 +35,7 @@ export declare class Table<T extends Row> {
32
35
  getPartialRow<K extends ColumnID<T>>(i: number, columns: K[], safe?: boolean): Maybe<Pick<T, K>>;
33
36
  getPartialRow<K extends ColumnID<T>>(i: number, columns: K[], safe?: boolean, includeID?: false): Maybe<Pick<T, K>>;
34
37
  getPartialRow<K extends ColumnID<T>>(i: number, columns: K[], safe?: boolean, includeID?: true): Maybe<RowWithMeta<Pick<T, K>>>;
38
+ slice(start?: number, end?: number): Table<T>;
35
39
  indexOf(id: ColumnID<T>, value: any, start?: number, end?: number): number;
36
40
  lastIndexOf(id: ColumnID<T>, value: any, start?: number, end?: number): number;
37
41
  validateRow(row: Partial<T>): void;
package/table.js CHANGED
@@ -13,6 +13,7 @@ import { TupleColumn } from "./columns/tuple.js";
13
13
  import { TypedArrayColumn } from "./columns/typedarray.js";
14
14
  import { VectorColumn } from "./columns/vector.js";
15
15
  import { __columnError } from "./internal/checks.js";
16
+ import { __clamp } from "./internal/indexof.js";
16
17
  import { Query } from "./query.js";
17
18
  class Table {
18
19
  opts;
@@ -31,6 +32,19 @@ class Table {
31
32
  this.opts = { ...opts };
32
33
  for (let id in schema) this.addColumn(id, schema[id]);
33
34
  }
35
+ clear() {
36
+ const { columns } = this;
37
+ for (let id in columns) columns[id].clear();
38
+ this.length = 0;
39
+ }
40
+ copy() {
41
+ const copy = new Table(this.schema, this.opts);
42
+ copy.addRows(this);
43
+ return copy;
44
+ }
45
+ empty() {
46
+ return new Table(this.schema, this.opts);
47
+ }
34
48
  query(terms) {
35
49
  return new Query(this, terms);
36
50
  }
@@ -104,6 +118,14 @@ class Table {
104
118
  }
105
119
  return row;
106
120
  }
121
+ slice(start = 0, end) {
122
+ const max = this.length;
123
+ start = __clamp(start, 0, max);
124
+ end = __clamp(end ?? max, start, max);
125
+ const copy = this.empty();
126
+ for (let i = start; i < end; i++) copy.addRow(this.getRow(i, true));
127
+ return copy;
128
+ }
107
129
  indexOf(id, value, start, end) {
108
130
  return this.columns[id]?.indexOf(value, start, end) ?? -1;
109
131
  }