Compare commits

..

16 Commits

Author SHA1 Message Date
Andrew Kane
7132c9b111 Use fabs [skip ci] 2023-05-05 18:34:15 -07:00
Andrew Kane
878e1e6c3a Added query-aware dynamic pruning [skip ci] 2023-05-05 18:13:56 -07:00
Andrew Kane
d885e2bcfa Added FAQ about results [skip ci] 2023-05-02 10:17:31 -07:00
Andrew Kane
a445355a48 Added Heroku Postgres link [skip ci] 2023-04-26 12:48:55 -07:00
Andrew Kane
d5b17a3624 Fixed installation error with Postgres 12.0-12.2 - fixes #101 2023-04-25 09:36:21 -07:00
Andrew Kane
6383078029 Updated readme [skip ci] 2023-04-22 17:07:10 -07:00
Andrew Kane
5146c7cc57 Added guidance for probes to readme [skip ci] 2023-04-20 12:32:49 -07:00
Andrew Kane
ac63f9858b Updated readme [skip ci] 2023-04-14 12:35:59 -07:00
Andrew Kane
76a4166857 Added link to pgvector-crystal [skip ci] 2023-04-13 21:10:18 -07:00
Andrew Kane
31fb6963a3 Now available on Render [skip ci] 2023-04-13 13:20:06 -07:00
Andrew Kane
18c06cb9b1 Added link to pgvector-swift [skip ci] 2023-04-11 21:11:46 -07:00
Andrew Kane
f858796c64 Added link to pgvector-haskell [skip ci] 2023-04-11 12:53:00 -07:00
Andrew Kane
f32f695844 Improved notice [skip ci] 2023-04-10 21:31:33 -07:00
Andrew Kane
1b013a94f7 Added notice when index created with little data [skip ci] 2023-04-10 21:28:24 -07:00
Andrew Kane
00148dfa1f Improved indexing docs [skip ci] 2023-04-10 21:12:25 -07:00
Andrew Kane
67fc791d95 Improved indexing docs [skip ci] 2023-04-10 21:04:46 -07:00
7 changed files with 49 additions and 26 deletions

View File

@@ -1,3 +1,8 @@
## 0.4.2 (unreleased)
- Added notice when index created with little data
- Fixed installation error with Postgres 12.0-12.2
## 0.4.1 (2023-03-21) ## 0.4.1 (2023-03-21)
- Improved performance of cosine distance - Improved performance of cosine distance

View File

@@ -2,7 +2,11 @@
Open-source vector similarity search for Postgres Open-source vector similarity search for Postgres
Supports exact and approximate nearest neighbor search for L2 distance, inner product, and cosine distance Supports
- exact and approximate nearest neighbor search
- L2 distance, inner product, and cosine distance
- any [language](#languages) with a Postgres client
[![Build Status](https://github.com/pgvector/pgvector/workflows/build/badge.svg?branch=master)](https://github.com/pgvector/pgvector/actions) [![Build Status](https://github.com/pgvector/pgvector/workflows/build/badge.svg?branch=master)](https://github.com/pgvector/pgvector/actions)
@@ -36,7 +40,7 @@ Create a vector column with 3 dimensions
CREATE TABLE items (id bigserial PRIMARY KEY, embedding vector(3)); CREATE TABLE items (id bigserial PRIMARY KEY, embedding vector(3));
``` ```
Insert values Insert vectors
```sql ```sql
INSERT INTO items (embedding) VALUES ('[1,2,3]'), ('[4,5,6]'); INSERT INTO items (embedding) VALUES ('[1,2,3]'), ('[4,5,6]');
@@ -124,7 +128,7 @@ SELECT embedding <-> '[3,1,2]' AS distance FROM items;
For inner product, multiply by -1 (since `<#>` returns the negative inner product) For inner product, multiply by -1 (since `<#>` returns the negative inner product)
```sql ```sql
SELECT -1 * (embedding <#> '[3,1,2]') AS inner_product FROM items; SELECT (embedding <#> '[3,1,2]') * -1 AS inner_product FROM items;
``` ```
For cosine similarity, use 1 - cosine distance For cosine similarity, use 1 - cosine distance
@@ -133,7 +137,7 @@ For cosine similarity, use 1 - cosine distance
SELECT 1 - (embedding <=> '[3,1,2]') AS cosine_similarity FROM items; SELECT 1 - (embedding <=> '[3,1,2]') AS cosine_similarity FROM items;
``` ```
#### Averaging #### Aggregates
Average vectors Average vectors
@@ -153,15 +157,11 @@ By default, pgvector performs exact nearest neighbor search, which provides perf
You can add an index to use approximate nearest neighbor search, which trades some recall for performance. Unlike typical indexes, you will see different results for queries after adding an approximate index. You can add an index to use approximate nearest neighbor search, which trades some recall for performance. Unlike typical indexes, you will see different results for queries after adding an approximate index.
Two keys to achieving good recall are: Three keys to achieving good recall are:
1. Create the index *after* the table has some data 1. Create the index *after* the table has some data
2. Choose an appropriate number of lists (lower is better for recall, higher is better for speed) 2. Choose an appropriate number of lists - a good place to start is `rows / 1000` for up to 1M rows and `sqrt(rows)` for over 1M rows
3. When querying, specify an appropriate number of [probes](#query-options) (higher is better for recall, lower is better for speed) - a good place to start is `lists / 10` for up to 1M rows and `sqrt(lists)` for over 1M rows
A good place to start is:
- `rows / 1000` for up to 1M rows
- `sqrt(rows)` for over 1M rows
Add an index for each distance function you want to use. Add an index for each distance function you want to use.
@@ -190,7 +190,7 @@ Vectors with up to 2,000 dimensions can be indexed.
Specify the number of probes (1 by default) Specify the number of probes (1 by default)
```sql ```sql
SET ivfflat.probes = 1; SET ivfflat.probes = 10;
``` ```
A higher value provides better recall at the cost of speed, and it can be set to the number of lists for exact nearest neighbor search (at which point the planner wont use the index) A higher value provides better recall at the cost of speed, and it can be set to the number of lists for exact nearest neighbor search (at which point the planner wont use the index)
@@ -199,7 +199,7 @@ Use `SET LOCAL` inside a transaction to set it for a single query
```sql ```sql
BEGIN; BEGIN;
SET LOCAL ivfflat.probes = 1; SET LOCAL ivfflat.probes = 10;
SELECT ... SELECT ...
COMMIT; COMMIT;
``` ```
@@ -279,8 +279,10 @@ Language | Libraries / Examples
--- | --- --- | ---
C++ | [pgvector-cpp](https://github.com/pgvector/pgvector-cpp) C++ | [pgvector-cpp](https://github.com/pgvector/pgvector-cpp)
C# | [pgvector-dotnet](https://github.com/pgvector/pgvector-dotnet) C# | [pgvector-dotnet](https://github.com/pgvector/pgvector-dotnet)
Crystal | [pgvector-crystal](https://github.com/pgvector/pgvector-crystal)
Elixir | [pgvector-elixir](https://github.com/pgvector/pgvector-elixir) Elixir | [pgvector-elixir](https://github.com/pgvector/pgvector-elixir)
Go | [pgvector-go](https://github.com/pgvector/pgvector-go) Go | [pgvector-go](https://github.com/pgvector/pgvector-go)
Haskell | [pgvector-haskell](https://github.com/pgvector/pgvector-haskell)
Java, Scala | [pgvector-java](https://github.com/pgvector/pgvector-java) Java, Scala | [pgvector-java](https://github.com/pgvector/pgvector-java)
Julia | [pgvector-julia](https://github.com/pgvector/pgvector-julia) Julia | [pgvector-julia](https://github.com/pgvector/pgvector-julia)
Lua | [pgvector-lua](https://github.com/pgvector/pgvector-lua) Lua | [pgvector-lua](https://github.com/pgvector/pgvector-lua)
@@ -291,6 +293,7 @@ Python | [pgvector-python](https://github.com/pgvector/pgvector-python)
R | [pgvector-r](https://github.com/pgvector/pgvector-r) R | [pgvector-r](https://github.com/pgvector/pgvector-r)
Ruby | [pgvector-ruby](https://github.com/pgvector/pgvector-ruby), [Neighbor](https://github.com/ankane/neighbor) Ruby | [pgvector-ruby](https://github.com/pgvector/pgvector-ruby), [Neighbor](https://github.com/ankane/neighbor)
Rust | [pgvector-rust](https://github.com/pgvector/pgvector-rust) Rust | [pgvector-rust](https://github.com/pgvector/pgvector-rust)
Swift | [pgvector-swift](https://github.com/pgvector/pgvector-swift)
## Frequently Asked Questions ## Frequently Asked Questions
@@ -309,6 +312,10 @@ Two things you can try are:
1. use dimensionality reduction 1. use dimensionality reduction
2. compile Postgres with a larger block size (`./configure --with-blocksize=32`) and edit the limit in `src/ivfflat.h` 2. compile Postgres with a larger block size (`./configure --with-blocksize=32`) and edit the limit in `src/ivfflat.h`
#### Why am I seeing less results after adding an index?
The index was likely created with too little data for the number of lists. Drop the index until the table has more data.
## Reference ## Reference
### Vector Type ### Vector Type
@@ -445,7 +452,7 @@ To request a new extension on other providers:
- Google Cloud SQL - vote or comment on [this page](https://issuetracker.google.com/issues/265172065) - Google Cloud SQL - vote or comment on [this page](https://issuetracker.google.com/issues/265172065)
- Azure Database - vote or comment on [this page](https://feedback.azure.com/d365community/idea/7b423322-6189-ed11-a81b-000d3ae49307) - Azure Database - vote or comment on [this page](https://feedback.azure.com/d365community/idea/7b423322-6189-ed11-a81b-000d3ae49307)
- DigitalOcean Managed Databases - vote or comment on [this page](https://ideas.digitalocean.com/app-framework-services/p/pgvector-extension-for-postgresql) - DigitalOcean Managed Databases - vote or comment on [this page](https://ideas.digitalocean.com/app-framework-services/p/pgvector-extension-for-postgresql)
- Render - vote or comment on [this page](https://feedback.render.com/features/p/add-pgvector-extension-to-postgresql) - Heroku Postgres - vote or comment on [this page](https://github.com/heroku/roadmap/issues/156)
## Upgrading ## Upgrading

View File

@@ -431,8 +431,18 @@ ComputeCenters(IvfflatBuildState * buildstate)
/* TODO Ensure within maintenance_work_mem */ /* TODO Ensure within maintenance_work_mem */
buildstate->samples = VectorArrayInit(numSamples, buildstate->dimensions); buildstate->samples = VectorArrayInit(numSamples, buildstate->dimensions);
if (buildstate->heap != NULL) if (buildstate->heap != NULL)
{
SampleRows(buildstate); SampleRows(buildstate);
if (buildstate->samples->length < buildstate->lists)
{
ereport(NOTICE,
(errmsg("ivfflat index created with little data"),
errdetail("this will cause poor recall"),
errhint("drop the index until the table has more data")));
}
}
/* Calculate centers */ /* Calculate centers */
IvfflatBench("k-means", IvfflatKmeans(buildstate->index, buildstate->samples, buildstate->centers)); IvfflatBench("k-means", IvfflatKmeans(buildstate->index, buildstate->samples, buildstate->centers));

View File

@@ -13,7 +13,6 @@
#endif #endif
int ivfflat_probes; int ivfflat_probes;
int ivfflat_bound;
static relopt_kind ivfflat_relopt_kind; static relopt_kind ivfflat_relopt_kind;
/* /*
@@ -33,10 +32,6 @@ _PG_init(void)
DefineCustomIntVariable("ivfflat.probes", "Sets the number of probes", DefineCustomIntVariable("ivfflat.probes", "Sets the number of probes",
"Valid range is 1..lists.", &ivfflat_probes, "Valid range is 1..lists.", &ivfflat_probes,
1, 1, IVFFLAT_MAX_LISTS, PGC_USERSET, 0, NULL, NULL, NULL); 1, 1, IVFFLAT_MAX_LISTS, PGC_USERSET, 0, NULL, NULL, NULL);
DefineCustomIntVariable("ivfflat.bound", "Sets the max results from index (experimental)",
NULL, &ivfflat_bound,
0, 0, INT_MAX, PGC_USERSET, 0, NULL, NULL, NULL);
} }
/* /*

View File

@@ -78,7 +78,6 @@
/* Variables */ /* Variables */
extern int ivfflat_probes; extern int ivfflat_probes;
extern int ivfflat_bound;
/* Exported functions */ /* Exported functions */
PGDLLEXPORT void _PG_init(void); PGDLLEXPORT void _PG_init(void);
@@ -207,6 +206,7 @@ typedef struct IvfflatScanOpaqueData
/* Lists */ /* Lists */
pairingheap *listQueue; pairingheap *listQueue;
double minDistance;
IvfflatScanList lists[FLEXIBLE_ARRAY_MEMBER]; /* must come last */ IvfflatScanList lists[FLEXIBLE_ARRAY_MEMBER]; /* must come last */
} IvfflatScanOpaqueData; } IvfflatScanOpaqueData;

View File

@@ -60,6 +60,9 @@ GetScanLists(IndexScanDesc scan, Datum value)
/* Use procinfo from the index instead of scan key for performance */ /* Use procinfo from the index instead of scan key for performance */
distance = DatumGetFloat8(FunctionCall2Coll(so->procinfo, so->collation, PointerGetDatum(&list->center), value)); distance = DatumGetFloat8(FunctionCall2Coll(so->procinfo, so->collation, PointerGetDatum(&list->center), value));
if (distance < so->minDistance)
so->minDistance = distance;
if (listCount < so->probes) if (listCount < so->probes)
{ {
scanlist = &so->lists[listCount]; scanlist = &so->lists[listCount];
@@ -126,14 +129,16 @@ GetScanItems(IndexScanDesc scan, Datum value)
*/ */
BufferAccessStrategy bas = GetAccessStrategy(BAS_BULKREAD); BufferAccessStrategy bas = GetAccessStrategy(BAS_BULKREAD);
/* Set the max number of results */
if (ivfflat_bound > 0)
tuplesort_set_bound(so->sortstate, ivfflat_bound);
/* Search closest probes lists */ /* Search closest probes lists */
while (!pairingheap_is_empty(so->listQueue)) while (!pairingheap_is_empty(so->listQueue))
{ {
searchPage = ((IvfflatScanList *) pairingheap_remove_first(so->listQueue))->startPage; IvfflatScanList *scanlist = (IvfflatScanList *) pairingheap_remove_first(so->listQueue);
/* Query-aware dynamic pruning */
if (fabs(scanlist->distance) > 1.5 * fabs(so->minDistance))
continue;
searchPage = scanlist->startPage;
/* Search all entry pages for list */ /* Search all entry pages for list */
while (BlockNumberIsValid(searchPage)) while (BlockNumberIsValid(searchPage))
@@ -256,6 +261,7 @@ ivfflatrescan(IndexScanDesc scan, ScanKey keys, int nkeys, ScanKey orderbys, int
so->first = true; so->first = true;
pairingheap_reset(so->listQueue); pairingheap_reset(so->listQueue);
so->minDistance = DBL_MAX;
if (keys && scan->numberOfKeys > 0) if (keys && scan->numberOfKeys > 0)
memmove(scan->keyData, keys, scan->numberOfKeys * sizeof(ScanKeyData)); memmove(scan->keyData, keys, scan->numberOfKeys * sizeof(ScanKeyData));

View File

@@ -100,7 +100,7 @@ CheckStateArray(ArrayType *statearray, const char *caller)
return (float8 *) ARR_DATA_PTR(statearray); return (float8 *) ARR_DATA_PTR(statearray);
} }
#if PG_VERSION_NUM < 120000 #if PG_VERSION_NUM < 120003
static pg_noinline void static pg_noinline void
float_overflow_error(void) float_overflow_error(void)
{ {