UNPKG

@langchain/community

Version:
1 lines • 9.83 kB
{"version":3,"file":"postgres.cjs","names":["namespace: string","config: PostgresRecordManagerOptions","e: any","index: number","numOfColumns: number","keys: string[]","updateOptions?: UpdateOptions","row: { ex: boolean }","options?: ListKeyOptions","values: (string | number | (string | null)[])[]","row: { key: string }"],"sources":["../../src/indexes/postgres.ts"],"sourcesContent":["import pg, { PoolConfig, Pool } from \"pg\";\nimport {\n ListKeyOptions,\n RecordManagerInterface,\n UpdateOptions,\n} from \"@langchain/core/indexing\";\n\nexport type PostgresRecordManagerOptions = {\n postgresConnectionOptions?: PoolConfig;\n pool?: Pool;\n tableName?: string;\n schema?: string;\n};\n\nexport class PostgresRecordManager implements RecordManagerInterface {\n lc_namespace = [\"langchain\", \"recordmanagers\", \"postgres\"];\n\n pool: Pool;\n\n tableName: string;\n\n namespace: string;\n\n finalTableName: string;\n\n constructor(namespace: string, config: PostgresRecordManagerOptions) {\n const { postgresConnectionOptions, tableName, pool } = config;\n this.namespace = namespace;\n if (!postgresConnectionOptions && !pool) {\n throw new Error(\n \"You must provide either a `postgresConnectionOptions` object or a `pool` instance.\"\n );\n }\n this.pool = pool ?? new pg.Pool(postgresConnectionOptions);\n this.tableName = tableName || \"upsertion_records\";\n this.finalTableName = config.schema\n ? `\"${config.schema}\".\"${this.tableName}\"`\n : `\"${this.tableName}\"`;\n }\n\n async createSchema(): Promise<void> {\n try {\n await this.pool.query(`\n CREATE TABLE IF NOT EXISTS ${this.finalTableName} (\n uuid UUID PRIMARY KEY DEFAULT gen_random_uuid(),\n key TEXT NOT NULL,\n namespace TEXT NOT NULL,\n updated_at Double PRECISION NOT NULL,\n group_id TEXT,\n UNIQUE (key, namespace)\n );\n CREATE INDEX IF NOT EXISTS updated_at_index ON ${this.finalTableName} (updated_at);\n CREATE INDEX IF NOT EXISTS key_index ON ${this.finalTableName} (key);\n CREATE INDEX IF NOT EXISTS namespace_index ON ${this.finalTableName} (namespace);\n CREATE INDEX IF NOT EXISTS group_id_index ON ${this.finalTableName} (group_id);`);\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } catch (e: any) {\n // This error indicates that the table already exists\n // Due to asynchronous nature of the code, it is possible that\n // the table is created between the time we check if it exists\n // and the time we try to create it. It can be safely ignored.\n if (\"code\" in e && e.code === \"23505\") {\n return;\n }\n throw e;\n }\n }\n\n async getTime(): Promise<number> {\n const res = await this.pool.query(\n \"SELECT EXTRACT(EPOCH FROM CURRENT_TIMESTAMP) AS extract\"\n );\n return Number.parseFloat(res.rows[0].extract);\n }\n\n /**\n * Generates the SQL placeholders for a specific row at the provided index.\n *\n * @param index - The index of the row for which placeholders need to be generated.\n * @param numOfColumns - The number of columns we are inserting data into.\n * @returns The SQL placeholders for the row values.\n */\n private generatePlaceholderForRowAt(\n index: number,\n numOfColumns: number\n ): string {\n const placeholders = [];\n for (let i = 0; i < numOfColumns; i += 1) {\n placeholders.push(`$${index * numOfColumns + i + 1}`);\n }\n return `(${placeholders.join(\", \")})`;\n }\n\n async update(keys: string[], updateOptions?: UpdateOptions): Promise<void> {\n if (keys.length === 0) {\n return;\n }\n\n const updatedAt = await this.getTime();\n const { timeAtLeast, groupIds: _groupIds } = updateOptions ?? {};\n\n if (timeAtLeast && updatedAt < timeAtLeast) {\n throw new Error(\n `Time sync issue with database ${updatedAt} < ${timeAtLeast}`\n );\n }\n\n const groupIds = _groupIds ?? keys.map(() => null);\n\n if (groupIds.length !== keys.length) {\n throw new Error(\n `Number of keys (${keys.length}) does not match number of group_ids ${groupIds.length})`\n );\n }\n\n const recordsToUpsert = keys.map((key, i) => [\n key,\n this.namespace,\n updatedAt,\n groupIds[i],\n ]);\n\n const valuesPlaceholders = recordsToUpsert\n .map((_, j) =>\n this.generatePlaceholderForRowAt(j, recordsToUpsert[0].length)\n )\n .join(\", \");\n\n 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;`;\n await this.pool.query(query, recordsToUpsert.flat());\n }\n\n async exists(keys: string[]): Promise<boolean[]> {\n if (keys.length === 0) {\n return [];\n }\n\n const startIndex = 2;\n const arrayPlaceholders = keys\n .map((_, i) => `$${i + startIndex}`)\n .join(\", \");\n\n const query = `\n WITH ordered_keys AS (\n SELECT * FROM unnest(ARRAY[${arrayPlaceholders}]) WITH ORDINALITY as t(key, o)\n )\n SELECT ok.key, (r.key IS NOT NULL) ex\n FROM ordered_keys ok \n LEFT JOIN ${this.finalTableName} r \n ON r.key = ok.key \n AND namespace = $1\n ORDER BY ok.o;\n `;\n const res = await this.pool.query(query, [this.namespace, ...keys.flat()]);\n return res.rows.map((row: { ex: boolean }) => row.ex);\n }\n\n async listKeys(options?: ListKeyOptions): Promise<string[]> {\n const { before, after, limit, groupIds } = options ?? {};\n\n let query = `SELECT key FROM ${this.finalTableName} WHERE namespace = $1`;\n const values: (string | number | (string | null)[])[] = [this.namespace];\n\n let index = 2;\n if (before) {\n values.push(before);\n query += ` AND updated_at < $${index}`;\n index += 1;\n }\n\n if (after) {\n values.push(after);\n query += ` AND updated_at > $${index}`;\n index += 1;\n }\n\n if (limit) {\n values.push(limit);\n query += ` LIMIT $${index}`;\n index += 1;\n }\n\n if (groupIds) {\n values.push(groupIds);\n query += ` AND group_id = ANY($${index})`;\n index += 1;\n }\n\n query += \";\";\n const res = await this.pool.query(query, values);\n return res.rows.map((row: { key: string }) => row.key);\n }\n\n async deleteKeys(keys: string[]): Promise<void> {\n if (keys.length === 0) {\n return;\n }\n\n const query = `DELETE FROM ${this.finalTableName} WHERE namespace = $1 AND key = ANY($2);`;\n await this.pool.query(query, [this.namespace, keys]);\n }\n\n /**\n * Terminates the connection pool.\n * @returns {Promise<void>}\n */\n async end(): Promise<void> {\n await this.pool.end();\n }\n}\n"],"mappings":"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"}