@workglow/postgres 0.2.34 → 0.2.36
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 +51 -0
- package/dist/job-queue/browser.d.ts.map +1 -1
- package/dist/job-queue/browser.js +11 -1
- package/dist/job-queue/browser.js.map +3 -3
- package/dist/job-queue/bun.d.ts.map +1 -1
- package/dist/job-queue/common.d.ts.map +1 -1
- package/dist/job-queue/node.d.ts.map +1 -1
- package/dist/job-queue/node.js +11 -1
- package/dist/job-queue/node.js.map +3 -3
- package/dist/migrations/PostgresMigrationRunner.d.ts +7 -3
- package/dist/migrations/PostgresMigrationRunner.d.ts.map +1 -1
- package/dist/migrations/common.d.ts.map +1 -1
- package/dist/storage/PostgresTabularStorage.d.ts +14 -2
- package/dist/storage/PostgresTabularStorage.d.ts.map +1 -1
- package/dist/storage/PostgresVectorStorage.d.ts +1 -5
- package/dist/storage/PostgresVectorStorage.d.ts.map +1 -1
- package/dist/storage/browser.d.ts.map +1 -1
- package/dist/storage/browser.js +50 -106
- package/dist/storage/browser.js.map +4 -4
- package/dist/storage/bun.d.ts.map +1 -1
- package/dist/storage/common.d.ts.map +1 -1
- package/dist/storage/node.d.ts.map +1 -1
- package/dist/storage/node.js +60 -106
- package/dist/storage/node.js.map +5 -5
- package/dist/text/PostgresFtsTextIndex.d.ts +108 -0
- package/dist/text/PostgresFtsTextIndex.d.ts.map +1 -0
- package/dist/text/browser.d.ts +7 -0
- package/dist/text/browser.d.ts.map +1 -0
- package/dist/text/browser.js +185 -0
- package/dist/text/browser.js.map +10 -0
- package/dist/text/bun.d.ts +7 -0
- package/dist/text/bun.d.ts.map +1 -0
- package/dist/text/common.d.ts +7 -0
- package/dist/text/common.d.ts.map +1 -0
- package/dist/text/node.d.ts +7 -0
- package/dist/text/node.d.ts.map +1 -0
- package/dist/text/node.js +185 -0
- package/dist/text/node.js.map +10 -0
- package/package.json +25 -9
package/dist/storage/node.js
CHANGED
|
@@ -639,6 +639,52 @@ class PostgresTabularStorage extends BaseSqlTabularStorage {
|
|
|
639
639
|
this.events.emit("get", key, val);
|
|
640
640
|
return val;
|
|
641
641
|
}
|
|
642
|
+
async getBulk(keys) {
|
|
643
|
+
if (keys.length === 0)
|
|
644
|
+
return [];
|
|
645
|
+
const pkColCount = this.primaryKeyColumns().length;
|
|
646
|
+
const chunkSize = Math.max(1, Math.floor(30000 / pkColCount));
|
|
647
|
+
let rows;
|
|
648
|
+
if (keys.length <= chunkSize) {
|
|
649
|
+
rows = await this.mutex(() => this._getBulkInternal(keys));
|
|
650
|
+
} else {
|
|
651
|
+
rows = [];
|
|
652
|
+
for (let i = 0;i < keys.length; i += chunkSize) {
|
|
653
|
+
const chunk = keys.slice(i, i + chunkSize);
|
|
654
|
+
const chunkRows = await this.mutex(() => this._getBulkInternal(chunk));
|
|
655
|
+
rows.push(...chunkRows);
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
this.events.emit("getBulk", keys, rows);
|
|
659
|
+
return rows;
|
|
660
|
+
}
|
|
661
|
+
async _getBulkInternal(keys) {
|
|
662
|
+
const db = this.db;
|
|
663
|
+
const pkCols = this.primaryKeyColumns();
|
|
664
|
+
const params = [];
|
|
665
|
+
const tuples = [];
|
|
666
|
+
let p = 1;
|
|
667
|
+
for (const key of keys) {
|
|
668
|
+
const ordered = this.getPrimaryKeyAsOrderedArray(key);
|
|
669
|
+
params.push(...ordered);
|
|
670
|
+
const slots = [];
|
|
671
|
+
for (let i = 0;i < pkCols.length; i++) {
|
|
672
|
+
slots.push(`$${p++}`);
|
|
673
|
+
}
|
|
674
|
+
tuples.push(`(${slots.join(", ")})`);
|
|
675
|
+
}
|
|
676
|
+
const lhs = pkCols.length === 1 ? `"${pkCols[0]}"` : `(${pkCols.map((c) => `"${c}"`).join(", ")})`;
|
|
677
|
+
const rhs = pkCols.length === 1 ? params.map((_, i) => `$${i + 1}`).join(", ") : tuples.join(", ");
|
|
678
|
+
const sql = `SELECT * FROM "${this.table}" WHERE ${lhs} IN (${rhs})`;
|
|
679
|
+
const result = await db.query(sql, params);
|
|
680
|
+
for (const row of result.rows) {
|
|
681
|
+
const record = row;
|
|
682
|
+
for (const key in this.schema.properties) {
|
|
683
|
+
record[key] = this.sqlToJsValue(key, record[key]);
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
return result.rows;
|
|
687
|
+
}
|
|
642
688
|
async delete(value) {
|
|
643
689
|
return this.mutex(() => this._deleteInternal(value));
|
|
644
690
|
}
|
|
@@ -711,10 +757,10 @@ class PostgresTabularStorage extends BaseSqlTabularStorage {
|
|
|
711
757
|
const result = await db.query(`SELECT COUNT(*) FROM "${this.table}" WHERE ${whereClause}`, params);
|
|
712
758
|
return parseInt(result.rows[0].count, 10);
|
|
713
759
|
}
|
|
714
|
-
async
|
|
715
|
-
return this.mutex(() => this.
|
|
760
|
+
async getOffsetPage(offset, limit) {
|
|
761
|
+
return this.mutex(() => this._getOffsetPageInternal(offset, limit));
|
|
716
762
|
}
|
|
717
|
-
async
|
|
763
|
+
async _getOffsetPageInternal(offset, limit) {
|
|
718
764
|
const db = this.db;
|
|
719
765
|
const orderByClause = this.primaryKeyColumns().map((col) => `"${String(col)}"`).join(", ");
|
|
720
766
|
const result = await db.query(`SELECT * FROM "${this.table}" ORDER BY ${orderByClause} LIMIT $1 OFFSET $2`, [limit, offset]);
|
|
@@ -1063,74 +1109,6 @@ class PostgresVectorStorage extends PostgresTabularStorage {
|
|
|
1063
1109
|
return this.searchFallback(query, options);
|
|
1064
1110
|
}
|
|
1065
1111
|
}
|
|
1066
|
-
async hybridSearch(query, options) {
|
|
1067
|
-
const { topK = 10, filter, scoreThreshold = 0, textQuery, vectorWeight = 0.7 } = options;
|
|
1068
|
-
if (!textQuery || textQuery.trim().length === 0) {
|
|
1069
|
-
return this.similaritySearch(query, { topK, filter, scoreThreshold });
|
|
1070
|
-
}
|
|
1071
|
-
try {
|
|
1072
|
-
const queryVector = `[${Array.from(query).join(",")}]`;
|
|
1073
|
-
const tsQueryText = textQuery;
|
|
1074
|
-
const vectorColRaw = String(this.vectorPropertyName);
|
|
1075
|
-
const vectorCol = PostgresDialect2.quoteId(vectorColRaw);
|
|
1076
|
-
const metadataColRaw = this.metadataPropertyName ? String(this.metadataPropertyName) : null;
|
|
1077
|
-
const metadataCol = metadataColRaw ? PostgresDialect2.quoteId(metadataColRaw) : null;
|
|
1078
|
-
const vectorScoreExpr = this.buildScoreExpr(vectorCol, "$1");
|
|
1079
|
-
const combinedScoreExpr = `(
|
|
1080
|
-
$2 * ${vectorScoreExpr} +
|
|
1081
|
-
$3 * ts_rank(to_tsvector('english', ${metadataCol || "''"}::text), plainto_tsquery('english', $4))
|
|
1082
|
-
)`;
|
|
1083
|
-
let sql = `
|
|
1084
|
-
SELECT
|
|
1085
|
-
*,
|
|
1086
|
-
${combinedScoreExpr} as score
|
|
1087
|
-
FROM "${this.table}"
|
|
1088
|
-
`;
|
|
1089
|
-
const params = [queryVector, vectorWeight, 1 - vectorWeight, tsQueryText];
|
|
1090
|
-
let paramIndex = 5;
|
|
1091
|
-
let hasWhere = false;
|
|
1092
|
-
if (filter && Object.keys(filter).length > 0 && metadataCol) {
|
|
1093
|
-
const conditions = [];
|
|
1094
|
-
for (const [key, value] of Object.entries(filter)) {
|
|
1095
|
-
if (!SAFE_IDENTIFIER_RE.test(key)) {
|
|
1096
|
-
throw new StorageValidationError(`Invalid metadata filter key: "${key}". Keys must match /^[a-zA-Z_][a-zA-Z0-9_]*$/.`);
|
|
1097
|
-
}
|
|
1098
|
-
conditions.push(`${metadataCol}->>'${key}' = $${paramIndex}`);
|
|
1099
|
-
params.push(String(value));
|
|
1100
|
-
paramIndex++;
|
|
1101
|
-
}
|
|
1102
|
-
if (conditions.length > 0) {
|
|
1103
|
-
sql += ` WHERE ${conditions.join(" AND ")}`;
|
|
1104
|
-
hasWhere = true;
|
|
1105
|
-
}
|
|
1106
|
-
}
|
|
1107
|
-
sql += hasWhere ? " AND" : " WHERE";
|
|
1108
|
-
sql += ` ${combinedScoreExpr} >= $${paramIndex}`;
|
|
1109
|
-
params.push(scoreThreshold);
|
|
1110
|
-
paramIndex++;
|
|
1111
|
-
sql += ` ORDER BY score DESC LIMIT $${paramIndex}`;
|
|
1112
|
-
params.push(topK);
|
|
1113
|
-
const result = await this.db.query(sql, params);
|
|
1114
|
-
const results = [];
|
|
1115
|
-
for (const row of result.rows) {
|
|
1116
|
-
const vectorResult = await this.db.query(`SELECT ${vectorCol}::text FROM "${this.table}" WHERE ${this.getPrimaryKeyWhereClause()}`, this.getPrimaryKeyValues(row));
|
|
1117
|
-
const vectorStr = vectorResult.rows[0]?.[vectorColRaw] || "[]";
|
|
1118
|
-
const vectorArray = JSON.parse(vectorStr);
|
|
1119
|
-
results.push({
|
|
1120
|
-
...row,
|
|
1121
|
-
[this.vectorPropertyName]: new this.vectorCtor(vectorArray),
|
|
1122
|
-
score: parseFloat(row.score)
|
|
1123
|
-
});
|
|
1124
|
-
}
|
|
1125
|
-
return results;
|
|
1126
|
-
} catch (error) {
|
|
1127
|
-
if (error instanceof StorageValidationError) {
|
|
1128
|
-
throw error;
|
|
1129
|
-
}
|
|
1130
|
-
console.error("pgvector hybrid query failed, falling back to in-memory search:", error);
|
|
1131
|
-
return this.hybridSearchFallback(query, options);
|
|
1132
|
-
}
|
|
1133
|
-
}
|
|
1134
1112
|
assertFallbackSupportsDistance() {
|
|
1135
1113
|
if (this.distance !== "cosine") {
|
|
1136
1114
|
throw new Error(`PostgresVectorStorage: pgvector is unavailable and the in-memory ` + `fallback only supports cosine distance (configured: "${this.distance}"). ` + `Install pgvector or switch the storage to cosine distance.`);
|
|
@@ -1156,40 +1134,6 @@ class PostgresVectorStorage extends PostgresTabularStorage {
|
|
|
1156
1134
|
const topResults = results.slice(0, topK);
|
|
1157
1135
|
return topResults;
|
|
1158
1136
|
}
|
|
1159
|
-
async hybridSearchFallback(query, options) {
|
|
1160
|
-
this.assertFallbackSupportsDistance();
|
|
1161
|
-
const { topK = 10, filter, scoreThreshold = 0, textQuery, vectorWeight = 0.7 } = options;
|
|
1162
|
-
const allRows = await this.getAll() || [];
|
|
1163
|
-
const results = [];
|
|
1164
|
-
const queryLower = textQuery.toLowerCase();
|
|
1165
|
-
const queryWords = queryLower.split(/\s+/).filter((w) => w.length > 0);
|
|
1166
|
-
for (const row of allRows) {
|
|
1167
|
-
const vector = row[this.vectorPropertyName];
|
|
1168
|
-
const metadata = this.metadataPropertyName ? row[this.metadataPropertyName] : {};
|
|
1169
|
-
if (filter && !this.matchesFilter(metadata, filter)) {
|
|
1170
|
-
continue;
|
|
1171
|
-
}
|
|
1172
|
-
const vectorScore = cosineSimilarity(query, vector);
|
|
1173
|
-
const metadataText = Object.values(metadata ?? {}).join(" ").toLowerCase();
|
|
1174
|
-
let textScore = 0;
|
|
1175
|
-
if (queryWords.length > 0) {
|
|
1176
|
-
let matches = 0;
|
|
1177
|
-
for (const word of queryWords) {
|
|
1178
|
-
if (metadataText.includes(word)) {
|
|
1179
|
-
matches++;
|
|
1180
|
-
}
|
|
1181
|
-
}
|
|
1182
|
-
textScore = matches / queryWords.length;
|
|
1183
|
-
}
|
|
1184
|
-
const combinedScore = vectorWeight * vectorScore + (1 - vectorWeight) * textScore;
|
|
1185
|
-
if (combinedScore >= scoreThreshold) {
|
|
1186
|
-
results.push({ ...row, score: combinedScore });
|
|
1187
|
-
}
|
|
1188
|
-
}
|
|
1189
|
-
results.sort((a, b) => b.score - a.score);
|
|
1190
|
-
const topResults = results.slice(0, topK);
|
|
1191
|
-
return topResults;
|
|
1192
|
-
}
|
|
1193
1137
|
getPrimaryKeyWhereClause() {
|
|
1194
1138
|
const conditions = this.primaryKeyNames.map((key, idx) => `${String(key)} = $${idx + 1}`);
|
|
1195
1139
|
return conditions.join(" AND ");
|
|
@@ -1211,6 +1155,7 @@ import {
|
|
|
1211
1155
|
MIGRATIONS_TABLE as MIGRATIONS_TABLE2,
|
|
1212
1156
|
sortMigrations
|
|
1213
1157
|
} from "@workglow/storage";
|
|
1158
|
+
var runLocks = new WeakMap;
|
|
1214
1159
|
|
|
1215
1160
|
class PostgresMigrationRunner {
|
|
1216
1161
|
db;
|
|
@@ -1243,6 +1188,15 @@ class PostgresMigrationRunner {
|
|
|
1243
1188
|
} };
|
|
1244
1189
|
}
|
|
1245
1190
|
async run(migrations, options = {}) {
|
|
1191
|
+
const key = this.db;
|
|
1192
|
+
const prev = runLocks.get(key) ?? Promise.resolve();
|
|
1193
|
+
const result = prev.then(() => this.runInternal(migrations, options));
|
|
1194
|
+
runLocks.set(key, result.catch(() => {
|
|
1195
|
+
return;
|
|
1196
|
+
}));
|
|
1197
|
+
return result;
|
|
1198
|
+
}
|
|
1199
|
+
async runInternal(migrations, options) {
|
|
1246
1200
|
await this.ensureBookkeepingTable();
|
|
1247
1201
|
const sorted = sortMigrations(migrations);
|
|
1248
1202
|
const applied = [];
|
|
@@ -1328,4 +1282,4 @@ export {
|
|
|
1328
1282
|
POSTGRES_KV_REPOSITORY
|
|
1329
1283
|
};
|
|
1330
1284
|
|
|
1331
|
-
//# debugId=
|
|
1285
|
+
//# debugId=31CB72E3014C272564756E2164756E21
|