@thi.ng/column-store 0.3.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/api.d.ts +15 -0
- package/bitmap.d.ts +1 -0
- package/bitmap.js +18 -4
- package/columns/acolumn.d.ts +1 -0
- package/columns/dict-tuple.d.ts +1 -0
- package/columns/dict-tuple.js +10 -0
- package/columns/dict.d.ts +2 -1
- package/columns/dict.js +13 -2
- package/columns/plain.d.ts +1 -0
- package/columns/plain.js +11 -0
- package/columns/tuple.d.ts +1 -0
- package/columns/tuple.js +10 -0
- package/columns/typedarray.d.ts +1 -0
- package/columns/typedarray.js +11 -0
- package/columns/vector.d.ts +1 -0
- package/columns/vector.js +17 -0
- package/internal/indexof.d.ts +6 -0
- package/internal/indexof.js +34 -0
- package/package.json +2 -2
- package/table.d.ts +1 -0
- package/table.js +5 -4
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
|
|
53
|
-
if (!
|
|
54
|
-
for (let i = 0, n =
|
|
55
|
-
let bits =
|
|
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);
|
package/columns/acolumn.d.ts
CHANGED
|
@@ -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;
|
package/columns/dict-tuple.d.ts
CHANGED
|
@@ -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: {
|
package/columns/dict-tuple.js
CHANGED
|
@@ -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 |
|
|
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);
|
package/columns/plain.d.ts
CHANGED
|
@@ -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
|
}
|
package/columns/tuple.d.ts
CHANGED
|
@@ -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;
|
package/columns/typedarray.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ export declare class TypedArrayColumn extends AColumn implements IColumn {
|
|
|
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
22
|
values: any[];
|
package/columns/typedarray.js
CHANGED
|
@@ -4,6 +4,7 @@ import { isNumber } from "@thi.ng/checks/is-number";
|
|
|
4
4
|
import {
|
|
5
5
|
LIMITS
|
|
6
6
|
} from "../api.js";
|
|
7
|
+
import { __indexOfSingle } from "../internal/indexof.js";
|
|
7
8
|
import { __replaceValue } from "../internal/replace.js";
|
|
8
9
|
import { __deserializeTyped, __serializeTyped } from "../internal/serialize.js";
|
|
9
10
|
import { AColumn } from "./acolumn.js";
|
|
@@ -67,6 +68,16 @@ class TypedArrayColumn extends AColumn {
|
|
|
67
68
|
this.values[this.table.length - 1] = 0;
|
|
68
69
|
this.bitmap?.removeBit(i);
|
|
69
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
|
+
}
|
|
70
81
|
replaceValue(currValue, newValue) {
|
|
71
82
|
return __replaceValue(this.bitmap, this.values, currValue, newValue);
|
|
72
83
|
}
|
package/columns/vector.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ export declare class VectorColumn extends AColumn implements IColumn {
|
|
|
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
|
@@ -76,6 +76,23 @@ class VectorColumn extends AColumn {
|
|
|
76
76
|
this.values.fill(0, (length - 1) * size);
|
|
77
77
|
this.bitmap?.removeBit(i);
|
|
78
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
|
+
}
|
|
79
96
|
replaceValue() {
|
|
80
97
|
unsupportedOp("TODO");
|
|
81
98
|
}
|
|
@@ -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
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thi.ng/column-store",
|
|
3
|
-
"version": "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": "
|
|
129
|
+
"gitHead": "5c620a1aa00535cc7c674794790edd87b2e5e526\n"
|
|
130
130
|
}
|
package/table.d.ts
CHANGED
|
@@ -27,6 +27,7 @@ export declare class Table {
|
|
|
27
27
|
removeRow(i: number): void;
|
|
28
28
|
getRow(i: number, safe?: boolean, includeID?: boolean): Row | undefined;
|
|
29
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
|
@@ -98,6 +98,9 @@ class Table {
|
|
|
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() {
|