@langchain/community
Version:
Third-party integrations for LangChain.js
136 lines (135 loc) • 5.07 kB
JavaScript
import { __exportAll } from "../_virtual/_rolldown/runtime.js";
import pg from "pg";
//#region src/indexes/postgres.ts
var postgres_exports = /* @__PURE__ */ __exportAll({ PostgresRecordManager: () => PostgresRecordManager });
var PostgresRecordManager = class {
lc_namespace = [
"langchain",
"recordmanagers",
"postgres"
];
pool;
tableName;
namespace;
finalTableName;
constructor(namespace, config) {
const { postgresConnectionOptions, tableName, pool } = config;
this.namespace = namespace;
if (!postgresConnectionOptions && !pool) throw new Error("You must provide either a `postgresConnectionOptions` object or a `pool` instance.");
this.pool = pool ?? new pg.Pool(postgresConnectionOptions);
this.tableName = tableName || "upsertion_records";
this.finalTableName = config.schema ? `"${config.schema}"."${this.tableName}"` : `"${this.tableName}"`;
}
async createSchema() {
try {
await this.pool.query(`
CREATE TABLE IF NOT EXISTS ${this.finalTableName} (
uuid UUID PRIMARY KEY DEFAULT gen_random_uuid(),
key TEXT NOT NULL,
namespace TEXT NOT NULL,
updated_at Double PRECISION NOT NULL,
group_id TEXT,
UNIQUE (key, namespace)
);
CREATE INDEX IF NOT EXISTS updated_at_index ON ${this.finalTableName} (updated_at);
CREATE INDEX IF NOT EXISTS key_index ON ${this.finalTableName} (key);
CREATE INDEX IF NOT EXISTS namespace_index ON ${this.finalTableName} (namespace);
CREATE INDEX IF NOT EXISTS group_id_index ON ${this.finalTableName} (group_id);`);
} catch (e) {
if ("code" in e && e.code === "23505") return;
throw e;
}
}
async getTime() {
const res = await this.pool.query("SELECT EXTRACT(EPOCH FROM CURRENT_TIMESTAMP) AS extract");
return Number.parseFloat(res.rows[0].extract);
}
/**
* Generates the SQL placeholders for a specific row at the provided index.
*
* @param index - The index of the row for which placeholders need to be generated.
* @param numOfColumns - The number of columns we are inserting data into.
* @returns The SQL placeholders for the row values.
*/
generatePlaceholderForRowAt(index, numOfColumns) {
const placeholders = [];
for (let i = 0; i < numOfColumns; i += 1) placeholders.push(`$${index * numOfColumns + i + 1}`);
return `(${placeholders.join(", ")})`;
}
async update(keys, updateOptions) {
if (keys.length === 0) return;
const updatedAt = await this.getTime();
const { timeAtLeast, groupIds: _groupIds } = updateOptions ?? {};
if (timeAtLeast && updatedAt < timeAtLeast) throw new Error(`Time sync issue with database ${updatedAt} < ${timeAtLeast}`);
const groupIds = _groupIds ?? keys.map(() => null);
if (groupIds.length !== keys.length) throw new Error(`Number of keys (${keys.length}) does not match number of group_ids ${groupIds.length})`);
const recordsToUpsert = keys.map((key, i) => [
key,
this.namespace,
updatedAt,
groupIds[i]
]);
const valuesPlaceholders = recordsToUpsert.map((_, j) => this.generatePlaceholderForRowAt(j, recordsToUpsert[0].length)).join(", ");
const query = `INSERT INTO ${this.finalTableName} (key, namespace, updated_at, group_id) VALUES ${valuesPlaceholders} ON CONFLICT (key, namespace) DO UPDATE SET updated_at = EXCLUDED.updated_at;`;
await this.pool.query(query, recordsToUpsert.flat());
}
async exists(keys) {
if (keys.length === 0) return [];
const startIndex = 2;
const query = `
WITH ordered_keys AS (
SELECT * FROM unnest(ARRAY[${keys.map((_, i) => `$${i + startIndex}`).join(", ")}]) WITH ORDINALITY as t(key, o)
)
SELECT ok.key, (r.key IS NOT NULL) ex
FROM ordered_keys ok
LEFT JOIN ${this.finalTableName} r
ON r.key = ok.key
AND namespace = $1
ORDER BY ok.o;
`;
return (await this.pool.query(query, [this.namespace, ...keys.flat()])).rows.map((row) => row.ex);
}
async listKeys(options) {
const { before, after, limit, groupIds } = options ?? {};
let query = `SELECT key FROM ${this.finalTableName} WHERE namespace = $1`;
const values = [this.namespace];
let index = 2;
if (before) {
values.push(before);
query += ` AND updated_at < $${index}`;
index += 1;
}
if (after) {
values.push(after);
query += ` AND updated_at > $${index}`;
index += 1;
}
if (limit) {
values.push(limit);
query += ` LIMIT $${index}`;
index += 1;
}
if (groupIds) {
values.push(groupIds);
query += ` AND group_id = ANY($${index})`;
index += 1;
}
query += ";";
return (await this.pool.query(query, values)).rows.map((row) => row.key);
}
async deleteKeys(keys) {
if (keys.length === 0) return;
const query = `DELETE FROM ${this.finalTableName} WHERE namespace = $1 AND key = ANY($2);`;
await this.pool.query(query, [this.namespace, keys]);
}
/**
* Terminates the connection pool.
* @returns {Promise<void>}
*/
async end() {
await this.pool.end();
}
};
//#endregion
export { PostgresRecordManager, postgres_exports };
//# sourceMappingURL=postgres.js.map