@thi.ng/column-store 0.10.1 → 0.11.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/README.md CHANGED
@@ -39,6 +39,7 @@
39
39
  - [Predicate-based matchers](#predicate-based-matchers)
40
40
  - [Row ranges](#row-ranges)
41
41
  - [Value ranges](#value-ranges)
42
+ - [Result order and pagination](#result-order-and-pagination)
42
43
  - [Custom operators](#custom-operators)
43
44
  - [Result aggregation](#result-aggregation)
44
45
  - [Query ranges](#query-ranges)
@@ -450,6 +451,15 @@ operator selects rows based on a given column's `start` .. `end` vaulue range
450
451
  query.valueRange("id", 100, 109);
451
452
  ```
452
453
 
454
+ ### Result order and pagination
455
+
456
+ - [`sortBy()`](https://docs.thi.ng/umbrella/column-store/classes/Query.html#sortby)
457
+ allows query results to be ordered (ascending or descending) via an arbitrary
458
+ number of sort columns or criteria, applied in the given order.
459
+ - [`limit()`](https://docs.thi.ng/umbrella/column-store/classes/Query.html#limit)
460
+ provides basic pagination of query results by specifying a max. number of
461
+ results and start offset
462
+
453
463
  ### Custom operators
454
464
 
455
465
  Custom query operators can be registered via
@@ -495,13 +505,14 @@ For Node.js REPL:
495
505
  const cs = await import("@thi.ng/column-store");
496
506
  ```
497
507
 
498
- Package sizes (brotli'd, pre-treeshake): ESM: 5.78 KB
508
+ Package sizes (brotli'd, pre-treeshake): ESM: 6.28 KB
499
509
 
500
510
  ## Dependencies
501
511
 
502
512
  - [@thi.ng/api](https://github.com/thi-ng/umbrella/tree/develop/packages/api)
503
513
  - [@thi.ng/bidir-index](https://github.com/thi-ng/umbrella/tree/develop/packages/bidir-index)
504
514
  - [@thi.ng/checks](https://github.com/thi-ng/umbrella/tree/develop/packages/checks)
515
+ - [@thi.ng/compare](https://github.com/thi-ng/umbrella/tree/develop/packages/compare)
505
516
  - [@thi.ng/errors](https://github.com/thi-ng/umbrella/tree/develop/packages/errors)
506
517
  - [@thi.ng/rle-pack](https://github.com/thi-ng/umbrella/tree/develop/packages/rle-pack)
507
518
 
@@ -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): Uint32Array<ArrayBufferLike> | Float32Array<ArrayBufferLike> | Float64Array<ArrayBufferLike> | Int8Array<ArrayBufferLike> | Int16Array<ArrayBufferLike> | Int32Array<ArrayBufferLike> | Uint8Array<ArrayBufferLike> | Uint8ClampedArray<ArrayBufferLike> | Uint16Array<ArrayBufferLike>;
18
+ getRow(i: number): Float32Array<ArrayBufferLike> | Float64Array<ArrayBufferLike> | Int8Array<ArrayBufferLike> | Int16Array<ArrayBufferLike> | Int32Array<ArrayBufferLike> | Uint8Array<ArrayBufferLike> | Uint8ClampedArray<ArrayBufferLike> | Uint16Array<ArrayBufferLike> | Uint32Array<ArrayBufferLike>;
19
19
  getRowKey(i: number): string;
20
20
  valueKey(value: any): string | string[];
21
21
  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[]) => 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.10.1",
3
+ "version": "0.11.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",
@@ -40,11 +40,12 @@
40
40
  "tool:tangle": "../../node_modules/.bin/tangle src/**/*.ts"
41
41
  },
42
42
  "dependencies": {
43
- "@thi.ng/api": "^8.12.14",
44
- "@thi.ng/bidir-index": "^1.5.0",
45
- "@thi.ng/checks": "^3.8.4",
46
- "@thi.ng/errors": "^2.6.3",
47
- "@thi.ng/rle-pack": "^3.2.1"
43
+ "@thi.ng/api": "^8.12.15",
44
+ "@thi.ng/bidir-index": "^1.5.1",
45
+ "@thi.ng/checks": "^3.8.5",
46
+ "@thi.ng/compare": "^2.5.1",
47
+ "@thi.ng/errors": "^2.6.4",
48
+ "@thi.ng/rle-pack": "^3.2.2"
48
49
  },
49
50
  "devDependencies": {
50
51
  "esbuild": "^0.27.2",
@@ -129,5 +130,5 @@
129
130
  "status": "alpha",
130
131
  "year": 2025
131
132
  },
132
- "gitHead": "f2700429ff95ad80d2b4487f6bde4d7e220204c3\n"
133
+ "gitHead": "8f50352caab9ec7757d645c0afa605dfb5427abe\n"
133
134
  }
package/query.d.ts CHANGED
@@ -1,11 +1,27 @@
1
- import type { Predicate } from "@thi.ng/api";
2
- import type { ColumnID, QueryTerm, QueryTermOpSpec, Row } from "./api.js";
1
+ import type { Comparator, Predicate } from "@thi.ng/api";
2
+ import type { ColumnID, 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>;
6
6
  terms: QueryTerm<T>[];
7
+ protected _cmp?: Comparator<any>;
8
+ protected _limit: number;
9
+ protected _offset: number;
7
10
  constructor(table: Table<T>, terms?: QueryTerm<T>[]);
8
11
  addTerm(term: QueryTerm<T>): this;
12
+ limit(limit: number, offset?: number): void;
13
+ /**
14
+ * Constructs a comparator for query results based on given sort criteria,
15
+ * which are applied in given order. Each criteria can be on of:
16
+ *
17
+ * - comparator (applied to full rows)
18
+ * - columnID
19
+ * - tuple of `[columnID, boolean]` (if boolean is true, sorts in ascending order)
20
+ * - tuple of `[columnID, comparator]`
21
+ *
22
+ * @param order
23
+ */
24
+ sortBy(...order: (ColumnID<T> | Comparator<T> | [ColumnID<T>, boolean | Comparator<any>])[]): this;
9
25
  /** Alias for {@link Query.or} */
10
26
  where: (column: ColumnID<T>, value: any) => this;
11
27
  /** Alias for {@link Query.nor} */
@@ -19,7 +35,7 @@ export declare class Query<T extends Row> {
19
35
  matchRow(pred: Predicate<T>): this;
20
36
  valueRange(column: ColumnID<T>, start: any, end?: any): this;
21
37
  rowRange(start?: number, end?: number): this;
22
- [Symbol.iterator](): Generator<import("./api.js").RowWithMeta<T>, void, unknown>;
38
+ [Symbol.iterator](): Generator<RowWithMeta<T>, void, unknown>;
23
39
  }
24
40
  export declare class QueryCtx<T extends Row> {
25
41
  readonly query: Query<T>;
package/query.js CHANGED
@@ -1,5 +1,11 @@
1
1
  import { isArray } from "@thi.ng/checks/is-array";
2
+ import { isFunction } from "@thi.ng/checks/is-function";
2
3
  import { isNumber } from "@thi.ng/checks/is-number";
4
+ import { isString } from "@thi.ng/checks/is-string";
5
+ import { compare } from "@thi.ng/compare/compare";
6
+ import { composeComparators } from "@thi.ng/compare/compose";
7
+ import { compareByKey } from "@thi.ng/compare/keys";
8
+ import { reverse } from "@thi.ng/compare/reverse";
3
9
  import { illegalArgs } from "@thi.ng/errors/illegal-arguments";
4
10
  import { unsupportedOp } from "@thi.ng/errors/unsupported";
5
11
  import { Bitfield } from "./bitmap.js";
@@ -11,11 +17,39 @@ class Query {
11
17
  for (let term of terms) this.addTerm(term);
12
18
  }
13
19
  terms = [];
20
+ _cmp;
21
+ _limit = Infinity;
22
+ _offset = 0;
14
23
  addTerm(term) {
15
24
  if (!QUERY_OPS[term.type]) unsupportedOp(`query type: ${term.type}`);
16
25
  this.terms.push(term);
17
26
  return this;
18
27
  }
28
+ limit(limit, offset = 0) {
29
+ this._limit = limit;
30
+ this._offset = offset;
31
+ }
32
+ /**
33
+ * Constructs a comparator for query results based on given sort criteria,
34
+ * which are applied in given order. Each criteria can be on of:
35
+ *
36
+ * - comparator (applied to full rows)
37
+ * - columnID
38
+ * - tuple of `[columnID, boolean]` (if boolean is true, sorts in ascending order)
39
+ * - tuple of `[columnID, comparator]`
40
+ *
41
+ * @param order
42
+ */
43
+ sortBy(...order) {
44
+ const fns = order.map(
45
+ (x) => isFunction(x) ? x : isString(x) ? compareByKey(x) : compareByKey(
46
+ x[0],
47
+ isFunction(x[1]) ? x[1] : x[1] ? compare : reverse(compare)
48
+ )
49
+ );
50
+ this._cmp = composeComparators(...fns);
51
+ return this;
52
+ }
19
53
  /** Alias for {@link Query.or} */
20
54
  where = (column, value) => this.or(column, value);
21
55
  /** Alias for {@link Query.nor} */
@@ -66,7 +100,7 @@ class Query {
66
100
  return this;
67
101
  }
68
102
  *[Symbol.iterator]() {
69
- const { table } = this;
103
+ const { table, _limit, _offset } = this;
70
104
  const ctx = new QueryCtx(this);
71
105
  for (let term of this.terms) {
72
106
  const op = QUERY_OPS[term.type];
@@ -81,8 +115,25 @@ class Query {
81
115
  }
82
116
  if (!op.fn(ctx, term, column)) return;
83
117
  }
84
- if (ctx.bitmap) {
85
- for (let i of ctx) yield table.getRow(i, false, true);
118
+ if (!ctx.bitmap) return;
119
+ if (this._cmp) {
120
+ const rows = [];
121
+ for (let i of ctx) {
122
+ rows.push(table.getRow(i, false, true));
123
+ }
124
+ rows.sort(this._cmp);
125
+ for (let i = this._offset, n = Math.min(rows.length, i + this._limit); i < n; i++) {
126
+ yield rows[i];
127
+ }
128
+ return;
129
+ }
130
+ let j = 0;
131
+ for (let i of ctx) {
132
+ if (j >= _offset) {
133
+ if (j >= _limit) return;
134
+ yield table.getRow(i, false, true);
135
+ }
136
+ j++;
86
137
  }
87
138
  }
88
139
  }
package/table.d.ts CHANGED
@@ -35,6 +35,17 @@ export declare class Table<T extends Row> implements IClear, ICopy<Table<T>>, IE
35
35
  getPartialRow<K extends ColumnID<T>>(i: number, columns: K[], safe?: boolean): Maybe<Pick<T, K>>;
36
36
  getPartialRow<K extends ColumnID<T>>(i: number, columns: K[], safe?: boolean, includeID?: false): Maybe<Pick<T, K>>;
37
37
  getPartialRow<K extends ColumnID<T>>(i: number, columns: K[], safe?: boolean, includeID?: true): Maybe<RowWithMeta<Pick<T, K>>>;
38
+ getRows(start?: number, end?: number, includeID?: false): IterableIterator<T>;
39
+ getRows(start?: number, end?: number, includeID?: true): IterableIterator<RowWithMeta<T>>;
40
+ getPartialRows<K extends ColumnID<T>>(columns: K[], start?: number, end?: number, includeID?: false): IterableIterator<Pick<T, K>>;
41
+ getPartialRows<K extends ColumnID<T>>(columns: K[], start?: number, end?: number, includeID?: true): IterableIterator<RowWithMeta<Pick<T, K>>>;
42
+ /**
43
+ * Creates a new table with same schema and options, but only containing the
44
+ * subset of rows in the `[start,end)` interval.
45
+ *
46
+ * @param start
47
+ * @param end
48
+ */
38
49
  slice(start?: number, end?: number): Table<T>;
39
50
  indexOf(id: ColumnID<T>, value: any, start?: number, end?: number): number;
40
51
  lastIndexOf(id: ColumnID<T>, value: any, start?: number, end?: number): number;
package/table.js CHANGED
@@ -13,7 +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
+ import { __clampRange } from "./internal/indexof.js";
17
17
  import { Query } from "./query.js";
18
18
  class Table {
19
19
  opts;
@@ -118,10 +118,25 @@ class Table {
118
118
  }
119
119
  return row;
120
120
  }
121
+ *getRows(start = 0, end, includeID = false) {
122
+ [start, end] = __clampRange(this.length, start, end);
123
+ for (let i = start; i < end; i++)
124
+ yield this.getRow(i, false, includeID);
125
+ }
126
+ *getPartialRows(columns, start = 0, end, includeID = false) {
127
+ [start, end] = __clampRange(this.length, start, end);
128
+ for (let i = start; i < end; i++)
129
+ yield this.getPartialRow(i, columns, false, includeID);
130
+ }
131
+ /**
132
+ * Creates a new table with same schema and options, but only containing the
133
+ * subset of rows in the `[start,end)` interval.
134
+ *
135
+ * @param start
136
+ * @param end
137
+ */
121
138
  slice(start = 0, end) {
122
- const max = this.length;
123
- start = __clamp(start, 0, max);
124
- end = __clamp(end ?? max, start, max);
139
+ [start, end] = __clampRange(this.length, start, end);
125
140
  const copy = this.empty();
126
141
  for (let i = start; i < end; i++) copy.addRow(this.getRow(i, true));
127
142
  return copy;