Static schemaThe statements that make one collection's table and its three indexes, for a caller that runs its own
migrations with manageSchema: false. The extension itself (CREATE EXTENSION vector) is the caller's.
The collection's name: letters, digits and underscores, at most 54 characters with the prefix.
The length of the embeddings the collection holds.
Optional options: "cosine" | "euclidean" | "dotproduct" | { The similarity metric ('cosine' when left out), on its own or with the table prefix and the
text search configuration ('english' when left out). Give the store the same three.
The CREATE TABLE statement and the three CREATE INDEX statements, in the order to run them.
When a name or the dimension cannot be written into SQL as given, or the name with its prefix is longer than 54 characters.
for (const statement of PostgresVectorStore.schemaSql('chunks', 1536, { tablePrefix: 'app_', textSearchConfig: 'simple' })) {
await pool.query(statement);
}
Initialize the selected connection and optionally manage collection metadata: take the caller's adapter or
pool, or open a pool from the connection string; then, unless manageSchema is false, ensure the pgvector
extension exists and create the collections metadata table.
vector_connection_ambiguous when an adapter is given with a pool or a connection string, without
contacting a database; vector_adapter_requires_postgres_transactions when the adapter is not a PostgreSQL
adapter with transactions; vector_transaction_closed on a view of a transaction whose callback has ended;
and an error when the configuration holds no adapter, pool or connection string.
Borrow only the transaction adapter supplied to StorageAdapter.transaction. work gets a view of this store
whose every statement runs on that transaction's connection, so its vector writes commit or roll back with the
caller's own writes in the same transaction. The view opens no pool, commits nothing and runs no DDL: like a
store with manageSchema: false, it takes the metric from similarityMetric. The adapter the store was made
with is refused in place of the transaction, on the store and on a view of it: on that adapter the view's
writes would run on pooled connections and commit at once, outside the caller's transaction.
When work settles the view stops working: close() and shutdown() do nothing, healthCheck() answers
false, checkHealth() answers { isHealthy: false }, fetchByIds() with no ids answers an empty list, and
every other call throws vector_transaction_closed.
Make the calls on the view one at a time, awaiting each inside work: they share the transaction's one
connection, and two searches with settings at once interleave their SET LOCAL and restore statements there,
so that one can run under the transaction's own values and the transaction can go on under the store's. Keep
neither the view nor the transaction after work, and let a failure reach the transaction's callback, so that
the transaction rolls back: a transaction that catches a failed write and commits is outside this contract.
Compute embeddings before the transaction opens, not inside it.
The adapter that StorageAdapter.transaction passed to its callback.
The reads and writes to run in the transaction.
What work returns.
vector_adapter_required when the store was configured with a pool or a connection string rather
than an adapter; vector_transaction_required when transaction is the adapter the store was made with;
vector_adapter_requires_postgres_transactions when transaction is not a PostgreSQL adapter with
transactions; vector_transaction_closed when called on a view whose callback has ended; and whatever
work throws.
await db.transaction((tx) => store.withStorageTransaction(tx, async (vectors) => {
await tx.run('UPDATE sources SET version = ? WHERE id = ?', [version, sourceId]);
await vectors.delete('chunks', undefined, { filter: { sourceId } });
await vectors.upsert('chunks', passages);
}));
Create a new collection (Postgres table) with pgvector HNSW index. Each index is made only when the table
has none of its kind, under a name that carries the table prefix; a name longer than the 63 bytes Postgres
keeps is shortened so that it stays the index's own. With manageSchema: false this does nothing: the
caller's migrations made the table.
Optional options: CreateCollectionOptionsInsert/update the batch atomically on the current storage connection: INSERT ... ON CONFLICT (id) DO UPDATE in one transaction, on a pool's client, in an adapter transaction, or in the caller's transaction for a view of PostgresVectorStore.withStorageTransaction.
Optional _options: UpsertOptionsQuery for top-K nearest neighbors using pgvector operators. Uses HNSW index for O(log n) approximate search.
Optional options: QueryOptionsFetch rows by primary key without similarity ranking. Used by
HybridSearcher to hydrate sparse-only RRF winners — a second
similarity query would return the next-K dense rows, not the
specific BM25 winners, and would misattribute rows under the
wrong fused-score position.
Returns an empty array for an empty id list without firing SQL.
similarityScore is set to 0 as a sentinel.
Optional options: { Optional includeOptional includeHybrid search combining pgvector ANN and tsvector BM25 in a single SQL query with Reciprocal Rank Fusion.
This runs as one query with two CTEs — no application-level fusion needed.
With match or prefix the lexical leg reads the query's own words, every word (match: 'all') or any word
('any', the default), each matching a stored word that begins with it when prefix is true; a query with no
word then runs the dense search alone. With neither, the leg reads the query with plainto_tsquery, which asks
for every word.
Optional options: QueryOptions & { Search on the tsvector column alone: no embedding, ranked by ts_rank. The query's words are runs of
letters and digits in lower case (at most 32); match asks for every word ('all') or any word ('any', the
default), and prefix matches a stored word that begins with a query word. A query with no word answers no
document and runs no search.
Optional options: QueryOptions & { Change the metadata of every document the filter matches: a key given as null is removed, the others
replace the stored ones, and the rest of the stored metadata stays.
When the filter holds no condition, so that no call changes every document.
Delete documents by id, else by a metadata filter (options.filter), else every document when
options.deleteAll is set. A filter that yields no condition deletes nothing.
Optional ids: string[]Optional options: DeleteOptions
A vector store on Postgres with pgvector: one table per collection, a dense search through its HNSW index, a lexical search through its
tsvcolumn, and both fused in one statement.