Before you begin
- pg_pinecone installed, as the quickstart shows.
- A Pinecone API key stored in the database. Store it with
SELECT pinecone.set_api_key('YOUR_API_KEY');. pg_pineconeinshared_preload_libraries, followed by a Postgres restart. See Preload the extension.- A table with a single-column primary key, which becomes each document’s ID. You must own the table.
Search a table
Create a sample table
This guide uses a table of help-center articles. The embeddings are only needed to combine text and vector search:
SQL
Declare the searchable columns
Call This creates a Pinecone index and queues the table’s existing rows to be copied to it. Only the declared columns leave Postgres. From then on, pg_pinecone copies every committed insert, update, and delete in the background.A list of column names uses English text analysis with stemming and stop words. To set options per column, pass an object instead. For example, on a table with a
pinecone.track_docs() with the text columns to index. metadata_columns lists columns you want to filter on, and vector_column is optional. text_columns takes a JSON list, and metadata_columns takes a Postgres array:SQL
sku column, turn off stemming and stop words so identifiers match exactly:SQL
Wait for the initial copy
Check progress with The copy is finished when
pinecone.status():SQL
docs_backfill_done equals docs_backfill_total. pinecone.flush_docs('articles') waits until everything committed so far has been delivered, then returns true. It returns false if delivery doesn’t finish within 30 seconds. Pinecone then takes up to about a minute to index delivered documents and make them searchable.Search the table
pinecone.search_docs() returns each match’s ID, score, and rank. Join back to the table for the rest of the row:SQL
Output
Filter results
Usefilter to restrict results by metadata columns, and must to require text in a column:
SQL
must accepts $match_phrase, $match_all, and $match_any. For the operators filter accepts, see Filter operators.
To write the whole query in Lucene syntax, set mode => 'query_string':
SQL
Combine text and vector search
If you declared avector_column, pass a query vector with vector, and name the column the text query must match with field. The text query narrows the results to rows that match it in that column, and the vector ranks what’s left:
SQL
Output
Search one tenant
To keep each tenant’s documents in their own Pinecone namespace, pass a tenant column aspartition_column when you call pinecone.track_docs(). Then name the tenant with partition on every search. This example uses a tickets table with a tenant_id column:
SQL
partition returns an error. With engine => 'local', partition becomes a condition on the column instead. pinecone.delete_partition('tickets', 42) deletes one tenant’s documents from Pinecone, and its rows stay in the table.
The partition column must be NOT NULL. Pinecone limits the number of namespaces per index by plan, and pg_pinecone warns when a table has more than 1,000 distinct values.
Highlight matches
pinecone.headline() marks the query terms in a column’s text, using the language you declared for that column:
SQL
Output
Search without Pinecone
Setengine => 'local' to answer the same call with Postgres’s built-in full-text search instead of Pinecone:
SQL
track_docs() if you name a column with field. It ranks results with Postgres’s ts_rank_cd instead of BM25 and requires every query word to match, so it can return fewer results than the remote engine, in a different order. It doesn’t support filter, must, vector, or mode => 'query_string', and it doesn’t create an index, so add a GIN index on large tables.
Manage a tracked table
Once a table is tracked, pg_pinecone keeps its Pinecone index in sync on its own. These functions handle the cases that need your attention:- Retry rejected documents: If Pinecone rejects a document, pg_pinecone sets it aside without stopping the rest of the table.
pinecone.failed_docs('articles')lists rejected documents and why, andpinecone.retry_failed_docs('articles')queues them again. - Re-send every row:
pinecone.backfill_docs('articles', clear => true)copies the whole table again, for example after restoring a database backup. - Change a declared column: Stop tracking the table before you drop, rename, or change the type of a declared column.
- Stop tracking:
pinecone.untrack_docs('articles', drop_index => true)stops syncing and deletes the Pinecone index.
Search an index directly
To work with a Pinecone index for full-text search that isn’t tied to a table, usepinecone.create_doc_index(), pinecone.upsert_docs(), and pinecone.search_doc_index():
SQL
SQL