API
vecs
is a python client for managing and querying vector stores in PostgreSQL with the pgvector extension. This guide will help you get started with using vecs.
If you don't have a Postgres database with the pgvector ready, see hosting for easy options.
Installation
Requires:
- Python 3.7+
You can install vecs using pip:
_10pip install vecs
Usage
Connecting
Before you can interact with vecs, create the client to communicate with Postgres. If you haven't started a Postgres instance yet, see hosting.
_10import vecs_10_10DB_CONNECTION = "postgresql://<user>:<password>@<host>:<port>/<db_name>"_10_10# create vector store client_10vx = vecs.create_client(DB_CONNECTION)
Get or Create a Collection
You can get a collection (or create if it doesn't exist), specifying the collection's name and the number of dimensions for the vectors you intend to store.
_10docs = vx.get_or_create_collection(name="docs", dimension=3)
Upserting vectors
vecs
combines the concepts of "insert" and "update" into "upsert". Upserting records adds them to the collection if the id
is not present, or updates the existing record if the id
does exist.
_15# add records to the collection_15docs.upsert(_15 records=[_15 (_15 "vec0", # the vector's identifier_15 [0.1, 0.2, 0.3], # the vector. list or np.array_15 {"year": 1973} # associated metadata_15 ),_15 (_15 "vec1",_15 [0.7, 0.8, 0.9],_15 {"year": 2012}_15 )_15 ]_15)
Deleting vectors
Deleting records removes them from the collection. To delete records, specify a list of ids
or metadata filters to the delete
method. The ids of the sucessfully deleted records are returned from the method. Note that attempting to delete non-existent records does not raise an error.
_10docs.delete(ids=["vec0", "vec1"])_10# or delete by a metadata filter_10docs.delete(filters={"year": {"$eq": 2012}})
Create an index
Collections can be queried immediately after being created. However, for good throughput, the collection should be indexed after records have been upserted.
Only one index may exist per-collection. By default, creating an index will replace any existing index.
To create an index:
_10docs.create_index()
You may optionally provide a distance measure and index method.
Available options for distance measure
are:
vecs.IndexMeasure.cosine_distance
vecs.IndexMeasure.l2_distance
vecs.IndexMeasure.l1_distance
vecs.IndexMeasure.max_inner_product
which correspond to different methods for comparing query vectors to the vectors in the database.
If you aren't sure which to use, the default of cosine_distance is the most widely compatible with off-the-shelf embedding methods.
Available options for index method
are:
vecs.IndexMethod.auto
vecs.IndexMethod.hnsw
vecs.IndexMethod.ivfflat
Where auto
selects the best available index method, hnsw
uses the HNSW method and ivfflat
uses IVFFlat.
HNSW and IVFFlat indexes both allow for parameterization to control the speed/accuracy tradeoff. vecs provides sane defaults for these parameters. For a greater level of control you can optionally pass an instance of vecs.IndexArgsIVFFlat
or vecs.IndexArgsHNSW
to create_index
's index_arguments
argument. Descriptions of the impact for each parameter are available in the pgvector docs.
When using IVFFlat indexes, the index must be created after the collection has been populated with records. Building an IVFFlat index on an empty collection will result in significantly reduced recall. You can continue upserting new documents after the index has been created, but should rebuild the index if the size of the collection more than doubles since the last index operation.
HNSW indexes can be created immediately after the collection without populating records.
To manually specify method
, measure
, and index_arguments
add them as arguments to create_index
for example:
_10docs.create_index(_10 method=IndexMethod.hnsw,_10 measure=IndexMeasure.cosine_distance,_10 index_arguments=IndexArgsHNSW(m=8),_10)
The time required to create an index grows with the number of records and size of vectors. For a few thousand records expect sub-minute a response in under a minute. It may take a few minutes for larger collections.
Query
Given a collection docs
with several records:
Basic
The simplest form of search is to provide a query vector.
Indexes are essential for good performance. See
for more info.
If you do not create an index, every query will return a warning
_10query does not have a covering index for cosine_similarity. See Collection.create_index
that incldues the IndexMeasure
you should index.
_10docs.query(_10 data=[0.4,0.5,0.6], # required_10 limit=5, # number of records to return_10 filters={}, # metadata filters_10 measure="cosine_distance", # distance measure to use_10 include_value=False, # should distance measure values be returned?_10 include_metadata=False, # should record metadata be returned?_10)
Which returns a list of vector record ids
.
Metadata Filtering
The metadata that is associated with each record can also be filtered during a query.
As an example, {"year": {"$eq": 2005}}
filters a year
metadata key to be equal to 2005
In context:
_10docs.query(_10 data=[0.4,0.5,0.6],_10 filters={"year": {"$eq": 2012}}, # metadata filters_10)
For a complete reference, see the metadata guide.
Disconnect
When you're done with a collection, be sure to disconnect the client from the database.
_10vx.disconnect()
alternatively, use the client as a context manager and it will automatically close the connection on exit.
_10import vecs_10_10DB_CONNECTION = "postgresql://<user>:<password>@<host>:<port>/<db_name>"_10_10# create vector store client_10with vecs.create_client(DB_CONNECTION) as vx:_10 # do some work here_10 pass_10_10# connections are now closed
Adapters
Adapters are an optional feature to transform data before adding to or querying from a collection. Adapters make it possible to interact with a collection using only your project's native data type (eg. just raw text), rather than manually handling vectors.
For a complete list of available adapters, see built-in adapters.
As an example, we'll create a collection with an adapter that chunks text into paragraphs and converts each chunk into an embedding vector using the all-MiniLM-L6-v2
model.
First, install vecs
with optional dependencies for text embeddings:
_10pip install "vecs[text_embedding]"
Then create a collection with an adapter to chunk text into paragraphs and embed each paragraph using the all-MiniLM-L6-v2
384 dimensional text embedding model.
_16import vecs_16from vecs.adapter import Adapter, ParagraphChunker, TextEmbedding_16_16# create vector store client_16vx = vecs.Client("postgresql://<user>:<password>@<host>:<port>/<db_name>")_16_16# create a collection with an adapter_16docs = vx.get_or_create_collection(_16 name="docs",_16 adapter=Adapter(_16 [_16 ParagraphChunker(skip_during_query=True),_16 TextEmbedding(model='all-MiniLM-L6-v2'),_16 ]_16 )_16)
With the adapter registered against the collection, we can upsert records into the collection passing in text rather than vectors.
_15# add records to the collection using text as the media type_15docs.upsert(_15 records=[_15 (_15 "vec0",_15 "four score and ....", # <- note that we can now pass text here_15 {"year": 1973}_15 ),_15 (_15 "vec1",_15 "hello, world!",_15 {"year": "2012"}_15 )_15 ]_15)
Similarly, we can query the collection using text.
_10_10# search by text_10docs.query(data="foo bar")
Deprecated
Create collection
Deprecated: use
You can create a collection to store vectors specifying the collections name and the number of dimensions in the vectors you intend to store.
_10docs = vx.create_collection(name="docs", dimension=3)
Get an existing collection
Deprecated: use
To access a previously created collection, use get_collection
to retrieve it by name
_10docs = vx.get_collection(name="docs")