pgvectorscale
App Listed
Postgres extension for vector search (DiskANN), complements pgvector for performance and scale. Postgres OSS licensed.
- Downloads
- 33,328
- Dependents
- Not applicable
- Stars
- 3,161
- Forks
- 159
- Contributors
- 15
- Commits
- 214
- Latest release
About
pgvectorscale builds on pgvector with higher performance embedding search and cost-efficient storage for AI applications.
[!NOTE] A new approach to vector search: We're building pg_vectorsearch, a next-generation PostgreSQL vector index that uses partitioning and compression instead of graph-based indexing to deliver faster searches with less memory. It's available in early alpha and isn't yet production-ready. Learn more.
pgvectorscale complements pgvector, the open-source vector data extension for PostgreSQL, and introduces the following key innovations for pgvector data:
- A new index type called StreamingDiskANN, inspired by the DiskANN algorithm, based on research from Microsoft.
- Statistical Binary Quantization: developed by Timescale researchers, This compression method improves on standard Binary Quantization.
- Label-based filtered vector search: based on Microsoft's Filtered DiskANN research, this allows you to combine vector similarity search with label filtering for more precise and efficient results.
Read the full README
On a benchmark dataset of 50 million Cohere embeddings with 768 dimensions
each, PostgreSQL with pgvector and pgvectorscale achieves 28x lower p95
latency and 16x higher query throughput compared to Pinecone's storage
optimized (s1) index for approximate nearest neighbor queries at 99% recall,
all at 75% less cost when self-hosted on AWS EC2.
To learn more about the performance impact of pgvectorscale, and details about benchmark methodology and results, see the pgvector vs Pinecone comparison blog post.
In contrast to pgvector, which is written in C, pgvectorscale is developed in Rust using the PGRX framework, offering the PostgreSQL community a new avenue for contributing to vector support.
Application developers or DBAs can use pgvectorscale with their PostgreSQL databases.
- Install pgvectorscale
- Get started using pgvectorscale
If you want to contribute to this extension, see how to build pgvectorscale from source in a developer environment and our testing guide.
For production vector workloads, get private beta access to vector-optimized databases with pgvector and pgvectorscale on Timescale. Sign up here for priority access.
Installation
The fastest ways to run PostgreSQL with pgvectorscale are:
- Using a pre-built Docker container
- Installing from source
- Enable pgvectorscale in a Timescale Cloud service
Using a pre-built Docker container
Connect to your database:
psql -d "postgres://<username>:<password>@<host>:<port>/<database-name>"Create the pgvectorscale extension:
CREATE EXTENSION IF NOT EXISTS vectorscale CASCADE;The
CASCADEautomatically installspgvector.
Installing from source
You can install pgvectorscale from source and install it in an existing PostgreSQL server
[!WARNING] Building pgvectorscale on macOS X86 (Intel) machines is currently not supported due to an open issue. As alternatives, you can:
- Use an ARM-based Mac.
- Build using Linux.
- Use our pre-built Docker containers.
We welcome community contributions to resolve this limitation. If you're interested in helping, please check the issue for details.
Compile and install the extension
# install rust curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh # download pgvectorscale cd /tmp git clone --branch <version> https://github.com/timescale/pgvectorscale cd pgvectorscale/pgvectorscale # install cargo-pgrx with the same version as pgrx cargo install --locked cargo-pgrx --version $(cargo metadata --format-version 1 | jq -r '.packages[] | select(.name == "pgrx") | .version') cargo pgrx init --pg18 pg_config # build and install pgvectorscale cargo pgrx install --releaseYou can also take a look at our documentation for extension developers for more complete instructions.
Connect to your database:
psql -d "postgres://<username>:<password>@<host>:<port>/<database-name>"Ensure the pgvector extension is available:
SELECT * FROM pg_available_extensions WHERE name = 'vector';If pgvector is not available, install it using the pgvector installation instructions.
Create the pgvectorscale extension:
CREATE EXTENSION IF NOT EXISTS vectorscale CASCADE;The
CASCADEautomatically installspgvector.
Enable pgvectorscale in a Timescale Cloud service
Note: the instructions below are for Timescale's standard compute instance. For production vector workloads, we're offering private beta access to vector-optimized databases with pgvector and pgvectorscale on Timescale. Sign up here for priority access.
To enable pgvectorscale:
Create a new Timescale Service.
If you want to use an existing service, pgvectorscale is added as an available extension on the first maintenance window after the pgvectorscale release date.
Connect to your Timescale service:
psql -d "postgres://<username>:<password>@<host>:<port>/<database-name>"Create the pgvectorscale extension:
CREATE EXTENSION IF NOT EXISTS vectorscale CASCADE;The
CASCADEautomatically installspgvector.
Get started with pgvectorscale
Create a table with an embedding column. For example:
CREATE TABLE IF NOT EXISTS document_embedding ( id BIGINT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, metadata JSONB, contents TEXT, embedding VECTOR(1536) )Populate the table.
For more information, see the pgvector instructions and list of clients.
Create a StreamingDiskANN index on the embedding column:
CREATE INDEX document_embedding_idx ON document_embedding USING diskann (embedding vector_cosine_ops);Find the 10 closest embeddings using the index.
SELECT * FROM document_embedding ORDER BY embedding <=> $1 LIMIT 10;Note: pgvectorscale currently supports: cosine distance (
<=>) queries, for indices created withvector_cosine_ops; L2 distance (<->) queries, for indices created withvector_l2_ops; and inner product (<#>) queries, for indices created withvector_ip_ops. This is the same syntax used bypgvector. If you would like additional distance types, create an issue. (Note: inner product indices are not compatible with plain storage.)
Filtered Vector Search
pgvectorscale supports combining vector similarity search with metadata filtering. There are two basic kinds of filtering, which can be combined in a single query:
- Label-based filtering with the diskann index: This provides optimized performance for filtering by labels.
- Arbitrary WHERE clause filtering: This uses post-filtering after the vector search.
The label-based filtering implementation is based on the Filtered DiskANN approach developed by Microsoft researchers, which enables efficient filtered vector search while maintaining high recall.
The post-filtering implementation, while slower, is streaming and correct, ensuring accurate results without requiring the entire result set to be loaded into memory.
Label-based Filtering with diskann
For optimal performance with label filtering, you must specify the label column directly in the index creation:
Create a table with an embedding column and a labels array:
CREATE TABLE documents ( id SERIAL PRIMARY KEY, embedding VECTOR(1536), labels SMALLINT[], -- Array of category labels status TEXT, created_at TIMESTAMPTZ );Create a StreamingDiskANN index on the embedding column, including the labels column:
CREATE INDEX ON documents USING diskann (embedding vector_cosine_ops, labels);
Note: Label values must be within the PostgreSQL
smallintrange (-32768 to 32767). Usingsmallint[]for labels ensures that PostgreSQL's type system will automatically enforce these bounds.pgvectorscale includes an implementation of the
&&overlap operator forsmallint[]arrays, which is used for efficient label-based filtering.
Perform label-filtered vector searches using the
&&operator (array overlap):-- Find similar documents with specific labels SELECT * FROM documents WHERE labels && ARRAY[1, 3] -- Documents with label 1 OR 3 ORDER BY embedding <=> '[...]' LIMIT 10;The index directly supports this type of filtering, providing significantly lower latency results compared to post-filtering.
Giving Semantic Meaning to Labels
While the labels must be stored as integers in the array for the index to work efficiently, you can give them semantic meaning by relating them to a separate labels table:
Create a labels table with meaningful descriptions:
CREATE TABLE label_definitions ( id INTEGER PRIMARY KEY, name TEXT, description TEXT, attributes JSONB -- Can store additional metadata about the label ); -- Insert some label definitions INSERT INTO label_definitions (id, name, description, attributes) VALUES (1, 'science', 'Scientific content', '{"domain": "academic", "confidence": 0.95}'), (2, 'technology', 'Technology-related content', '{"domain": "technical", "confidence": 0.92}'), (3, 'business', 'Business and finance content', '{"domain": "commercial", "confidence": 0.88}');When inserting documents, use the appropriate label IDs:
-- Insert a document with science and technology labels INSERT INTO documents (embedding, labels) VALUES ('[...]', ARRAY[1, 2]);When querying, you can join with the labels table to work with meaningful names:
-- Find similar science documents and include label information SELECT d.*, array_agg(l.name) as label_names FROM documents d JOIN label_definitions l ON l.id = ANY(d.labels) WHERE d.labels && ARRAY[1] -- Science label GROUP BY d.id, d.embedding, d.labels, d.status, d.created_at ORDER BY d.embedding <=> '[...]' LIMIT 10;You can also convert between label names and IDs when filtering:
-- Find documents with specific label names SELECT d.* FROM documents d WHERE d.labels && ( SELECT array_agg(id) FROM label_definitions WHERE name IN ('science', 'business') ) ORDER BY d.embedding <=> '[...]' LIMIT 10;
This approach gives you the performance benefits of integer-based label filtering while still allowing you to work with semantically meaningful labels in your application.
Arbitrary WHERE Clause Filtering
You can also use any PostgreSQL WHERE clause with vector search, but these conditions will be applied as post-filtering:
-- Find similar documents with specific status and date range
SELECT * FROM documents
WHERE status = 'active' AND created_at > '2024-01-01'
ORDER BY embedding <=> '[...]'
LIMIT 10;
For these arbitrary conditions, the vector search happens first, and then the WHERE conditions are applied to the results. For best performance with frequently used filters, consider using the label-based approach described above.
Tuning
The StreamingDiskANN index comes with smart defaults but also the ability to customize its behavior. There are two types of parameters: index build-time parameters that are specified when an index is created and query-time parameters that can be tuned when querying an index.
We suggest setting the index build-time paramers for major changes to index operations while query-time parameters can be used to tune the accuracy/performance tradeoff for individual queries.
We expect most people to tune the query-time parameters (if any) and leave the index build time parameters set to default.
StreamingDiskANN index build-time parameters
The StreamingDiskANN index build process can be memory-intensive. You may need to increase the maintenance_work_mem parameter to improve build performance. For example:
SET maintenance_work_mem = '2GB';
This parameter controls the maximum amount of memory to be used by maintenance operations, including index builds. The default value is typically 64MB, which may be too low for building StreamingDiskANN indexes on large datasets.
These parameters can be set when an index is created.
| Parameter name | Description | Default value |
|---|---|---|
storage_layout | memory_optimized which uses SBQ to compress vector data or plain which stores data uncompressed | memory_optimized |
num_neighbors | Sets the maximum number of neighbors per node. Higher values increase accuracy but make the graph traversal slower. | 50 |
search_list_size | This is the S parameter used in the greedy search algorithm used during construction. Higher values improve graph quality at the cost of slower index builds. | 100 |
max_alpha | Is the alpha parameter in the algorithm. Higher values improve graph quality at the cost of slower index builds. | 1.2 |
num_dimensions | The number of dimensions to index. By default, all dimensions are indexed. But you can also index less dimensions to make use of Matryoshka embeddings | 0 (all dimensions) |
num_bits_per_dimension | Number of bits used to encode each dimension when using SBQ | 2 for less than 900 dimensions, 1 otherwise |
An example of how to set the num_neighbors parameter is:
CREATE INDEX document_embedding_idx ON document_embedding
USING diskann (embedding) WITH(num_neighbors=50);
An example of creating an index with label-based filtering:
CREATE INDEX document_embedding_idx ON document_embedding
USING diskann (embedding vector_cosine_ops, labels);
Parallel Index Build Parameters
pgvectorscale supports parallel index building to improve performance on large datasets. These parameters control parallel build behavior:
| Parameter name | Description | Default value |
|---|---|---|
diskann.parallel_flush_interval | The fraction of total vectors (0.0-1.0) processed before flushing neighbor cache in parallel builds | 0.1 |
diskann.parallel_initial_start_nodes_count | The number of initial start nodes to process before starting parallel workers | 1024 |
diskann.min_vectors_for_parallel_build | Minimum number of vectors required to enable parallel building | 65536 |
diskann.force_parallel_workers | Force a specific number of parallel workers for index builds (-1 for automatic) | -1 |
Parallel building is automatically enabled when:
- The feature is compiled in (enabled by default)
- The table has no labels (label-based filtering not yet supported in parallel)
- Using SBQ compression storage layout
- The number of vectors meets the
min_vectors_for_parallel_buildthreshold
You can set these parameters using SET before creating an index:
-- Enable parallel building for smaller datasets
SET diskann.min_vectors_for_parallel_build = 10000;
-- Use 4 parallel workers regardless of PostgreSQL's automatic determination
SET diskann.force_parallel_workers = 4;
-- Adjust cache flushing frequency for memory usage (flush after processing 5% of vectors)
SET diskann.parallel_flush_interval = 0.05;
CREATE INDEX ON my_table USING diskann (embedding vector_cosine_ops);
StreamingDiskANN query-time parameters
You can also set two parameters to control the accuracy vs. query speed trade-off at query time. We suggest adjusting diskann.query_rescore to fine-tune accuracy.
| Parameter name | Description | Default value |
|---|---|---|
diskann.query_search_list_size | The number of additional candidates considered during the graph search. | 100 |
diskann.query_rescore | The number of elements rescored (0 to disable rescoring) | 50 |
You can set the value by using SET before executing a query. For example:
SET diskann.query_rescore = 400;
Note the SET command applies to the entire session (database connection) from the point of execution. You can use a transaction-local variant using LOCAL which will
be reset after the end of the transaction:
BEGIN;
SET LOCAL diskann.query_search_list_size= 10;
SELECT * FROM document_embedding ORDER BY embedding <=> $1 LIMIT 10
COMMIT;
Null Value Handling
- Null vectors are not indexed
- Null labels are treated as empty arrays
- Null values in label arrays are ignored
ORDER BY vector distance
pgvectorscale's diskann index uses relaxed ordering which allows results to be
slightly out of order by distance. This is analogous to using
iterative scan with relaxed ordering with
pgvector's ivfflat or hnsw indexes.
If you need strict ordering you can use a materialized CTE:
WITH relaxed_results AS MATERIALIZED (
SELECT id, embedding <=> '[1,2,3]' AS distance
FROM items
WHERE category_id = 123
ORDER BY distance
LIMIT 5
) SELECT * FROM relaxed_results ORDER BY distance;
Index on an UNLOGGED table
Creating an index on an UNLOGGED table is currently not supported. Trying will yield the error:
ERROR: ambuildempty: not yet implemented
Get involved
pgvectorscale is still at an early stage. Now is a great time to help shape the direction of this project; we are currently deciding priorities. Have a look at the list of features we're thinking of working on. Feel free to comment, expand the list, or hop on the Discussions forum.
About Timescale
Timescale is a PostgreSQL cloud company. To learn more visit the timescale.com.
Timescale Cloud is a high-performance, developer focused, cloud platform that provides PostgreSQL services for the most demanding AI, time-series, analytics, and event workloads. Timescale Cloud is ideal for production applications and provides high availability, streaming backups, upgrades over time, roles and permissions, and great security.
Compatibility
- License
- PostgreSQL
- Crate facts
- Not measured
- Runs on, Native code and Python bindings read crates on crates.io. This entry lists no crate.
Compare with similar projects
Order: strongest tier first, then most downloads, then name, with unmeasured values last. A library counts downloads over 30 days. An app counts binary downloads per release. An app with none counts its crate downloads over 30 days. ML Rust runs this catalogue, so its closest project always gets a row.
| Project | Downloads | Dependents | Stars | Latest release | Runs on (GPU) | Native code | License |
|---|---|---|---|---|---|---|---|
|
Postgres extension for vector search (DiskANN), complements pgvector for performance and scale. Postgres OSS licensed. |
33,328 binary downloads per release | Not applicable | 3,161 | Not measured | Not measured | PostgreSQL | |
|
kiddo Featured
A high-performance, flexible, ergonomic k-d tree library. Ideal for geo- and astro- nearest-neighbour and k-nearest-neighbor queries |
481,504 downloads over 30 days | 56 | 179 | 6.3.0 | GPU support unknown | Build uses a native toolchain: cc (feature cpp_competitors) | Apache-2.0 OR MIT |
|
Qdrant - High-performance, massive-scale Vector Database and Vector Search Engine for the next generation of AI. Also available in the cloud https://cloud.qdrant.io/ |
34,017 binary downloads per release | Not applicable | 35,478 | Not measured | Not measured | Apache-2.0 | |
|
splintr Listed
Tokenizer for Rust and Python: pure Rust by default, with 18 bundled vocabularies and four backends covering BPE, SentencePiece and WordPiece. Its output matches Hugging Face tokenizers id for id on 14 model families, including GPT-2, Qwen, Whisper, T5 and BERT. Python bindings |
8,204 downloads over 30 days | 7 | 63 | 0.22.2 | GPU support unknown | Binds native code: pcre2-sys (feature pcre2) | MIT |
|
hyperspace-db Listed
[H] HyperspaceDB is a high-performance, vector database. It features 1-bit quantization, async replication, and native support for hierarchical datasets (Lorentz, Poincaré ball & Hybrid models). |
96 downloads over 30 days | 3 | 163 | 4.2.0 | GPU support unknown | Binds native code: ort-sys (feature local-onnx) | MIT |
|
edgevec Listed
High-performance embedded vector database for Browser, Node, and Edge |
14 downloads over 30 days | 1 | 100 | 0.9.0 | GPU support unknown | No native code found | Apache-2.0 OR MIT |
|
In-memory vector store with efficient read and write performance for semantic caching and retrieval system. Redis for Semantic Caching. |
0 binary downloads per release | Not applicable | 376 | Not measured | Not measured | Apache-2.0 |
Why ListedNo archive, New or Featured rule places it, so it is Listed.
What it lacks for Featured
- The check holds no value for Homebrew installs on request over 365 days, Repology repository families, Docker pull growth over 90 days. A gate that lacks its signal does not pass.
What else the check found
- The last activity is on 14 Oct 2026, 26 days ago. The rules expect an update within 365 days. Dormancy rule
Not measured: First release, Homebrew installs on request over 365 days, Repology repository families, Docker pull growth over 90 days.
Exact rules: Listed, Featured app. The check on applied thresholds version 9.
Every rule outcome, as the check records it
expected-interval: app: E = 365 daysdormancy: last activity 2026-10-14, 26 days ago, E = 365: activefeatured: not featured: unknown homebrew_on_request_365d, repology_families, docker_pull_growth_90dlisted: listed: no archive, new or featured rule applies
Measurements and sources
Each signal row shows the raw value, its source and the fetch date. The Sources list gathers every distinct source on this page. Movement needs more history.
Sources4 sources
- Description: the GitHub repository description
- GitHub releases, fetched
- GitHub repository, fetched
- GitHub contributors, fetched
Adoption33,328 binary downloads per release4 not measured
| Signal | Value | Source | Fetched | Counts |
|---|---|---|---|---|
| Active dependents | Not measured | Yes | ||
| New dependents (365 days) | Not measured | Yes | ||
| Peak active dependents | Not measured | Yes | ||
| Active dependents as a share of the peak | Not measured | Yes | ||
| Binary downloads per release | 33328 | GitHub releases | Yes |
MaintenanceLatest human commit: 26 days ago7 not measured
| Signal | Value | Source | Fetched | Counts |
|---|---|---|---|---|
| Latest human commit | 2026-10-14 | GitHub repository | Yes | |
| Latest release | 2026-09-04 | GitHub releases | Yes | |
| First release | Not measured | Yes | ||
| Latest version | Not measured | Yes | ||
| Days the oldest outside issue waits for a reply | 125 | GitHub repository | Yes | |
| Maintainer replies over 365 days | 6 | GitHub repository | Yes | |
| Repository archived | false | GitHub repository | Yes | |
| Repository gone | false | GitHub repository | Yes | |
| Release commit or manifest in the repo | Not measured | Yes | ||
| Published docs | Not measured | Yes | ||
| Latest release fails cargo check | Not measured | Yes | ||
| Build check date | Not measured | Yes | ||
| Build check toolchain | Not measured | Yes |
SecurityNot measured
| Signal | Value | Source | Fetched | Counts |
|---|---|---|---|---|
| Unpatched vulnerabilities past the grace window | Not measured | Yes | ||
| RustSec unmaintained advisory | Not measured | Yes | ||
| Deprecation notice | Not measured | Yes | ||
| Deprecated requirement | Not measured | Yes |
Distribution99,985 binary downloads over the last 3 releases10 not measured
| Signal | Value | Source | Fetched | Counts |
|---|---|---|---|---|
| Releases | Not measured | Yes | ||
| Downloads over 30 days | Not measured | Yes | ||
| Binary downloads over the last 3 releases | 99985 | GitHub releases | Yes | |
| Homebrew installs on request over 365 days | Not measured | Yes | ||
| Repology repository families | Not measured | Yes | ||
| Docker pull growth over 90 days | Not measured | Yes | ||
| Docker pulls at the growth baseline | Not measured | Yes | ||
| Every version a prerelease | Not measured | Yes | ||
| Every version 0.0.x | Not measured | Yes | ||
| Every version yanked | Not measured | Yes | ||
| Crate deleted from crates.io | Not measured | Yes |
Repository3,161 stars, 159 forks, 15 contributors, 214 commits. Stars alone never qualify a project.1 not measured
| Signal | Value | Source | Fetched | Counts |
|---|---|---|---|---|
| GitHub stars | 3161 | GitHub repository | Yes — one part of the repository route | |
| GitHub forks | 159 | GitHub repository | Yes — one part of the repository route | |
| Contributors with a GitHub account | 15 | GitHub contributors | Yes — one part of the repository route | |
| Commits on the default branch | 214 | GitHub repository | No | |
| Repository's main crate left out of the entry | Not measured | Yes |
Facts that never count
| Signal | Value | Source | Fetched | Counts |
|---|---|---|---|---|
| License | PostgreSQL | GitHub repository | No | |
| Runs on, from listed crates and their direct dependencies | Not measured | No | ||
| What shows each backend | Not measured | No | ||
| Native code, from listed crates and their dependencies | Not measured | No | ||
| Python bindings, from direct dependencies of listed crates | Not measured | No | ||
| Crates that depend on a listed crate directly | Not measured | No |
Corrections and review history
No correction applies to this entry.
Judgments
A judgment enters only through an approved issue and a merged pull request.
No judgment applies to this entry.