@thi.ng/column-store 0.12.0 → 0.13.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 +20 -2
- package/api.d.ts +19 -0
- package/internal/indexof.d.ts +1 -1
- package/package.json +3 -3
- package/query.d.ts +9 -1
- package/query.js +68 -30
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
|
-
**
|
|
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.
|
|
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/internal/indexof.d.ts
CHANGED
|
@@ -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
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thi.ng/column-store",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.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",
|
|
@@ -127,8 +127,8 @@
|
|
|
127
127
|
}
|
|
128
128
|
},
|
|
129
129
|
"thi.ng": {
|
|
130
|
-
"status": "
|
|
130
|
+
"status": "beta",
|
|
131
131
|
"year": 2025
|
|
132
132
|
},
|
|
133
|
-
"gitHead": "
|
|
133
|
+
"gitHead": "b3bcd5623ec78379cceebc6808d7c7b262dfc168\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>;
|
|
@@ -36,6 +36,12 @@ export declare class Query<T extends Row> {
|
|
|
36
36
|
matchRow(pred: Predicate<T>): this;
|
|
37
37
|
valueRange(column: ColumnID<T>, start: any, end?: any): this;
|
|
38
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>;
|
|
39
45
|
[Symbol.iterator](): Generator<RowWithMeta<T>, void, unknown>;
|
|
40
46
|
}
|
|
41
47
|
export declare class QueryCtx<T extends Row> {
|
|
@@ -44,11 +50,13 @@ export declare class QueryCtx<T extends Row> {
|
|
|
44
50
|
readonly size: number;
|
|
45
51
|
bitmap?: Uint32Array;
|
|
46
52
|
constructor(query: Query<T>);
|
|
53
|
+
exec(): this;
|
|
47
54
|
/**
|
|
48
55
|
* If a bitmap is already present, yield iterator of only currently selected
|
|
49
56
|
* row IDs, otherwise yields row IDs in `[0,table.length)` range.
|
|
50
57
|
*/
|
|
51
58
|
[Symbol.iterator](): Generator<number, void, unknown>;
|
|
59
|
+
rows(): Generator<RowWithMeta<T>, void, unknown>;
|
|
52
60
|
clear(): void;
|
|
53
61
|
makeMask(seed?: number | Uint32Array): Uint32Array<ArrayBuffer>;
|
|
54
62
|
/**
|
package/query.js
CHANGED
|
@@ -103,42 +103,60 @@ class Query {
|
|
|
103
103
|
this.terms.push({ type: "rowRange", value: { start, end } });
|
|
104
104
|
return this;
|
|
105
105
|
}
|
|
106
|
-
|
|
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() {
|
|
107
112
|
const { table, _limit, _offset } = this;
|
|
108
|
-
const ctx = new QueryCtx(this);
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
`query op: ${term.type} requires a column name given`
|
|
118
|
-
);
|
|
119
|
-
}
|
|
120
|
-
if (!op.fn(ctx, term, column)) return;
|
|
121
|
-
}
|
|
122
|
-
if (!ctx.bitmap) return;
|
|
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 && this.terms.length) return result;
|
|
123
122
|
if (this._cmp) {
|
|
124
|
-
|
|
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;
|
|
125
131
|
for (const i of ctx) {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
yield rows[i];
|
|
132
|
+
if (n >= _offset && n < max) {
|
|
133
|
+
rows.push(table.getRow(i, false, true));
|
|
134
|
+
}
|
|
135
|
+
n++;
|
|
131
136
|
}
|
|
132
|
-
|
|
137
|
+
result.total = n;
|
|
133
138
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
139
|
+
return result;
|
|
140
|
+
}
|
|
141
|
+
*[Symbol.iterator]() {
|
|
142
|
+
const { table, _limit, _offset } = this;
|
|
143
|
+
const ctx = new QueryCtx(this).exec();
|
|
144
|
+
if (!ctx.bitmap && this.terms.length) return;
|
|
145
|
+
if (this._cmp) {
|
|
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++;
|
|
140
159
|
}
|
|
141
|
-
j++;
|
|
142
160
|
}
|
|
143
161
|
}
|
|
144
162
|
}
|
|
@@ -151,6 +169,22 @@ class QueryCtx {
|
|
|
151
169
|
table;
|
|
152
170
|
size;
|
|
153
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
|
+
}
|
|
154
188
|
/**
|
|
155
189
|
* If a bitmap is already present, yield iterator of only currently selected
|
|
156
190
|
* row IDs, otherwise yields row IDs in `[0,table.length)` range.
|
|
@@ -163,6 +197,10 @@ class QueryCtx {
|
|
|
163
197
|
for (let i = 0; i < n; i++) yield i;
|
|
164
198
|
}
|
|
165
199
|
}
|
|
200
|
+
*rows() {
|
|
201
|
+
const table = this.query.table;
|
|
202
|
+
for (const i of this) yield table.getRow(i, false, true);
|
|
203
|
+
}
|
|
166
204
|
clear() {
|
|
167
205
|
this.bitmap = void 0;
|
|
168
206
|
}
|