Skip to content

Commit

Permalink
doc: search rewrite
Browse files Browse the repository at this point in the history
Signed-off-by: cutecutecat <junyuchen@tensorchord.ai>
  • Loading branch information
cutecutecat committed Feb 6, 2024
1 parent 76c2cf9 commit 60e185f
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 65 deletions.
1 change: 1 addition & 0 deletions .vitepress/config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ export default defineConfig({
items: [
{ text: 'Schema', link: '/reference/schema' },
{ text: 'Indexing Options', link: '/reference/indexing_options' },
{ text: 'Search Options', link: '/reference/search_options' },
]
},
{
Expand Down
32 changes: 32 additions & 0 deletions src/reference/search_options.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Indexing Options

Search options are specified by [PostgreSQL GUC](https://www.postgresql.org/docs/current/config-setting.html).

## Options for `ivf`

These options only work at `ivf` indexing algorithm.
For `ivf` algorithm, refer to the [indexing document](../usage/indexing.html#inverted-file-index-ivf).

| Option | Type | Range | Default | Description |
| ------------------------ | ------- | ---------------- | ------- | ----------------------------------------- |
| vectors.ivf_nprobe | integer | `[1, 1_000_000]` | `10` | Number of lists to scan. |

## Options for `hnsw`

These options only work at `hnsw` indexing algorithm.
For `ivf` algorithm, refer to the [indexing document](../usage/indexing.html#hierarchical-navigable-small-world-graph-hnsw).

| Option | Type | Range | Default | Description |
| ------------------------ | ------- | -------------- | ------- | ----------------------------------------- |
| vectors.hnsw_ef_search | integer | `[1, 65535]` | `100` | Search scope of HNSW. |

## Other Options

Query options for search mode:

These options only work at `hnsw` indexing algorithm.
For search mode, refer to the [search document](../usage/search.html#Search modes).

| Option | Type | Range | Default | Description |
| ------------------------ | ------- | ------------------ | --------- | -------------------------------------- |
| vectors.search_mode | enum | `"basic", "vbase"` | `"vbase"` | Search mode. |
30 changes: 15 additions & 15 deletions src/usage/compatibility.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# `pgvector` compatibility

`pgvecto.rs` is natively compatible with `pgvector` at:
* `CREATE TABLE` commands, for instance, `CREATE TABLE t (val vector(3))`
* `INSERT INTO` commands, for instance, `INSERT INTO t (val) VALUES ('[0.6,0.6,0.6]')`
* `CREATE TABLE` commands, e.g. `CREATE TABLE t (val vector(3))`
* `INSERT INTO` commands, e.g. `INSERT INTO t (val) VALUES ('[0.6,0.6,0.6]')`

`pgvecto.rs` can be configured to be compatible with `pgvector` at:
* Index options, which allows you to create index by `USING hnsw (val vector_ip_ops)`
Expand All @@ -20,32 +20,32 @@ For index `ivfflat` and `hnsw` only the following options are available.

Index options for `ivfflat`:

| Key | Type | Default | Description |
| ----- | ------- | ------- | ------------------------- |
| lists | integer | `100` | Number of cluster units. |
| Key | Type | Range | Default | Description |
| ----- | ------- | ---------------- | ------- | -------------------------------------- |
| nlist | integer | `[1, 1_000_000]` | `100` | Number of cluster units. |

Query options for `ivfflat`:

| Option | Type | Default | Description |
| ---------------- | ------------------------ | ------- | ----------------------------------------- |
| ivfflat.probes | integer (`[1, 1000000]`) | `10` | Number of lists to scan. |
| Option | Type | Range nn | Default | Description |
| -------------- | ------- | ---------------- | ------- | ----------------------------------------- |
| ivfflat.probes | integer | `[1, 1_000_000]` | `10` | Number of lists to scan. |

::: warning
Default value of `ivfflat.probes` is `10` instead of `1` from pgvector.
:::

Index options for `hnsw`:

| key | type | default | description |
| --------------- | ------- | ------- | -------------------------------- |
| m | integer | `16` | Maximum degree of the node. |
| ef_construction | integer | `64` | Search extent in construction. |
| Key | Type | Range | Default | Description |
| --------------- | ------- | ------------ | ------- | -------------------------------------- |
| m | integer | `[4, 128]` | `16` | Maximum degree of the node. |
| ef_construction | integer | `[10, 2000]` | `64` | Search scope in building. |

Query options for `hnsw`:

| Option | Type | Default | Descrcompatibilitymodeiption |
| -------------- | ------------------------ | ------- | ----------------------------------------- |
| hnsw.ef_search | integer (`[1, 65535]`) | `100` | Search scope of HNSW. |
| Option | Type | Range | Default | Description |
| -------------- | ------- | -------------- | ------- | ----------------------------------------- |
| hnsw.ef_search | integer | `[1, 65535]` | `100` | Search scope of HNSW. |

::: warning
Default value for `hnsw.ef_search` is `100` instead of `40` from pgvector.
Expand Down
93 changes: 43 additions & 50 deletions src/usage/search.md
Original file line number Diff line number Diff line change
@@ -1,83 +1,76 @@
# Search

The SQL for searching is very simple. Here is an example of searching the $5$ nearest embedding in table `items`:

Get the nearest 5 neighbors to a vector
```sql
SET vectors.hnsw_ef_search = 64;
SELECT * FROM items ORDER BY embedding <-> '[3,2,1]' LIMIT 5;
```

The vector index will search for `64` nearest rows, and `5` nearest rows is gotten since there is a `LIMIT` clause.
## Operators

## Search modes
These operators are used for distance metrics:

There are two search modes: `basic` and `vbase`.
| Name | Description |
| ---- | -------------------------- |
| <-> | squared Euclidean distance |
| <#> | negative dot product |
| <=> | cosine distance |

### `basic`
For their definitions, see [overview](../getting-started/overview).

`basic` is the default search mode. In this mode, vector indexes behave like a vector search library. It works well if all of your queries is like this:
## Filter

For a given category, get the nearest 10 neighbors to a vector
```sql
SELECT * FROM items ORDER BY embedding <-> '[3,2,1]' LIMIT 5;
SELECT 1 FROM items WHERE category_id = 1 ORDER BY embedding <#> '[0.5,0.5,0.5]' limit 10
```

It's recommended if your do **not** take advantages of

* database transaction
* deletions without `VACUUM`
* WHERE clauses and very complex SQL statements

### `vbase`

`vbase` is another search mode. In this mode, vector indexes behave like a database index. In `vbase` mode, searching results become a stream and every time the database pulls a row, the vector index computes a row to return. It's quite different from an ordinary vector search if you are using a vector search library, such as *faiss*. The latter always wants to know how many results are needed before searching. The original idea comes from [VBASE: Unifying Online Vector Similarity Search and Relational Queries via Relaxed Monotonicity](https://www.usenix.org/conference/osdi23/presentation/zhang-qianxi).

Assuming you are using HNSW algorithm, you may want the following SQL to work:
## Query options

Set `ivf` scan lists to 1 in session:
```sql
SET vectors.hnsw_ef_search = 64;
SELECT * FROM items ORDER BY embedding <-> '[3,2,1]' WHERE id % 2 = 0 LIMIT 64;
SET vectors.ivf_nprobe=1;
```

In `basic` mode, you may only get `32` rows because the HNSW algorithm does search simply so the filter condition is ignored.

In `vbase` mode, the HNSW algorithm is guaranteed to return rows as many as you need, so you can always get correct behavior if your do take advantages of:

* database transaction
* deletions without `VACUUM`
* `WHERE` clauses and very complex SQL statements
Set `hnsw` search scope to 40 in transaction:
```sql
SET LOCAL vectors.hnsw_ef_search=40;
```

You can enable `vbase` by a SQL statement `SET vectors.search_mode = vbase;`.
For all options, refer to [search options](../reference/search_options.html).

## Prefilter
## Advanced usage

If your queries include a `WHERE` clause, you can set set search mode to `vbase`. It's good and it even works on all conditions. `vbase` is a **postfilter** method: it pulls rows as many as you need, but it scans rows that you may not need. Since some rows will definitely be removed by the `WHERE` clause, we can skip scanning them, which will make the search faster. We call it **prefilter**.
In traditional vector database, sometimes you expect the search to return the exact number of vectors equal to `LIMIT`, but it can't:
```sql
SELECT COUNT(1) FROM (SELECT 1 FROM t WHERE (category_id = 1) ORDER BY val <-> '[1,1,1]' limit 10) t2;
--- returns 1, much less than 10
```
That is why we introduce `vbase` search mode and set it as default.

Prefilter speeds your query in the following condition:
### Search modes

* You create a multicolumn vector index containing a vector column and many payload columns.
* The `WHERE` clause in a query is just simple like `(id % 2 = 0) AND (age > 50)`.
There are two search modes: `vbase` and `basic`.

Prefilter is also used in internal implementation for handling deleted rows in `pgvecto.rs`.
### `vbase`

Prefilter may have a negative impact on precision. Test the precision before using it.
As the default search mode, `vbase` is suitable for most scenarios.
It would return enough vectors for your filter as far as it could.

Prefilter is enabled by default because it almost only works if you create a multicolumn vector index.
For how it works, refer to the thesis [VBASE: Unifying Online Vector Similarity Search and Relational Queries via Relaxed Monotonicity](https://www.usenix.org/conference/osdi23/presentation/zhang-qianxi).

## Options
It's recommended to use `vbase` in these situations:

Search options are specified by PostgreSQL GUC. You can use `SET` command to apply these options in session or `SET LOCAL` command to apply these options in transaction.
* Search with filter or transaction
* Returning sufficient vectors is important
* Tired of tuning query options in `basic` mode

Runtime parameters for planning a query:
### `basic`
`basic` is behaviorally consistent with traditional vector databases.
It will be useful if you want to align other vector databases.

| Option | Type | Range | Default | Description |
| -------------------- | ------- | ------------------ | --------- | ---------------------------------------------------------------------------- |
| vectors.enable_index | boolean | | `on` | Enables or disables the query planner's use of vector index-scan plan types. |
| vectors.search_mode | enum | `"basic", "vbase"` | `"basic"` | Search mode. |
Enabling `basic`, you must respect these restrictions:

Runtime parameters for executing a query:
* Search without filter and transaction
* Returning insufficient vectors is acceptable

| Option | Type | Range | Default | Description |
| ------------------------ | ------- | -------------- | ------- | ----------------------------------------- |
| vectors.enable_prefilter | boolean | | `on` | Enables or disables the use of prefilter. |
| vectors.ivf_nprobe | integer | `[1, 1000000]` | `10` | Number of lists to scan. |
| vectors.hnsw_ef_search | integer | `[1, 65535]` | `100` | Search scope of HNSW. |

0 comments on commit 60e185f

Please sign in to comment.