@hotmeshio/hotmesh 0.5.0 → 0.5.2
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 +234 -237
- package/build/modules/errors.d.ts +9 -0
- package/build/modules/errors.js +9 -0
- package/build/package.json +16 -14
- package/build/services/hotmesh/index.d.ts +9 -11
- package/build/services/hotmesh/index.js +9 -11
- package/build/services/memflow/entity.d.ts +168 -4
- package/build/services/memflow/entity.js +177 -15
- package/build/services/memflow/index.d.ts +8 -0
- package/build/services/memflow/index.js +8 -0
- package/build/services/memflow/worker.js +25 -0
- package/build/services/memflow/workflow/execChild.js +1 -0
- package/build/services/memflow/workflow/execHook.d.ts +2 -2
- package/build/services/memflow/workflow/execHook.js +19 -9
- package/build/services/memflow/workflow/index.d.ts +2 -4
- package/build/services/memflow/workflow/index.js +2 -4
- package/build/services/memflow/workflow/interruption.d.ts +28 -0
- package/build/services/memflow/workflow/interruption.js +43 -0
- package/build/services/memflow/workflow/proxyActivities.js +1 -0
- package/build/services/memflow/workflow/sleepFor.js +1 -0
- package/build/services/memflow/workflow/waitFor.js +4 -4
- package/build/services/search/index.d.ts +10 -0
- package/build/services/search/providers/postgres/postgres.d.ts +12 -0
- package/build/services/search/providers/postgres/postgres.js +209 -0
- package/build/services/search/providers/redis/ioredis.d.ts +4 -0
- package/build/services/search/providers/redis/ioredis.js +13 -0
- package/build/services/search/providers/redis/redis.d.ts +4 -0
- package/build/services/search/providers/redis/redis.js +13 -0
- package/build/services/store/providers/postgres/kvsql.d.ts +13 -37
- package/build/services/store/providers/postgres/kvsql.js +2 -2
- package/build/services/store/providers/postgres/kvtypes/hash/basic.d.ts +16 -0
- package/build/services/store/providers/postgres/kvtypes/hash/basic.js +480 -0
- package/build/services/store/providers/postgres/kvtypes/hash/expire.d.ts +5 -0
- package/build/services/store/providers/postgres/kvtypes/hash/expire.js +33 -0
- package/build/services/store/providers/postgres/kvtypes/hash/index.d.ts +29 -0
- package/build/services/store/providers/postgres/kvtypes/hash/index.js +190 -0
- package/build/services/store/providers/postgres/kvtypes/hash/jsonb.d.ts +14 -0
- package/build/services/store/providers/postgres/kvtypes/hash/jsonb.js +699 -0
- package/build/services/store/providers/postgres/kvtypes/hash/scan.d.ts +10 -0
- package/build/services/store/providers/postgres/kvtypes/hash/scan.js +91 -0
- package/build/services/store/providers/postgres/kvtypes/hash/types.d.ts +19 -0
- package/build/services/store/providers/postgres/kvtypes/hash/types.js +2 -0
- package/build/services/store/providers/postgres/kvtypes/hash/utils.d.ts +18 -0
- package/build/services/store/providers/postgres/kvtypes/hash/utils.js +90 -0
- package/build/types/memflow.d.ts +1 -1
- package/build/types/meshdata.d.ts +1 -1
- package/package.json +16 -14
- package/build/services/store/providers/postgres/kvtypes/hash.d.ts +0 -60
- package/build/services/store/providers/postgres/kvtypes/hash.js +0 -1287
|
@@ -2,7 +2,7 @@ import { KeyStoreParams } from '../../../../types/hotmesh';
|
|
|
2
2
|
import { PostgresClientType } from '../../../../types/postgres';
|
|
3
3
|
import { ProviderTransaction } from '../../../../types/provider';
|
|
4
4
|
import { stringModule } from './kvtypes/string';
|
|
5
|
-
import { hashModule } from './kvtypes/hash';
|
|
5
|
+
import { hashModule } from './kvtypes/hash/index';
|
|
6
6
|
import { listModule } from './kvtypes/list';
|
|
7
7
|
import { zsetModule } from './kvtypes/zset';
|
|
8
8
|
/**
|
|
@@ -30,8 +30,8 @@ export declare class KVSQL {
|
|
|
30
30
|
*/
|
|
31
31
|
tableForKey(key: string, stats_type?: 'hash' | 'sorted_set' | 'list'): string;
|
|
32
32
|
safeName(input: string, prefix?: string): string;
|
|
33
|
-
set: (key: string, value: string, options?: import("
|
|
34
|
-
_set: (key: string, value: string, options?: import("
|
|
33
|
+
set: (key: string, value: string, options?: import("./kvtypes/hash/index").SetOptions, multi?: ProviderTransaction) => Promise<boolean>;
|
|
34
|
+
_set: (key: string, value: string, options?: import("./kvtypes/hash/index").SetOptions) => {
|
|
35
35
|
sql: string;
|
|
36
36
|
params: any[];
|
|
37
37
|
};
|
|
@@ -47,51 +47,27 @@ export declare class KVSQL {
|
|
|
47
47
|
};
|
|
48
48
|
setnx: (key: string, value: string, multi?: ProviderTransaction) => Promise<boolean>;
|
|
49
49
|
setnxex: (key: string, value: string, delay: number, multi?: ProviderTransaction) => Promise<boolean>;
|
|
50
|
-
hset: (key: string, fields: Record<string, string>, options?: import("
|
|
51
|
-
_hset: (key: string, fields: Record<string, string>, options?: import("
|
|
52
|
-
sql: string;
|
|
53
|
-
params: any[];
|
|
54
|
-
};
|
|
50
|
+
hset: (key: string, fields: Record<string, string>, options?: import("./kvtypes/hash/index").HSetOptions, multi?: any) => Promise<any>;
|
|
51
|
+
_hset: (key: string, fields: Record<string, string>, options?: import("./kvtypes/hash/index").HSetOptions) => import("./kvtypes/hash/types").SqlResult;
|
|
55
52
|
hsetnx: (key: string, field: string, value: string, multi?: ProviderTransaction, entity?: string) => Promise<number>;
|
|
56
53
|
hget: (key: string, field: string, multi?: ProviderTransaction) => Promise<string>;
|
|
57
|
-
_hget: (key: string, field: string) =>
|
|
58
|
-
sql: string;
|
|
59
|
-
params: any[];
|
|
60
|
-
};
|
|
54
|
+
_hget: (key: string, field: string) => import("./kvtypes/hash/types").SqlResult;
|
|
61
55
|
hdel: (key: string, fields: string[], multi?: unknown) => Promise<number>;
|
|
62
|
-
_hdel: (key: string, fields: string[]) =>
|
|
63
|
-
sql: string;
|
|
64
|
-
params: any[];
|
|
65
|
-
};
|
|
56
|
+
_hdel: (key: string, fields: string[]) => import("./kvtypes/hash/types").SqlResult;
|
|
66
57
|
hmget: (key: string, fields: string[], multi?: ProviderTransaction) => Promise<string[]>;
|
|
67
|
-
_hmget: (key: string, fields: string[]) =>
|
|
68
|
-
sql: string;
|
|
69
|
-
params: any[];
|
|
70
|
-
};
|
|
58
|
+
_hmget: (key: string, fields: string[]) => import("./kvtypes/hash/types").SqlResult;
|
|
71
59
|
hgetall: (key: string, multi?: ProviderTransaction) => Promise<Record<string, string>>;
|
|
72
60
|
hincrbyfloat: (key: string, field: string, increment: number, multi?: ProviderTransaction) => Promise<number>;
|
|
73
|
-
_hincrbyfloat: (key: string, field: string, increment: number) =>
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
};
|
|
77
|
-
hscan: (key: string, cursor: string, count?: number, pattern?: string, multi?: ProviderTransaction) => Promise<import("../../../../types/provider").HScanResult>;
|
|
78
|
-
_hscan: (key: string, cursor: string, count: number, pattern?: string) => {
|
|
79
|
-
sql: string;
|
|
80
|
-
params: any[];
|
|
81
|
-
};
|
|
61
|
+
_hincrbyfloat: (key: string, field: string, increment: number) => import("./kvtypes/hash/types").SqlResult;
|
|
62
|
+
hscan: (key: string, cursor: string, count?: number, pattern?: string, multi?: ProviderTransaction) => Promise<import("./kvtypes/hash/index").HScanResult>;
|
|
63
|
+
_hscan: (key: string, cursor: string, count: number, pattern?: string) => import("./kvtypes/hash/types").SqlResult;
|
|
82
64
|
expire: (key: string, seconds: number, multi?: ProviderTransaction) => Promise<boolean>;
|
|
83
|
-
_expire: (key: string, seconds: number) =>
|
|
84
|
-
sql: string;
|
|
85
|
-
params: any[];
|
|
86
|
-
};
|
|
65
|
+
_expire: (key: string, seconds: number) => import("./kvtypes/hash/types").SqlResult;
|
|
87
66
|
scan: (cursor: number, count?: number, pattern?: string, multi?: ProviderTransaction) => Promise<{
|
|
88
67
|
cursor: number;
|
|
89
68
|
keys: string[];
|
|
90
69
|
}>;
|
|
91
|
-
_scan: (cursor: number, count: number, pattern?: string) =>
|
|
92
|
-
sql: string;
|
|
93
|
-
params: any[];
|
|
94
|
-
};
|
|
70
|
+
_scan: (cursor: number, count: number, pattern?: string) => import("./kvtypes/hash/types").SqlResult;
|
|
95
71
|
lrange: (key: string, start: number, end: number, multi?: ProviderTransaction) => Promise<string[]>;
|
|
96
72
|
_lrange: (key: string, start: number, end: number) => {
|
|
97
73
|
sql: string;
|
|
@@ -4,7 +4,7 @@ exports.KVSQL = void 0;
|
|
|
4
4
|
const key_1 = require("../../../../modules/key");
|
|
5
5
|
const kvtransaction_1 = require("./kvtransaction");
|
|
6
6
|
const string_1 = require("./kvtypes/string");
|
|
7
|
-
const
|
|
7
|
+
const index_1 = require("./kvtypes/hash/index");
|
|
8
8
|
const list_1 = require("./kvtypes/list");
|
|
9
9
|
const zset_1 = require("./kvtypes/zset");
|
|
10
10
|
/**
|
|
@@ -72,7 +72,7 @@ class KVSQL {
|
|
|
72
72
|
this.pgClient = pgClient;
|
|
73
73
|
this.namespace = namespace;
|
|
74
74
|
this.appId = appId;
|
|
75
|
-
this.hash = (0,
|
|
75
|
+
this.hash = (0, index_1.hashModule)(this);
|
|
76
76
|
this.list = (0, list_1.listModule)(this);
|
|
77
77
|
this.zset = (0, zset_1.zsetModule)(this);
|
|
78
78
|
this.string = (0, string_1.stringModule)(this);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { HashContext, SqlResult, HSetOptions, ProviderTransaction } from './types';
|
|
2
|
+
export declare function createBasicOperations(context: HashContext['context']): {
|
|
3
|
+
hsetnx(key: string, field: string, value: string, multi?: ProviderTransaction, entity?: string): Promise<number>;
|
|
4
|
+
hset(key: string, fields: Record<string, string>, options?: HSetOptions, multi?: ProviderTransaction): Promise<number | any>;
|
|
5
|
+
hget(key: string, field: string, multi?: ProviderTransaction): Promise<string | null>;
|
|
6
|
+
hdel(key: string, fields: string[], multi?: unknown): Promise<number>;
|
|
7
|
+
hmget(key: string, fields: string[], multi?: ProviderTransaction): Promise<(string | null)[]>;
|
|
8
|
+
hgetall(key: string, multi?: ProviderTransaction): Promise<Record<string, string>>;
|
|
9
|
+
hincrbyfloat(key: string, field: string, increment: number, multi?: ProviderTransaction): Promise<number>;
|
|
10
|
+
};
|
|
11
|
+
export declare function _hset(context: HashContext['context'], key: string, fields: Record<string, string>, options?: HSetOptions): SqlResult;
|
|
12
|
+
export declare function _hget(context: HashContext['context'], key: string, field: string): SqlResult;
|
|
13
|
+
export declare function _hdel(context: HashContext['context'], key: string, fields: string[]): SqlResult;
|
|
14
|
+
export declare function _hmget(context: HashContext['context'], key: string, fields: string[]): SqlResult;
|
|
15
|
+
export declare function _hgetall(context: HashContext['context'], key: string): SqlResult;
|
|
16
|
+
export declare function _hincrbyfloat(context: HashContext['context'], key: string, field: string, increment: number): SqlResult;
|
|
@@ -0,0 +1,480 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports._hincrbyfloat = exports._hgetall = exports._hmget = exports._hdel = exports._hget = exports._hset = exports.createBasicOperations = void 0;
|
|
4
|
+
const utils_1 = require("./utils");
|
|
5
|
+
function createBasicOperations(context) {
|
|
6
|
+
return {
|
|
7
|
+
async hsetnx(key, field, value, multi, entity) {
|
|
8
|
+
const { sql, params } = _hset(context, key, { [field]: value }, { nx: true, entity });
|
|
9
|
+
if (multi) {
|
|
10
|
+
multi.addCommand(sql, params, 'number');
|
|
11
|
+
return Promise.resolve(0);
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
try {
|
|
15
|
+
const res = await context.pgClient.query(sql, params);
|
|
16
|
+
return res.rowCount;
|
|
17
|
+
}
|
|
18
|
+
catch (err) {
|
|
19
|
+
console.error('hsetnx error', err, sql, params);
|
|
20
|
+
return 0;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
async hset(key, fields, options, multi) {
|
|
25
|
+
const { sql, params } = _hset(context, key, fields, options);
|
|
26
|
+
if (multi) {
|
|
27
|
+
multi.addCommand(sql, params, 'number');
|
|
28
|
+
return Promise.resolve(0);
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
try {
|
|
32
|
+
const res = await context.pgClient.query(sql, params);
|
|
33
|
+
// Check if this is a JSONB operation that returns a value
|
|
34
|
+
const isJsonbOperation = Object.keys(fields).some(k => k.startsWith('@context:') && k !== '@context');
|
|
35
|
+
if (isJsonbOperation && res.rows[0]?.new_value !== undefined) {
|
|
36
|
+
let returnValue;
|
|
37
|
+
try {
|
|
38
|
+
// Try to parse as JSON, fallback to string if it fails
|
|
39
|
+
returnValue = JSON.parse(res.rows[0].new_value);
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
returnValue = res.rows[0].new_value;
|
|
43
|
+
}
|
|
44
|
+
return returnValue;
|
|
45
|
+
}
|
|
46
|
+
return res.rowCount;
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
console.error('hset error', err, sql, params);
|
|
50
|
+
return 0;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
async hget(key, field, multi) {
|
|
55
|
+
const { sql, params } = _hget(context, key, field);
|
|
56
|
+
if (multi) {
|
|
57
|
+
multi.addCommand(sql, params, 'string', (rows) => {
|
|
58
|
+
return rows[0]?.value || null;
|
|
59
|
+
});
|
|
60
|
+
return Promise.resolve(null);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
const res = await context.pgClient.query(sql, params);
|
|
64
|
+
return res.rows[0]?.value || null;
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
async hdel(key, fields, multi) {
|
|
68
|
+
// Ensure fields is an array
|
|
69
|
+
if (!Array.isArray(fields)) {
|
|
70
|
+
fields = [fields];
|
|
71
|
+
}
|
|
72
|
+
const { sql, params } = _hdel(context, key, fields);
|
|
73
|
+
if (multi) {
|
|
74
|
+
multi.addCommand(sql, params, 'number');
|
|
75
|
+
return Promise.resolve(0);
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
const res = await context.pgClient.query(sql, params);
|
|
79
|
+
return Number(res.rows[0]?.count || 0);
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
async hmget(key, fields, multi) {
|
|
83
|
+
const { sql, params } = _hmget(context, key, fields);
|
|
84
|
+
const processRows = (rows) => {
|
|
85
|
+
const tableName = context.tableForKey(key, 'hash');
|
|
86
|
+
const isJobsTableResult = (0, utils_1.isJobsTable)(tableName);
|
|
87
|
+
if (isJobsTableResult) {
|
|
88
|
+
return (0, utils_1.processJobsRows)(rows, fields);
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
const fieldValueMap = new Map();
|
|
92
|
+
for (const row of rows) {
|
|
93
|
+
fieldValueMap.set(row.field, row.value);
|
|
94
|
+
}
|
|
95
|
+
return fields.map((field) => fieldValueMap.get(field) || null);
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
if (multi) {
|
|
99
|
+
multi.addCommand(sql, params, 'array', (rows) => {
|
|
100
|
+
return processRows(rows);
|
|
101
|
+
});
|
|
102
|
+
return Promise.resolve([]);
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
try {
|
|
106
|
+
const res = await context.pgClient.query(sql, params);
|
|
107
|
+
return processRows(res.rows);
|
|
108
|
+
}
|
|
109
|
+
catch (err) {
|
|
110
|
+
console.error('hmget error', err, sql, params);
|
|
111
|
+
throw err;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
async hgetall(key, multi) {
|
|
116
|
+
const tableName = context.tableForKey(key, 'hash');
|
|
117
|
+
const isJobsTableResult = (0, utils_1.isJobsTable)(tableName);
|
|
118
|
+
const { sql, params } = _hgetall(context, key);
|
|
119
|
+
const processRows = (rows) => {
|
|
120
|
+
if (isJobsTableResult) {
|
|
121
|
+
return (0, utils_1.processJobsRows)(rows);
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
return (0, utils_1.processRegularRows)(rows);
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
if (multi) {
|
|
128
|
+
multi.addCommand(sql, params, 'object', (rows) => {
|
|
129
|
+
return processRows(rows);
|
|
130
|
+
});
|
|
131
|
+
return Promise.resolve({});
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
try {
|
|
135
|
+
const res = await context.pgClient.query(sql, params);
|
|
136
|
+
return processRows(res.rows);
|
|
137
|
+
}
|
|
138
|
+
catch (err) {
|
|
139
|
+
console.error('hgetall error', err, sql, params);
|
|
140
|
+
throw err;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
async hincrbyfloat(key, field, increment, multi) {
|
|
145
|
+
const { sql, params } = _hincrbyfloat(context, key, field, increment);
|
|
146
|
+
if (multi) {
|
|
147
|
+
multi.addCommand(sql, params, 'number', (rows) => {
|
|
148
|
+
return parseFloat(rows[0].value);
|
|
149
|
+
});
|
|
150
|
+
return Promise.resolve(0);
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
const res = await context.pgClient.query(sql, params);
|
|
154
|
+
return parseFloat(res.rows[0].value);
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
exports.createBasicOperations = createBasicOperations;
|
|
160
|
+
// Internal helper functions
|
|
161
|
+
function _hset(context, key, fields, options) {
|
|
162
|
+
const tableName = context.tableForKey(key, 'hash');
|
|
163
|
+
const isJobsTableResult = (0, utils_1.isJobsTable)(tableName);
|
|
164
|
+
const fieldEntries = Object.entries(fields);
|
|
165
|
+
const isStatusOnly = fieldEntries.length === 1 && fieldEntries[0][0] === ':';
|
|
166
|
+
let targetTable = tableName; // Default table name
|
|
167
|
+
if (isJobsTableResult) {
|
|
168
|
+
if (isStatusOnly) {
|
|
169
|
+
// Target the jobs table directly when setting only the status field
|
|
170
|
+
targetTable = tableName;
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
// For other fields, target the attributes table
|
|
174
|
+
targetTable = `${tableName}_attributes`;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
const params = [];
|
|
178
|
+
let sql = '';
|
|
179
|
+
if (isJobsTableResult && isStatusOnly) {
|
|
180
|
+
if (options?.nx) {
|
|
181
|
+
// Use WHERE NOT EXISTS to enforce nx
|
|
182
|
+
sql = `
|
|
183
|
+
INSERT INTO ${targetTable} (id, key, status, entity)
|
|
184
|
+
SELECT gen_random_uuid(), $1, $2, $3
|
|
185
|
+
WHERE NOT EXISTS (
|
|
186
|
+
SELECT 1 FROM ${targetTable}
|
|
187
|
+
WHERE key = $1 AND is_live
|
|
188
|
+
)
|
|
189
|
+
RETURNING 1 as count
|
|
190
|
+
`;
|
|
191
|
+
params.push(key, fields[':'], options?.entity ?? null);
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
// Update existing job or insert new one
|
|
195
|
+
sql = `
|
|
196
|
+
INSERT INTO ${targetTable} (id, key, status, entity)
|
|
197
|
+
VALUES (gen_random_uuid(), $1, $2, $3)
|
|
198
|
+
ON CONFLICT (key) WHERE is_live DO UPDATE SET status = EXCLUDED.status
|
|
199
|
+
RETURNING 1 as count
|
|
200
|
+
`;
|
|
201
|
+
params.push(key, fields[':'], options?.entity ?? null);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
else if (isJobsTableResult) {
|
|
205
|
+
const schemaName = context.safeName(context.appId);
|
|
206
|
+
const conflictAction = options?.nx
|
|
207
|
+
? 'ON CONFLICT DO NOTHING'
|
|
208
|
+
: `ON CONFLICT (job_id, field) DO UPDATE SET value = EXCLUDED.value`;
|
|
209
|
+
const placeholders = fieldEntries
|
|
210
|
+
.map(([field, value], index) => {
|
|
211
|
+
const baseIndex = index * 3 + 2; // Adjusted baseIndex
|
|
212
|
+
params.push(field, value, (0, utils_1.deriveType)(field));
|
|
213
|
+
return `($${baseIndex}, $${baseIndex + 1}, $${baseIndex + 2}::${schemaName}.type_enum)`;
|
|
214
|
+
})
|
|
215
|
+
.join(', ');
|
|
216
|
+
sql = `
|
|
217
|
+
INSERT INTO ${targetTable} (job_id, field, value, type)
|
|
218
|
+
SELECT
|
|
219
|
+
job.id,
|
|
220
|
+
vals.field,
|
|
221
|
+
vals.value,
|
|
222
|
+
vals.type
|
|
223
|
+
FROM (
|
|
224
|
+
SELECT id FROM ${tableName} WHERE key = $1 AND is_live
|
|
225
|
+
) AS job
|
|
226
|
+
CROSS JOIN (
|
|
227
|
+
VALUES ${placeholders}
|
|
228
|
+
) AS vals(field, value, type)
|
|
229
|
+
${conflictAction}
|
|
230
|
+
RETURNING 1 as count
|
|
231
|
+
`;
|
|
232
|
+
params.unshift(key); // Add key as first parameter
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
// For non-jobs tables
|
|
236
|
+
const conflictAction = options?.nx
|
|
237
|
+
? 'ON CONFLICT DO NOTHING'
|
|
238
|
+
: `ON CONFLICT (key, field) DO UPDATE SET value = EXCLUDED.value`;
|
|
239
|
+
const placeholders = fieldEntries
|
|
240
|
+
.map(([field, value], index) => {
|
|
241
|
+
params.push(field, value);
|
|
242
|
+
return `($1, $${index * 2 + 2}, $${index * 2 + 3})`;
|
|
243
|
+
})
|
|
244
|
+
.join(', ');
|
|
245
|
+
sql = `
|
|
246
|
+
INSERT INTO ${targetTable} (key, field, value)
|
|
247
|
+
VALUES ${placeholders}
|
|
248
|
+
${conflictAction}
|
|
249
|
+
RETURNING 1 as count
|
|
250
|
+
`;
|
|
251
|
+
params.unshift(key); // Add key as the first parameter
|
|
252
|
+
}
|
|
253
|
+
return { sql, params };
|
|
254
|
+
}
|
|
255
|
+
exports._hset = _hset;
|
|
256
|
+
function _hget(context, key, field) {
|
|
257
|
+
const tableName = context.tableForKey(key, 'hash');
|
|
258
|
+
const isJobsTableResult = (0, utils_1.isJobsTable)(tableName);
|
|
259
|
+
const isStatusField = field === ':';
|
|
260
|
+
const isContextField = field === '@';
|
|
261
|
+
if (isJobsTableResult && isStatusField) {
|
|
262
|
+
// Fetch status from jobs table
|
|
263
|
+
const sql = `
|
|
264
|
+
SELECT status::text AS value
|
|
265
|
+
FROM ${tableName}
|
|
266
|
+
WHERE key = $1 AND is_live
|
|
267
|
+
`;
|
|
268
|
+
return { sql, params: [key] };
|
|
269
|
+
}
|
|
270
|
+
else if (isJobsTableResult && isContextField) {
|
|
271
|
+
// Fetch context from jobs table
|
|
272
|
+
const sql = `
|
|
273
|
+
SELECT context::text AS value
|
|
274
|
+
FROM ${tableName}
|
|
275
|
+
WHERE key = $1 AND is_live
|
|
276
|
+
`;
|
|
277
|
+
return { sql, params: [key] };
|
|
278
|
+
}
|
|
279
|
+
else if (isJobsTableResult) {
|
|
280
|
+
// Fetch a specific field from the attributes table for a job
|
|
281
|
+
const sql = `
|
|
282
|
+
SELECT value
|
|
283
|
+
FROM ${tableName}_attributes
|
|
284
|
+
WHERE job_id = (
|
|
285
|
+
SELECT id FROM ${tableName}
|
|
286
|
+
WHERE key = $1 AND is_live
|
|
287
|
+
)
|
|
288
|
+
AND field = $2
|
|
289
|
+
`;
|
|
290
|
+
return { sql, params: [key, field] };
|
|
291
|
+
}
|
|
292
|
+
else {
|
|
293
|
+
// Non-jobs tables
|
|
294
|
+
const baseQuery = `
|
|
295
|
+
SELECT value
|
|
296
|
+
FROM ${tableName}
|
|
297
|
+
WHERE key = $1 AND field = $2
|
|
298
|
+
`;
|
|
299
|
+
const sql = context.appendExpiryClause(baseQuery, tableName);
|
|
300
|
+
return { sql, params: [key, field] };
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
exports._hget = _hget;
|
|
304
|
+
function _hdel(context, key, fields) {
|
|
305
|
+
const tableName = context.tableForKey(key, 'hash');
|
|
306
|
+
const isJobsTableResult = (0, utils_1.isJobsTable)(tableName);
|
|
307
|
+
const targetTable = isJobsTableResult ? `${tableName}_attributes` : tableName;
|
|
308
|
+
const fieldPlaceholders = fields.map((_, i) => `$${i + 2}`).join(', ');
|
|
309
|
+
const params = [key, ...fields];
|
|
310
|
+
if (isJobsTableResult) {
|
|
311
|
+
const sql = `
|
|
312
|
+
WITH valid_job AS (
|
|
313
|
+
SELECT id
|
|
314
|
+
FROM ${tableName}
|
|
315
|
+
WHERE key = $1 AND is_live
|
|
316
|
+
),
|
|
317
|
+
deleted AS (
|
|
318
|
+
DELETE FROM ${targetTable}
|
|
319
|
+
WHERE job_id IN (SELECT id FROM valid_job) AND field IN (${fieldPlaceholders})
|
|
320
|
+
RETURNING 1
|
|
321
|
+
)
|
|
322
|
+
SELECT COUNT(*) as count FROM deleted
|
|
323
|
+
`;
|
|
324
|
+
return { sql, params };
|
|
325
|
+
}
|
|
326
|
+
else {
|
|
327
|
+
const sql = `
|
|
328
|
+
WITH deleted AS (
|
|
329
|
+
DELETE FROM ${targetTable}
|
|
330
|
+
WHERE key = $1 AND field IN (${fieldPlaceholders})
|
|
331
|
+
RETURNING 1
|
|
332
|
+
)
|
|
333
|
+
SELECT COUNT(*) as count FROM deleted
|
|
334
|
+
`;
|
|
335
|
+
return { sql, params };
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
exports._hdel = _hdel;
|
|
339
|
+
function _hmget(context, key, fields) {
|
|
340
|
+
const tableName = context.tableForKey(key, 'hash');
|
|
341
|
+
const isJobsTableResult = (0, utils_1.isJobsTable)(tableName);
|
|
342
|
+
if (isJobsTableResult) {
|
|
343
|
+
const sql = `
|
|
344
|
+
WITH valid_job AS (
|
|
345
|
+
SELECT id, status, context
|
|
346
|
+
FROM ${tableName}
|
|
347
|
+
WHERE key = $1
|
|
348
|
+
AND (expired_at IS NULL OR expired_at > NOW())
|
|
349
|
+
LIMIT 1
|
|
350
|
+
),
|
|
351
|
+
job_fields AS (
|
|
352
|
+
-- Include both status and context fields from jobs table
|
|
353
|
+
SELECT
|
|
354
|
+
'status' AS field,
|
|
355
|
+
status::text AS value
|
|
356
|
+
FROM valid_job
|
|
357
|
+
|
|
358
|
+
UNION ALL
|
|
359
|
+
|
|
360
|
+
SELECT
|
|
361
|
+
'context' AS field,
|
|
362
|
+
context::text AS value
|
|
363
|
+
FROM valid_job
|
|
364
|
+
|
|
365
|
+
UNION ALL
|
|
366
|
+
|
|
367
|
+
-- Get attribute fields with proper type handling
|
|
368
|
+
SELECT
|
|
369
|
+
a.field,
|
|
370
|
+
a.value
|
|
371
|
+
FROM ${tableName}_attributes a
|
|
372
|
+
JOIN valid_job j ON j.id = a.job_id
|
|
373
|
+
WHERE a.field = ANY($2::text[])
|
|
374
|
+
)
|
|
375
|
+
SELECT field, value
|
|
376
|
+
FROM job_fields
|
|
377
|
+
ORDER BY field
|
|
378
|
+
`;
|
|
379
|
+
return { sql, params: [key, fields] };
|
|
380
|
+
}
|
|
381
|
+
else {
|
|
382
|
+
// Non-job tables logic remains the same
|
|
383
|
+
const baseQuery = `
|
|
384
|
+
SELECT field, value
|
|
385
|
+
FROM ${tableName}
|
|
386
|
+
WHERE key = $1
|
|
387
|
+
AND field = ANY($2::text[])
|
|
388
|
+
`;
|
|
389
|
+
const sql = context.appendExpiryClause(baseQuery, tableName);
|
|
390
|
+
return { sql, params: [key, fields] };
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
exports._hmget = _hmget;
|
|
394
|
+
function _hgetall(context, key) {
|
|
395
|
+
const tableName = context.tableForKey(key, 'hash');
|
|
396
|
+
const isJobsTableResult = (0, utils_1.isJobsTable)(tableName);
|
|
397
|
+
if (isJobsTableResult) {
|
|
398
|
+
const sql = `
|
|
399
|
+
WITH valid_job AS (
|
|
400
|
+
SELECT id, status, context
|
|
401
|
+
FROM ${tableName}
|
|
402
|
+
WHERE key = $1 AND is_live
|
|
403
|
+
),
|
|
404
|
+
job_data AS (
|
|
405
|
+
SELECT 'status' AS field, status::text AS value
|
|
406
|
+
FROM ${tableName}
|
|
407
|
+
WHERE key = $1 AND is_live
|
|
408
|
+
|
|
409
|
+
UNION ALL
|
|
410
|
+
|
|
411
|
+
SELECT 'context' AS field, context::text AS value
|
|
412
|
+
FROM ${tableName}
|
|
413
|
+
WHERE key = $1 AND is_live
|
|
414
|
+
),
|
|
415
|
+
attribute_data AS (
|
|
416
|
+
SELECT field, value
|
|
417
|
+
FROM ${tableName}_attributes
|
|
418
|
+
WHERE job_id IN (SELECT id FROM valid_job)
|
|
419
|
+
)
|
|
420
|
+
SELECT * FROM job_data
|
|
421
|
+
UNION ALL
|
|
422
|
+
SELECT * FROM attribute_data;
|
|
423
|
+
`;
|
|
424
|
+
return { sql, params: [key] };
|
|
425
|
+
}
|
|
426
|
+
else {
|
|
427
|
+
// Non-job tables
|
|
428
|
+
const sql = context.appendExpiryClause(`
|
|
429
|
+
SELECT field, value
|
|
430
|
+
FROM ${tableName}
|
|
431
|
+
WHERE key = $1
|
|
432
|
+
`, tableName);
|
|
433
|
+
return { sql, params: [key] };
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
exports._hgetall = _hgetall;
|
|
437
|
+
function _hincrbyfloat(context, key, field, increment) {
|
|
438
|
+
const tableName = context.tableForKey(key, 'hash');
|
|
439
|
+
const isJobsTableResult = (0, utils_1.isJobsTable)(tableName);
|
|
440
|
+
const isStatusField = field === ':';
|
|
441
|
+
if (isJobsTableResult && isStatusField) {
|
|
442
|
+
const sql = `
|
|
443
|
+
UPDATE ${tableName}
|
|
444
|
+
SET status = status + $2
|
|
445
|
+
WHERE key = $1 AND is_live
|
|
446
|
+
RETURNING status::text AS value
|
|
447
|
+
`;
|
|
448
|
+
return { sql, params: [key, increment] };
|
|
449
|
+
}
|
|
450
|
+
else if (isJobsTableResult) {
|
|
451
|
+
// Update the condition here
|
|
452
|
+
const sql = `
|
|
453
|
+
WITH valid_job AS (
|
|
454
|
+
SELECT id
|
|
455
|
+
FROM ${tableName}
|
|
456
|
+
WHERE key = $1 AND is_live
|
|
457
|
+
)
|
|
458
|
+
INSERT INTO ${tableName}_attributes (job_id, field, value, type)
|
|
459
|
+
SELECT id, $2, ($3::double precision)::text, $4
|
|
460
|
+
FROM valid_job
|
|
461
|
+
ON CONFLICT (job_id, field) DO UPDATE
|
|
462
|
+
SET
|
|
463
|
+
value = ((COALESCE(${tableName}_attributes.value, '0')::double precision) + $3::double precision)::text,
|
|
464
|
+
type = EXCLUDED.type
|
|
465
|
+
RETURNING value;
|
|
466
|
+
`;
|
|
467
|
+
return { sql, params: [key, field, increment, (0, utils_1.deriveType)(field)] };
|
|
468
|
+
}
|
|
469
|
+
else {
|
|
470
|
+
const sql = `
|
|
471
|
+
INSERT INTO ${tableName} (key, field, value)
|
|
472
|
+
VALUES ($1, $2, ($3)::text)
|
|
473
|
+
ON CONFLICT (key, field) DO UPDATE
|
|
474
|
+
SET value = ((COALESCE(${tableName}.value, '0')::double precision + $3::double precision)::text)
|
|
475
|
+
RETURNING value
|
|
476
|
+
`;
|
|
477
|
+
return { sql, params: [key, field, increment] };
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
exports._hincrbyfloat = _hincrbyfloat;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { HashContext, SqlResult, ProviderTransaction } from './types';
|
|
2
|
+
export declare function createExpireOperations(context: HashContext['context']): {
|
|
3
|
+
expire(key: string, seconds: number, multi?: ProviderTransaction): Promise<boolean>;
|
|
4
|
+
};
|
|
5
|
+
export declare function _expire(context: HashContext['context'], key: string, seconds: number): SqlResult;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports._expire = exports.createExpireOperations = void 0;
|
|
4
|
+
function createExpireOperations(context) {
|
|
5
|
+
return {
|
|
6
|
+
async expire(key, seconds, multi) {
|
|
7
|
+
const { sql, params } = _expire(context, key, seconds);
|
|
8
|
+
if (multi) {
|
|
9
|
+
multi.addCommand(sql, params, 'boolean');
|
|
10
|
+
return Promise.resolve(true);
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
const res = await context.pgClient.query(sql, params);
|
|
14
|
+
return res.rowCount > 0;
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
exports.createExpireOperations = createExpireOperations;
|
|
20
|
+
function _expire(context, key, seconds) {
|
|
21
|
+
// Only job tables are ever expired
|
|
22
|
+
const tableName = context.tableForKey(key);
|
|
23
|
+
const expiryTime = new Date(Date.now() + seconds * 1000);
|
|
24
|
+
const sql = `
|
|
25
|
+
UPDATE ${tableName}
|
|
26
|
+
SET expired_at = $2
|
|
27
|
+
WHERE key = $1 AND is_live
|
|
28
|
+
RETURNING true as success
|
|
29
|
+
`;
|
|
30
|
+
const params = [key, expiryTime];
|
|
31
|
+
return { sql, params };
|
|
32
|
+
}
|
|
33
|
+
exports._expire = _expire;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { KVSQL } from '../../kvsql';
|
|
2
|
+
import { HSetOptions } from './types';
|
|
3
|
+
import { isJobsTable } from './utils';
|
|
4
|
+
export declare const hashModule: (context: KVSQL) => {
|
|
5
|
+
hset(key: string, fields: Record<string, string>, options?: HSetOptions, multi?: any): Promise<number | any>;
|
|
6
|
+
_hset: (key: string, fields: Record<string, string>, options?: HSetOptions) => import("./types").SqlResult;
|
|
7
|
+
_hget: (key: string, field: string) => import("./types").SqlResult;
|
|
8
|
+
_hdel: (key: string, fields: string[]) => import("./types").SqlResult;
|
|
9
|
+
_hmget: (key: string, fields: string[]) => import("./types").SqlResult;
|
|
10
|
+
_hincrbyfloat: (key: string, field: string, increment: number) => import("./types").SqlResult;
|
|
11
|
+
_hscan: (key: string, cursor: string, count: number, pattern?: string) => import("./types").SqlResult;
|
|
12
|
+
_expire: (key: string, seconds: number) => import("./types").SqlResult;
|
|
13
|
+
_scan: (cursor: number, count: number, pattern?: string) => import("./types").SqlResult;
|
|
14
|
+
isJobsTable: typeof isJobsTable;
|
|
15
|
+
expire(key: string, seconds: number, multi?: import("./types").ProviderTransaction): Promise<boolean>;
|
|
16
|
+
hscan(key: string, cursor: string, count?: number, pattern?: string, multi?: import("./types").ProviderTransaction): Promise<import("./types").HScanResult>;
|
|
17
|
+
scan(cursor: number, count?: number, pattern?: string, multi?: import("./types").ProviderTransaction): Promise<{
|
|
18
|
+
cursor: number;
|
|
19
|
+
keys: string[];
|
|
20
|
+
}>;
|
|
21
|
+
hsetnx(key: string, field: string, value: string, multi?: import("./types").ProviderTransaction, entity?: string): Promise<number>;
|
|
22
|
+
hget(key: string, field: string, multi?: import("./types").ProviderTransaction): Promise<string>;
|
|
23
|
+
hdel(key: string, fields: string[], multi?: unknown): Promise<number>;
|
|
24
|
+
hmget(key: string, fields: string[], multi?: import("./types").ProviderTransaction): Promise<string[]>;
|
|
25
|
+
hgetall(key: string, multi?: import("./types").ProviderTransaction): Promise<Record<string, string>>;
|
|
26
|
+
hincrbyfloat(key: string, field: string, increment: number, multi?: import("./types").ProviderTransaction): Promise<number>;
|
|
27
|
+
};
|
|
28
|
+
export { isJobsTable, deriveType } from './utils';
|
|
29
|
+
export * from './types';
|