@luxdb/sdk 1.1.0 → 1.2.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/dist/index.d.ts +67 -10
- package/dist/index.js +141 -20
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,17 +1,9 @@
|
|
|
1
|
-
import Redis, { RedisOptions } from 'ioredis';
|
|
1
|
+
import Redis, { type RedisOptions } from 'ioredis';
|
|
2
2
|
export interface VSearchResult {
|
|
3
3
|
key: string;
|
|
4
4
|
similarity: number;
|
|
5
5
|
metadata?: Record<string, unknown>;
|
|
6
6
|
}
|
|
7
|
-
export interface VSearchOptions {
|
|
8
|
-
k: number;
|
|
9
|
-
filter?: {
|
|
10
|
-
key: string;
|
|
11
|
-
value: string;
|
|
12
|
-
};
|
|
13
|
-
meta?: boolean;
|
|
14
|
-
}
|
|
15
7
|
export interface TSSample {
|
|
16
8
|
timestamp: number;
|
|
17
9
|
value: number;
|
|
@@ -36,8 +28,66 @@ export interface KSubEvent {
|
|
|
36
28
|
key: string;
|
|
37
29
|
operation: string;
|
|
38
30
|
}
|
|
31
|
+
export interface TableRow {
|
|
32
|
+
id: number;
|
|
33
|
+
[field: string]: unknown;
|
|
34
|
+
}
|
|
35
|
+
declare class TableQueryBuilder {
|
|
36
|
+
private client;
|
|
37
|
+
private name;
|
|
38
|
+
private conditions;
|
|
39
|
+
private orderField?;
|
|
40
|
+
private orderDir?;
|
|
41
|
+
private limitCount?;
|
|
42
|
+
private joinTable?;
|
|
43
|
+
constructor(client: Lux, name: string);
|
|
44
|
+
where(field: string, op: '=' | '!=' | '>' | '<' | '>=' | '<=', value: string | number | boolean): this;
|
|
45
|
+
orderBy(field: string, dir?: 'asc' | 'desc'): this;
|
|
46
|
+
limit(n: number): this;
|
|
47
|
+
join(table: string): this;
|
|
48
|
+
run(): Promise<TableRow[]>;
|
|
49
|
+
insert(data: Record<string, unknown>): Promise<number>;
|
|
50
|
+
update(id: number, data: Record<string, unknown>): Promise<string>;
|
|
51
|
+
delete(...ids: number[]): Promise<number>;
|
|
52
|
+
}
|
|
53
|
+
declare class VectorNamespace {
|
|
54
|
+
private client;
|
|
55
|
+
constructor(client: Lux);
|
|
56
|
+
set(key: string, vector: number[], metadata?: Record<string, unknown>): Promise<string>;
|
|
57
|
+
get(key: string): Promise<{
|
|
58
|
+
dims: number;
|
|
59
|
+
vector: number[];
|
|
60
|
+
metadata?: Record<string, unknown>;
|
|
61
|
+
} | null>;
|
|
62
|
+
search(query: number[], options: {
|
|
63
|
+
topK: number;
|
|
64
|
+
filter?: {
|
|
65
|
+
key: string;
|
|
66
|
+
value: string;
|
|
67
|
+
};
|
|
68
|
+
meta?: boolean;
|
|
69
|
+
}): Promise<VSearchResult[]>;
|
|
70
|
+
count(): Promise<number>;
|
|
71
|
+
}
|
|
72
|
+
declare class TimeSeriesNamespace {
|
|
73
|
+
private client;
|
|
74
|
+
constructor(client: Lux);
|
|
75
|
+
add(key: string, value: number, options?: {
|
|
76
|
+
timestamp?: number | '*';
|
|
77
|
+
retention?: number;
|
|
78
|
+
labels?: Record<string, string>;
|
|
79
|
+
}): Promise<number>;
|
|
80
|
+
get(key: string): Promise<TSSample | null>;
|
|
81
|
+
range(key: string, from: number | '-', to: number | '+', options?: TSRangeOptions): Promise<TSSample[]>;
|
|
82
|
+
mrange(from: number | '-', to: number | '+', filter: string, options?: TSRangeOptions): Promise<TSMRangeResult[]>;
|
|
83
|
+
info(key: string): Promise<Record<string, unknown>>;
|
|
84
|
+
}
|
|
39
85
|
export declare class Lux extends Redis {
|
|
86
|
+
vectors: VectorNamespace;
|
|
87
|
+
timeseries: TimeSeriesNamespace;
|
|
40
88
|
constructor(options?: RedisOptions | string);
|
|
89
|
+
table(name: string): TableQueryBuilder;
|
|
90
|
+
_tquery(args: string[]): Promise<TableRow[]>;
|
|
41
91
|
vset(key: string, vector: number[], options?: {
|
|
42
92
|
metadata?: Record<string, unknown>;
|
|
43
93
|
ex?: number;
|
|
@@ -48,7 +98,14 @@ export declare class Lux extends Redis {
|
|
|
48
98
|
vector: number[];
|
|
49
99
|
metadata?: Record<string, unknown>;
|
|
50
100
|
} | null>;
|
|
51
|
-
vsearch(query: number[], options:
|
|
101
|
+
vsearch(query: number[], options: {
|
|
102
|
+
k: number;
|
|
103
|
+
filter?: {
|
|
104
|
+
key: string;
|
|
105
|
+
value: string;
|
|
106
|
+
};
|
|
107
|
+
meta?: boolean;
|
|
108
|
+
}): Promise<VSearchResult[]>;
|
|
52
109
|
vcard(): Promise<number>;
|
|
53
110
|
tsadd(key: string, timestamp: number | '*', value: number, options?: TSAddOptions): Promise<number>;
|
|
54
111
|
tsmadd(...entries: [string, number | '*', number][]): Promise<string>;
|
package/dist/index.js
CHANGED
|
@@ -5,10 +5,139 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.Lux = void 0;
|
|
7
7
|
const ioredis_1 = __importDefault(require("ioredis"));
|
|
8
|
+
class TableQueryBuilder {
|
|
9
|
+
constructor(client, name) {
|
|
10
|
+
this.conditions = [];
|
|
11
|
+
this.client = client;
|
|
12
|
+
this.name = name;
|
|
13
|
+
}
|
|
14
|
+
where(field, op, value) {
|
|
15
|
+
const v = typeof value === 'string' ? `'${value}'` : String(value);
|
|
16
|
+
this.conditions.push(`${field} ${op} ${v}`);
|
|
17
|
+
return this;
|
|
18
|
+
}
|
|
19
|
+
orderBy(field, dir = 'asc') {
|
|
20
|
+
this.orderField = field;
|
|
21
|
+
this.orderDir = dir.toUpperCase();
|
|
22
|
+
return this;
|
|
23
|
+
}
|
|
24
|
+
limit(n) {
|
|
25
|
+
this.limitCount = n;
|
|
26
|
+
return this;
|
|
27
|
+
}
|
|
28
|
+
join(table) {
|
|
29
|
+
this.joinTable = table;
|
|
30
|
+
return this;
|
|
31
|
+
}
|
|
32
|
+
async run() {
|
|
33
|
+
const args = [this.name];
|
|
34
|
+
if (this.conditions.length) {
|
|
35
|
+
args.push('WHERE', this.conditions.join(' AND '));
|
|
36
|
+
}
|
|
37
|
+
if (this.orderField) {
|
|
38
|
+
args.push('ORDER', 'BY', this.orderField, this.orderDir || 'ASC');
|
|
39
|
+
}
|
|
40
|
+
if (this.limitCount != null) {
|
|
41
|
+
args.push('LIMIT', String(this.limitCount));
|
|
42
|
+
}
|
|
43
|
+
if (this.joinTable) {
|
|
44
|
+
args.push('JOIN', this.joinTable);
|
|
45
|
+
}
|
|
46
|
+
return this.client._tquery(args);
|
|
47
|
+
}
|
|
48
|
+
async insert(data) {
|
|
49
|
+
const args = [this.name];
|
|
50
|
+
for (const [k, v] of Object.entries(data)) {
|
|
51
|
+
args.push(k, String(v));
|
|
52
|
+
}
|
|
53
|
+
const result = await this.client.call('TINSERT', ...args);
|
|
54
|
+
return parseInt(result, 10) || 0;
|
|
55
|
+
}
|
|
56
|
+
async update(id, data) {
|
|
57
|
+
const args = [this.name, id];
|
|
58
|
+
for (const [k, v] of Object.entries(data)) {
|
|
59
|
+
args.push(k, String(v));
|
|
60
|
+
}
|
|
61
|
+
return this.client.call('TUPDATE', ...args);
|
|
62
|
+
}
|
|
63
|
+
async delete(...ids) {
|
|
64
|
+
return this.client.call('TDEL', this.name, ...ids);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
class VectorNamespace {
|
|
68
|
+
constructor(client) {
|
|
69
|
+
this.client = client;
|
|
70
|
+
}
|
|
71
|
+
async set(key, vector, metadata) {
|
|
72
|
+
const args = [key, vector.length, ...vector];
|
|
73
|
+
if (metadata) {
|
|
74
|
+
args.push('META', JSON.stringify(metadata));
|
|
75
|
+
}
|
|
76
|
+
return this.client.call('VSET', ...args);
|
|
77
|
+
}
|
|
78
|
+
async get(key) {
|
|
79
|
+
return this.client.vget(key);
|
|
80
|
+
}
|
|
81
|
+
async search(query, options) {
|
|
82
|
+
return this.client.vsearch(query, { k: options.topK, filter: options.filter, meta: options.meta ?? true });
|
|
83
|
+
}
|
|
84
|
+
async count() {
|
|
85
|
+
return this.client.vcard();
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
class TimeSeriesNamespace {
|
|
89
|
+
constructor(client) {
|
|
90
|
+
this.client = client;
|
|
91
|
+
}
|
|
92
|
+
async add(key, value, options) {
|
|
93
|
+
return this.client.tsadd(key, options?.timestamp ?? '*', value, { retention: options?.retention, labels: options?.labels });
|
|
94
|
+
}
|
|
95
|
+
async get(key) {
|
|
96
|
+
return this.client.tsget(key);
|
|
97
|
+
}
|
|
98
|
+
async range(key, from, to, options) {
|
|
99
|
+
return this.client.tsrange(key, from, to, options);
|
|
100
|
+
}
|
|
101
|
+
async mrange(from, to, filter, options) {
|
|
102
|
+
return this.client.tsmrange(from, to, filter, options);
|
|
103
|
+
}
|
|
104
|
+
async info(key) {
|
|
105
|
+
return this.client.tsinfo(key);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
8
108
|
class Lux extends ioredis_1.default {
|
|
9
109
|
constructor(options) {
|
|
10
110
|
super(options);
|
|
111
|
+
this.vectors = new VectorNamespace(this);
|
|
112
|
+
this.timeseries = new TimeSeriesNamespace(this);
|
|
11
113
|
}
|
|
114
|
+
table(name) {
|
|
115
|
+
return new TableQueryBuilder(this, name);
|
|
116
|
+
}
|
|
117
|
+
async _tquery(args) {
|
|
118
|
+
const result = await this.call('TQUERY', ...args);
|
|
119
|
+
if (!result || !Array.isArray(result))
|
|
120
|
+
return [];
|
|
121
|
+
const rows = [];
|
|
122
|
+
for (const item of result) {
|
|
123
|
+
if (Array.isArray(item) && item.length >= 2) {
|
|
124
|
+
const row = { id: 0 };
|
|
125
|
+
for (let i = 0; i < item.length - 1; i += 2) {
|
|
126
|
+
const key = String(item[i]);
|
|
127
|
+
const val = item[i + 1];
|
|
128
|
+
if (key === 'id') {
|
|
129
|
+
row.id = parseInt(val, 10);
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
row[key] = val;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
rows.push(row);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return rows;
|
|
139
|
+
}
|
|
140
|
+
// Vector methods (keep for backward compat)
|
|
12
141
|
async vset(key, vector, options) {
|
|
13
142
|
const args = [key, vector.length, ...vector];
|
|
14
143
|
if (options?.metadata) {
|
|
@@ -23,7 +152,7 @@ class Lux extends ioredis_1.default {
|
|
|
23
152
|
return this.call('VSET', ...args);
|
|
24
153
|
}
|
|
25
154
|
async vget(key) {
|
|
26
|
-
const result =
|
|
155
|
+
const result = await this.call('VGET', key);
|
|
27
156
|
if (!result || !Array.isArray(result))
|
|
28
157
|
return null;
|
|
29
158
|
const dims = parseInt(result[0], 10);
|
|
@@ -49,16 +178,13 @@ class Lux extends ioredis_1.default {
|
|
|
49
178
|
if (options.meta) {
|
|
50
179
|
args.push('META');
|
|
51
180
|
}
|
|
52
|
-
const result =
|
|
181
|
+
const result = await this.call('VSEARCH', ...args);
|
|
53
182
|
if (!result || !Array.isArray(result))
|
|
54
183
|
return [];
|
|
55
184
|
const results = [];
|
|
56
185
|
for (const item of result) {
|
|
57
186
|
if (Array.isArray(item)) {
|
|
58
|
-
const entry = {
|
|
59
|
-
key: item[0],
|
|
60
|
-
similarity: parseFloat(item[1]),
|
|
61
|
-
};
|
|
187
|
+
const entry = { key: item[0], similarity: parseFloat(item[1]) };
|
|
62
188
|
if (options.meta && item[2]) {
|
|
63
189
|
try {
|
|
64
190
|
entry.metadata = JSON.parse(item[2]);
|
|
@@ -75,6 +201,7 @@ class Lux extends ioredis_1.default {
|
|
|
75
201
|
async vcard() {
|
|
76
202
|
return this.call('VCARD');
|
|
77
203
|
}
|
|
204
|
+
// Time series methods (keep for backward compat)
|
|
78
205
|
async tsadd(key, timestamp, value, options) {
|
|
79
206
|
const args = [key, timestamp === '*' ? '*' : timestamp, value];
|
|
80
207
|
if (options?.retention != null) {
|
|
@@ -96,7 +223,7 @@ class Lux extends ioredis_1.default {
|
|
|
96
223
|
return this.call('TSMADD', ...args);
|
|
97
224
|
}
|
|
98
225
|
async tsget(key) {
|
|
99
|
-
const result =
|
|
226
|
+
const result = await this.call('TSGET', key);
|
|
100
227
|
if (!result || !Array.isArray(result) || result.length < 2)
|
|
101
228
|
return null;
|
|
102
229
|
return { timestamp: parseInt(result[0], 10), value: parseFloat(result[1]) };
|
|
@@ -106,13 +233,10 @@ class Lux extends ioredis_1.default {
|
|
|
106
233
|
if (options?.aggregation) {
|
|
107
234
|
args.push('AGGREGATION', options.aggregation.type, options.aggregation.bucketSize);
|
|
108
235
|
}
|
|
109
|
-
const result =
|
|
236
|
+
const result = await this.call('TSRANGE', ...args);
|
|
110
237
|
if (!result || !Array.isArray(result))
|
|
111
238
|
return [];
|
|
112
|
-
return result.map((pair) => ({
|
|
113
|
-
timestamp: parseInt(pair[0], 10),
|
|
114
|
-
value: parseFloat(pair[1]),
|
|
115
|
-
}));
|
|
239
|
+
return result.map((pair) => ({ timestamp: parseInt(pair[0], 10), value: parseFloat(pair[1]) }));
|
|
116
240
|
}
|
|
117
241
|
async tsmrange(from, to, filter, options) {
|
|
118
242
|
const args = [from === '-' ? '-' : from, to === '+' ? '+' : to];
|
|
@@ -120,16 +244,15 @@ class Lux extends ioredis_1.default {
|
|
|
120
244
|
args.push('AGGREGATION', options.aggregation.type, options.aggregation.bucketSize);
|
|
121
245
|
}
|
|
122
246
|
args.push('FILTER', filter);
|
|
123
|
-
const result =
|
|
247
|
+
const result = await this.call('TSMRANGE', ...args);
|
|
124
248
|
if (!result || !Array.isArray(result))
|
|
125
249
|
return [];
|
|
126
250
|
return result.map((series) => {
|
|
127
251
|
const labels = {};
|
|
128
252
|
if (Array.isArray(series[1])) {
|
|
129
253
|
for (const pair of series[1]) {
|
|
130
|
-
if (Array.isArray(pair) && pair.length >= 2)
|
|
254
|
+
if (Array.isArray(pair) && pair.length >= 2)
|
|
131
255
|
labels[pair[0]] = pair[1];
|
|
132
|
-
}
|
|
133
256
|
}
|
|
134
257
|
}
|
|
135
258
|
const samples = Array.isArray(series[2])
|
|
@@ -139,7 +262,7 @@ class Lux extends ioredis_1.default {
|
|
|
139
262
|
});
|
|
140
263
|
}
|
|
141
264
|
async tsinfo(key) {
|
|
142
|
-
const result =
|
|
265
|
+
const result = await this.call('TSINFO', key);
|
|
143
266
|
if (!result || !Array.isArray(result))
|
|
144
267
|
return {};
|
|
145
268
|
const info = {};
|
|
@@ -160,10 +283,10 @@ class Lux extends ioredis_1.default {
|
|
|
160
283
|
}
|
|
161
284
|
return info;
|
|
162
285
|
}
|
|
286
|
+
// Realtime key subscriptions
|
|
163
287
|
ksub(patterns, handler) {
|
|
164
288
|
const sub = this.duplicate();
|
|
165
289
|
sub.on('error', () => { });
|
|
166
|
-
const origReturnReply = sub.returnReply?.bind(sub);
|
|
167
290
|
const dataHandler = sub._dataHandler || sub.dataHandler;
|
|
168
291
|
if (dataHandler && dataHandler.returnReply) {
|
|
169
292
|
const origReturn = dataHandler.returnReply.bind(dataHandler);
|
|
@@ -191,9 +314,7 @@ class Lux extends ioredis_1.default {
|
|
|
191
314
|
sub.call('KSUB', ...patterns);
|
|
192
315
|
return {
|
|
193
316
|
connection: sub,
|
|
194
|
-
unsubscribe() {
|
|
195
|
-
sub.disconnect();
|
|
196
|
-
},
|
|
317
|
+
unsubscribe() { sub.disconnect(); },
|
|
197
318
|
};
|
|
198
319
|
}
|
|
199
320
|
}
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@luxdb/sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Lux SDK - ioredis extended with vector search, time series, and realtime key subscriptions",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"files": ["dist"],
|
|
8
8
|
"scripts": {
|
|
9
9
|
"build": "tsc",
|
|
10
|
-
"prepublishOnly": "
|
|
10
|
+
"prepublishOnly": "bun run build"
|
|
11
11
|
},
|
|
12
12
|
"dependencies": {
|
|
13
13
|
"ioredis": "^5.0.0"
|