@thi.ng/column-store 0.11.4 → 0.13.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
@@ -337,6 +337,24 @@ for(let result of query) { ... }
337
337
  const results = [...query];
338
338
  ```
339
339
 
340
+ Alternatively, queries can also be eagerly executed via
341
+ [`.exec()`](https://docs.thi.ng/umbrella/column-store/classes/Query.html#exec).
342
+ This will also include metadata alongside the results, useful for paging
343
+ results.
344
+
345
+ ```ts
346
+ // predefine query
347
+ const query = table.query().or("name", ["alice", "bob"]).limit(10);
348
+
349
+ const results = query.exec();
350
+ // {
351
+ // results: [{...}, ...],
352
+ // total: 123,
353
+ // offset: 0,
354
+ // limit: 10
355
+ // }
356
+ ```
357
+
340
358
  #### Optimized row iteration
341
359
 
342
360
  The query engine works by applying a number of [query
@@ -475,7 +493,7 @@ TODO
475
493
 
476
494
  ## Status
477
495
 
478
- **ALPHA** - bleeding edge / work-in-progress
496
+ **BETA** - possibly breaking changes forthcoming
479
497
 
480
498
  [Search or submit any issues for this package](https://github.com/thi-ng/umbrella/issues?q=%5Bcolumn-store%5D+in%3Atitle)
481
499
 
@@ -505,7 +523,7 @@ For Node.js REPL:
505
523
  const cs = await import("@thi.ng/column-store");
506
524
  ```
507
525
 
508
- Package sizes (brotli'd, pre-treeshake): ESM: 6.27 KB
526
+ Package sizes (brotli'd, pre-treeshake): ESM: 6.48 KB
509
527
 
510
528
  ## Dependencies
511
529
 
package/api.d.ts CHANGED
@@ -192,6 +192,25 @@ export interface QueryTermOpSpec {
192
192
  */
193
193
  fn: QueryTermOp;
194
194
  }
195
+ export interface QueryResult<T extends Row> {
196
+ /**
197
+ * Array of result rows (each incl. `__row` index)
198
+ */
199
+ results: RowWithMeta<T>[];
200
+ /**
201
+ * Total number of query results (might be more than in
202
+ * {@link QueryResult.results}).
203
+ */
204
+ total: number;
205
+ /**
206
+ * Page info. Configured result offset (via {@link Query.limit}).
207
+ */
208
+ offset: number;
209
+ /**
210
+ * Page info. Configured result limit (via {@link Query.limit}).
211
+ */
212
+ limit: number;
213
+ }
195
214
  /**
196
215
  * Initial capacity for typedarray and vector columns
197
216
  *
package/bitmap.js CHANGED
@@ -35,11 +35,11 @@ class BitmapIndex {
35
35
  * @param id
36
36
  */
37
37
  removeBit(rowID) {
38
- for (let bitmap of this.index.values()) bitmap.removeBit(rowID);
38
+ for (const bitmap of this.index.values()) bitmap.removeBit(rowID);
39
39
  }
40
40
  toJSON() {
41
41
  const res = {};
42
- for (let [k, bits] of this.index) {
42
+ for (const [k, bits] of this.index) {
43
43
  if (bits.buffer) res[k] = Array.from(bits.buffer);
44
44
  }
45
45
  return res;
@@ -62,7 +62,7 @@ class AColumn {
62
62
  const value = this.getRowKey(i);
63
63
  if (value == null) continue;
64
64
  if (isArray) {
65
- for (let x of value) bitmap.setBit(x, i);
65
+ for (const x of value) bitmap.setBit(x, i);
66
66
  } else bitmap.setBit(value, i);
67
67
  }
68
68
  }
@@ -2,6 +2,10 @@ 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 {
6
+ __frequenciesTuples,
7
+ __frequencyIndex
8
+ } from "../internal/frequencies.js";
5
9
  import { __indexOfTuple, __lastIndexOfTuple } from "../internal/indexof.js";
6
10
  import { __serializeDict } from "../internal/serialize.js";
7
11
  import { AColumn } from "./acolumn.js";
@@ -21,9 +25,12 @@ class DictTupleColumn extends AColumn {
21
25
  }
22
26
  reindex() {
23
27
  const dict = this.dict;
24
- const newDict = new BidirIndex();
28
+ const newDict = __frequencyIndex(
29
+ dict,
30
+ __frequenciesTuples(this.values)
31
+ );
25
32
  this.values = this.values.map(
26
- (ids) => ids ? newDict.addAll(dict.getAllIDs(ids)) : null
33
+ (ids) => ids ? newDict.getAll(dict.getAllIDs(ids)) : null
27
34
  );
28
35
  this.dict = newDict;
29
36
  super.updateBitmap();
@@ -48,7 +55,7 @@ class DictTupleColumn extends AColumn {
48
55
  values.length = n;
49
56
  values.fill($value, 0, n);
50
57
  if (bitmap && $value) {
51
- for (let x of $value) bitmap.ensure(x).fill(1, 0, n);
58
+ for (const x of $value) bitmap.ensure(x).fill(1, 0, n);
52
59
  }
53
60
  }
54
61
  setRow(i, value) {
@@ -57,8 +64,8 @@ class DictTupleColumn extends AColumn {
57
64
  const old = values[i];
58
65
  const encoded = values[i] = value != null ? this.table.schema[this.id].flags & FLAG_UNIQUE ? [...dict.addAllUnique(value)] : dict.addAll(value) : null;
59
66
  if (bitmap) {
60
- if (old) for (let x of old) bitmap.clearBit(x, i);
61
- if (encoded) for (let x of encoded) bitmap.setBit(x, i);
67
+ if (old) for (const x of old) bitmap.clearBit(x, i);
68
+ if (encoded) for (const x of encoded) bitmap.setBit(x, i);
62
69
  }
63
70
  }
64
71
  getRow(i) {
package/columns/dict.js CHANGED
@@ -4,6 +4,7 @@ import { decodeBinary, encodeBinary } from "@thi.ng/rle-pack/binary";
4
4
  import { decodeSimple, encodeSimple } from "@thi.ng/rle-pack/simple";
5
5
  import { FLAG_RLE } from "../api.js";
6
6
  import { __validateValue } from "../internal/checks.js";
7
+ import { __frequencies, __frequencyIndex } from "../internal/frequencies.js";
7
8
  import { __indexOfSingle, __lastIndexOfSingle } from "../internal/indexof.js";
8
9
  import { __serializeDict } from "../internal/serialize.js";
9
10
  import { AColumn } from "./acolumn.js";
@@ -23,9 +24,9 @@ class DictColumn extends AColumn {
23
24
  }
24
25
  reindex() {
25
26
  const dict = this.dict;
26
- const newDict = new BidirIndex();
27
+ const newDict = __frequencyIndex(dict, __frequencies(this.values));
27
28
  this.values = this.values.map(
28
- (x) => x != null ? newDict.add(dict.getID(x)) : null
29
+ (x) => x != null ? newDict.get(dict.getID(x)) : null
29
30
  );
30
31
  this.dict = newDict;
31
32
  super.updateBitmap();
package/columns/tuple.js CHANGED
@@ -30,7 +30,7 @@ class TupleColumn extends AColumn {
30
30
  values.length = n;
31
31
  values.fill(value ?? null, 0, n);
32
32
  if (bitmap && value) {
33
- for (let x of value) bitmap.ensure(x).fill(1, 0, n);
33
+ for (const x of value) bitmap.ensure(x).fill(1, 0, n);
34
34
  }
35
35
  }
36
36
  setRow(i, value) {
@@ -39,8 +39,8 @@ class TupleColumn extends AColumn {
39
39
  const old = values[i];
40
40
  const row = values[i] = value != null ? this.table.schema[this.id].flags & FLAG_UNIQUE ? [...new Set(value)] : value : null;
41
41
  if (bitmap) {
42
- if (old) for (let x of old) bitmap.clearBit(x, i);
43
- if (row) for (let x of row) bitmap.setBit(x, i);
42
+ if (old) for (const x of old) bitmap.clearBit(x, i);
43
+ if (row) for (const x of row) bitmap.setBit(x, i);
44
44
  }
45
45
  }
46
46
  getRow(i) {
@@ -15,7 +15,7 @@ export declare class VectorColumn<T extends Row = Row> extends AColumn<T> {
15
15
  validate(value: any): boolean;
16
16
  ensureRows(): void;
17
17
  setRow(i: number, value: any): void;
18
- 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>;
19
19
  getRowKey(i: number): string;
20
20
  valueKey(value: any): string | string[];
21
21
  removeRow(i: number): void;
@@ -0,0 +1,26 @@
1
+ import type { TypedArray } from "@thi.ng/api";
2
+ import { BidirIndex } from "@thi.ng/bidir-index";
3
+ /**
4
+ * Constructs a new bidir index, sorted by key frequency, based on given index
5
+ * and pre-computed histogram.
6
+ *
7
+ * @param dict
8
+ * @param bins
9
+ */
10
+ export declare const __frequencyIndex: (dict: BidirIndex<any>, bins: Map<number, number>) => BidirIndex<unknown>;
11
+ /**
12
+ * Computes histogram of `values`, sorted frequency. Returns array of
13
+ * `[value,count]` bins. Nullish values in
14
+ *
15
+ * @internal
16
+ */
17
+ export declare const __frequencies: (rows: TypedArray | (number | null)[]) => Map<number, number>;
18
+ /**
19
+ * Same as {@link __frequencies}, but for tuple-based columns. Computes
20
+ * histogram for unique component values inside tuples, not for the tuples
21
+ * themselves.
22
+ *
23
+ * @internal
24
+ */
25
+ export declare const __frequenciesTuples: (rows: (number[] | null)[]) => Map<number, number>;
26
+ //# sourceMappingURL=frequencies.d.ts.map
@@ -0,0 +1,33 @@
1
+ import { BidirIndex } from "@thi.ng/bidir-index";
2
+ const __frequencyIndex = (dict, bins) => {
3
+ const newDict = new BidirIndex();
4
+ for (const bin of [...bins].sort((a, b) => b[1] - a[1])) {
5
+ newDict.add(dict.getID(bin[0]));
6
+ }
7
+ return newDict;
8
+ };
9
+ const __frequencies = (rows) => {
10
+ const bins = /* @__PURE__ */ new Map();
11
+ for (const row of rows) {
12
+ if (row == null) continue;
13
+ const n = bins.get(row);
14
+ bins.set(row, n != null ? n + 1 : 1);
15
+ }
16
+ return bins;
17
+ };
18
+ const __frequenciesTuples = (rows) => {
19
+ const bins = /* @__PURE__ */ new Map();
20
+ for (const row of rows) {
21
+ if (row == null) continue;
22
+ for (const v of row) {
23
+ const n = bins.get(v);
24
+ bins.set(v, n != null ? n + 1 : 1);
25
+ }
26
+ }
27
+ return bins;
28
+ };
29
+ export {
30
+ __frequencies,
31
+ __frequenciesTuples,
32
+ __frequencyIndex
33
+ };
@@ -11,5 +11,5 @@ export declare const __lastIndexOfTuple: (needle: ArrayLike<any> | null, values:
11
11
  /** @internal */
12
12
  export declare const __clamp: (x: number, a: number, b: number) => number;
13
13
  /** @internal */
14
- export declare const __clampRange: (max: number, start: number, end?: number) => number[];
14
+ export declare const __clampRange: (max: number, start: number, end?: number) => [number, number];
15
15
  //# sourceMappingURL=indexof.d.ts.map
@@ -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.11.4",
3
+ "version": "0.13.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",
@@ -127,8 +127,8 @@
127
127
  }
128
128
  },
129
129
  "thi.ng": {
130
- "status": "alpha",
130
+ "status": "beta",
131
131
  "year": 2025
132
132
  },
133
- "gitHead": "7a21075f27ba00b28a6eb77e70918a4c7f4e6e68\n"
133
+ "gitHead": "7b9fc19fe963ac68bfe9d04c63f8a91cc3cfff5c\n"
134
134
  }
package/query.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { Comparator, Predicate } from "@thi.ng/api";
2
- import type { ColumnID, QueryTerm, QueryTermOpSpec, Row, RowWithMeta } from "./api.js";
2
+ import type { ColumnID, QueryResult, QueryTerm, QueryTermOpSpec, Row, RowWithMeta } from "./api.js";
3
3
  import type { Table } from "./table.js";
4
4
  export declare class Query<T extends Row> {
5
5
  readonly table: Table<T>;
@@ -7,8 +7,9 @@ export declare class Query<T extends Row> {
7
7
  protected _cmp?: Comparator<any>;
8
8
  protected _limit: number;
9
9
  protected _offset: number;
10
- constructor(table: Table<T>, terms?: QueryTerm<T>[]);
10
+ constructor(table: Table<T>, terms?: Iterable<QueryTerm<T>>);
11
11
  addTerm(term: QueryTerm<T>): this;
12
+ addTerms(terms: Iterable<QueryTerm<T>>): void;
12
13
  limit(limit: number, offset?: number): this;
13
14
  /**
14
15
  * Constructs a comparator for query results based on given sort criteria,
@@ -35,6 +36,12 @@ export declare class Query<T extends Row> {
35
36
  matchRow(pred: Predicate<T>): this;
36
37
  valueRange(column: ColumnID<T>, start: any, end?: any): this;
37
38
  rowRange(start?: number, end?: number): this;
39
+ /**
40
+ * Eagerly executes query with current terms and returns results as array in
41
+ * an object including total number of results and selected paging details
42
+ * (i.e. offset & limit).
43
+ */
44
+ exec(): QueryResult<T>;
38
45
  [Symbol.iterator](): Generator<RowWithMeta<T>, void, unknown>;
39
46
  }
40
47
  export declare class QueryCtx<T extends Row> {
@@ -43,11 +50,13 @@ export declare class QueryCtx<T extends Row> {
43
50
  readonly size: number;
44
51
  bitmap?: Uint32Array;
45
52
  constructor(query: Query<T>);
53
+ exec(): this;
46
54
  /**
47
55
  * If a bitmap is already present, yield iterator of only currently selected
48
56
  * row IDs, otherwise yields row IDs in `[0,table.length)` range.
49
57
  */
50
58
  [Symbol.iterator](): Generator<number, void, unknown>;
59
+ rows(): Generator<RowWithMeta<T>, void, unknown>;
51
60
  clear(): void;
52
61
  makeMask(seed?: number | Uint32Array): Uint32Array<ArrayBuffer>;
53
62
  /**
package/query.js CHANGED
@@ -12,9 +12,9 @@ import { Bitfield } from "./bitmap.js";
12
12
  import { __columnError } from "./internal/checks.js";
13
13
  import { __clampRange } from "./internal/indexof.js";
14
14
  class Query {
15
- constructor(table, terms = []) {
15
+ constructor(table, terms) {
16
16
  this.table = table;
17
- for (let term of terms) this.addTerm(term);
17
+ if (terms) this.addTerms(terms);
18
18
  }
19
19
  terms = [];
20
20
  _cmp;
@@ -25,6 +25,9 @@ class Query {
25
25
  this.terms.push(term);
26
26
  return this;
27
27
  }
28
+ addTerms(terms) {
29
+ for (const term of terms) this.addTerm(term);
30
+ }
28
31
  limit(limit, offset = 0) {
29
32
  this._limit = limit;
30
33
  this._offset = offset;
@@ -100,42 +103,60 @@ class Query {
100
103
  this.terms.push({ type: "rowRange", value: { start, end } });
101
104
  return this;
102
105
  }
103
- *[Symbol.iterator]() {
106
+ /**
107
+ * Eagerly executes query with current terms and returns results as array in
108
+ * an object including total number of results and selected paging details
109
+ * (i.e. offset & limit).
110
+ */
111
+ exec() {
104
112
  const { table, _limit, _offset } = this;
105
- const ctx = new QueryCtx(this);
106
- for (let term of this.terms) {
107
- const op = QUERY_OPS[term.type];
108
- let column;
109
- if (term.column) {
110
- column = ctx.table.columns[term.column];
111
- if (!column) illegalArgs(`column: ${String(term.column)}`);
112
- } else if (QUERY_OPS[term.type].mode !== "row") {
113
- illegalArgs(
114
- `query op: ${term.type} requires a column name given`
115
- );
113
+ const ctx = new QueryCtx(this).exec();
114
+ let rows = [];
115
+ const result = {
116
+ results: rows,
117
+ total: 0,
118
+ offset: _offset,
119
+ limit: _limit
120
+ };
121
+ if (!ctx.bitmap) return result;
122
+ if (this._cmp) {
123
+ rows = [...ctx.rows()].sort(this._cmp);
124
+ result.total = rows.length;
125
+ result.results = rows.slice(
126
+ ...__clampRange(rows.length, _offset, _offset + _limit)
127
+ );
128
+ } else {
129
+ let n = 0;
130
+ const max = _offset + _limit;
131
+ for (const i of ctx) {
132
+ if (n >= _offset && n < max) {
133
+ rows.push(table.getRow(i, false, true));
134
+ }
135
+ n++;
116
136
  }
117
- if (!op.fn(ctx, term, column)) return;
137
+ result.total = n;
118
138
  }
139
+ return result;
140
+ }
141
+ *[Symbol.iterator]() {
142
+ const { table, _limit, _offset } = this;
143
+ const ctx = new QueryCtx(this).exec();
119
144
  if (!ctx.bitmap) return;
120
145
  if (this._cmp) {
121
- const rows = [];
122
- for (let i of ctx) {
123
- rows.push(table.getRow(i, false, true));
124
- }
125
- rows.sort(this._cmp);
126
- for (let i = this._offset, n2 = Math.min(rows.length, i + _limit); i < n2; i++) {
127
- yield rows[i];
128
- }
129
- return;
130
- }
131
- let j = 0;
132
- const n = _offset + _limit;
133
- for (let i of ctx) {
134
- if (j >= _offset) {
135
- if (j >= n) return;
136
- yield table.getRow(i, false, true);
146
+ const rows = [...ctx.rows()].sort(this._cmp);
147
+ yield* rows.slice(
148
+ ...__clampRange(rows.length, _offset, _offset + _limit)
149
+ );
150
+ } else {
151
+ let j = 0;
152
+ const n = _offset + _limit;
153
+ for (const i of ctx) {
154
+ if (j >= _offset) {
155
+ if (j >= n) return;
156
+ yield table.getRow(i, false, true);
157
+ }
158
+ j++;
137
159
  }
138
- j++;
139
160
  }
140
161
  }
141
162
  }
@@ -148,6 +169,22 @@ class QueryCtx {
148
169
  table;
149
170
  size;
150
171
  bitmap;
172
+ exec() {
173
+ for (const term of this.query.terms) {
174
+ const op = QUERY_OPS[term.type];
175
+ let column;
176
+ if (term.column) {
177
+ column = this.table.columns[term.column];
178
+ if (!column) illegalArgs(`column: ${String(term.column)}`);
179
+ } else if (QUERY_OPS[term.type].mode !== "row") {
180
+ illegalArgs(
181
+ `query op: ${term.type} requires a column name given`
182
+ );
183
+ }
184
+ if (!op.fn(this, term, column)) break;
185
+ }
186
+ return this;
187
+ }
151
188
  /**
152
189
  * If a bitmap is already present, yield iterator of only currently selected
153
190
  * row IDs, otherwise yields row IDs in `[0,table.length)` range.
@@ -160,6 +197,10 @@ class QueryCtx {
160
197
  for (let i = 0; i < n; i++) yield i;
161
198
  }
162
199
  }
200
+ *rows() {
201
+ const table = this.query.table;
202
+ for (const i of this) yield table.getRow(i, false, true);
203
+ }
163
204
  clear() {
164
205
  this.bitmap = void 0;
165
206
  }
@@ -211,7 +252,7 @@ const execBitOr = (ctx, term, column) => {
211
252
  const key = column.valueKey(term.value);
212
253
  let mask;
213
254
  if (isArray(key)) {
214
- for (let k of key) {
255
+ for (const k of key) {
215
256
  const b = bitmap.index.get(k)?.buffer;
216
257
  if (!b) continue;
217
258
  if (mask) {
@@ -228,8 +269,8 @@ const execOr = (ctx, term, column) => {
228
269
  const key = column.valueKey(term.value);
229
270
  const pred = column.isArray ? (row, k) => row.includes(k) : (row, k) => row === k;
230
271
  let mask;
231
- for (let k of isArray(key) ? key : [key]) {
232
- for (let i of ctx) {
272
+ for (const k of isArray(key) ? key : [key]) {
273
+ for (const i of ctx) {
233
274
  if (pred(column.getRowKey(i), k)) {
234
275
  if (!mask) mask = ctx.makeMask();
235
276
  mask[i >>> 5] |= 1 << (i & 31);
@@ -249,7 +290,7 @@ const execBitAnd = (ctx, term, column) => {
249
290
  const isNeg = term.type === "nand";
250
291
  let mask;
251
292
  if (isArray(key)) {
252
- for (let k of key) {
293
+ for (const k of key) {
253
294
  const b = bitmap.index.get(k)?.buffer;
254
295
  if (!b) {
255
296
  if (isNeg) {
@@ -276,9 +317,9 @@ const execAnd = (ctx, term, column) => {
276
317
  const pred = column.isArray ? (row, v) => row.includes(v) : (row, v) => row === v;
277
318
  const isNeg = term.type === "nand";
278
319
  let mask;
279
- for (let k of isArray(key) ? key : [key]) {
320
+ for (const k of isArray(key) ? key : [key]) {
280
321
  let m;
281
- for (let i of ctx) {
322
+ for (const i of ctx) {
282
323
  if (pred(column.getRowKey(i), k)) {
283
324
  if (!m) m = ctx.makeMask();
284
325
  m[i >>> 5] |= 1 << (i & 31);
@@ -321,7 +362,7 @@ const QUERY_OPS = {
321
362
  fn: (ctx, term, column) => {
322
363
  const pred = term.value;
323
364
  let mask;
324
- for (let i of ctx) {
365
+ for (const i of ctx) {
325
366
  if (pred(column.getRow(i))) {
326
367
  if (!mask) mask = ctx.makeMask();
327
368
  mask[i >>> 5] |= 1 << (i & 31);
@@ -338,7 +379,7 @@ const QUERY_OPS = {
338
379
  const columns = term.params;
339
380
  const pred = term.value;
340
381
  let mask;
341
- for (let i of ctx) {
382
+ for (const i of ctx) {
342
383
  if (pred(table.getPartialRow(i, columns, false))) {
343
384
  if (!mask) mask = ctx.makeMask();
344
385
  mask[i >>> 5] |= 1 << (i & 31);
@@ -354,7 +395,7 @@ const QUERY_OPS = {
354
395
  const table = ctx.table;
355
396
  const pred = term.value;
356
397
  let mask;
357
- for (let i of ctx) {
398
+ for (const i of ctx) {
358
399
  if (pred(table.getRow(i, false))) {
359
400
  if (!mask) mask = ctx.makeMask();
360
401
  mask[i >>> 5] |= 1 << (i & 31);
package/table.js CHANGED
@@ -86,7 +86,7 @@ class Table {
86
86
  this.length++;
87
87
  }
88
88
  addRows(rows) {
89
- for (let row of rows) this.addRow(row);
89
+ for (const row of rows) this.addRow(row);
90
90
  }
91
91
  updateRow(i, row) {
92
92
  if (i < 0 || i >= this.length) illegalArgs(`row ID: ${i}`);
@@ -113,7 +113,7 @@ class Table {
113
113
  getPartialRow(i, columns, safe = true, includeID = false) {
114
114
  if (safe && (i < 0 || i >= this.length)) return;
115
115
  const row = includeID ? { __row: i } : {};
116
- for (let id of columns) {
116
+ for (const id of columns) {
117
117
  row[id] = this.columns[id]?.getRow(i);
118
118
  }
119
119
  return row;