# Vector search

Vector search ranks rows by how close a stored vector is to a query vector, where a traditional search compares keywords for an exact match. The values it compares are vector embeddings, the numeric representations of text, images, or other data. [SQL Database](/en/documentation/platform/sql-database/) implements vector search through libSQL, which stores each vector in the native SQLite BLOB storage class. The vector type sets how many bits represent each floating-point number in the vector.

Vector search serves four kinds of workload:

- Recommendations that find items with similar characteristics, such as related products in an ecommerce catalog or content in a streaming platform.
- Semantic text search, where a text embedding represents a word or a phrase as a vector.
- Voice assistants and chatbots that apply natural language processing (NLP).
- The retrieval step of a retrieval-augmented generation (RAG) application built on [AI Inference](/en/documentation/platform/ai-inference/), with a framework such as LangChain or LangGraph.

The vectors sit in the same database as the rest of the rows, so one statement filters on ordinary columns and ranks by vector distance. No separate vector service holds a copy of the data. For how a query reaches the data, refer to [How SQL Database works](/en/documentation/platform/sql-database/how-it-works/).

---

## Vector columns

A vector column is declared as a blob type carrying the number of dimensions each vector holds. A `F32_BLOB` column holds an array of 32-bit floating-point numbers as a binary large object (BLOB). This statement declares a three-dimensional vector column:

```sql
CREATE TABLE teams (
  name TEXT,
  year INT,
  stats_embedding F32_BLOB(3)
);
```

The examples on this page use three dimensions so that every vector is readable in full. A real embedding column declares the dimension count of the model that fills it. The `text-embedding-3-small` model returns 1,536 dimensions, so its column is declared `F32_BLOB(1536)`.

Each type has an alias, and the two spellings declare the same column:

| Type        | Alias        |
| ----------- | ------------ |
| `FLOAT1BIT` | `F1BIT_BLOB` |
| `FLOAT8`    | `F8_BLOB`    |
| `FLOATB16`  | `FB16_BLOB`  |
| `FLOAT16`   | `F16_BLOB`   |
| `FLOAT32`   | `F32_BLOB`   |
| `FLOAT64`   | `F64_BLOB`   |

`FLOAT32` is the recommended starting point.

---

## Functions

SQL Database adds six SQL functions for vector data:

| Function                           | What it does                                                                              |
| ---------------------------------- | ----------------------------------------------------------------------------------------- |
| `vector('[...]')`                  | Builds a vector from its text form. A vector of more than 65,536 dimensions is rejected   |
| `vector_extract(column)`           | Returns the text form of a stored vector                                                  |
| `vector_distance_cos(a, b)`        | Returns the cosine distance between two vectors, where `0` is nearest                     |
| `vector_distance_l2(a, b)`         | Returns the Euclidean distance between two vectors. Not supported for `FLOAT1BIT` vectors |
| `libsql_vector_idx(column)`        | Marks a column for an approximate-nearest-neighbor index                                  |
| `vector_top_k('index', vector, k)` | Returns the `k` nearest rows of an index, to join on `rowid`                              |

`vector` builds the value a vector column stores, and `vector_extract` reads it back. The text form `vector_extract` returns carries no spaces between the elements, so a column holding `vector('[85, 25, 65]')` returns `[85,25,65]`.

---

## Distance

A distance function compares two vectors and returns a number. A smaller number means the two vectors are closer.

`vector_distance_cos` returns the cosine distance, which is `1 - cosine similarity`. The value ranges from 0 to 2:

- A distance close to `0` means the vectors are nearly identical, or exactly matching.
- A distance close to `1` means the vectors are orthogonal, at a right angle to each other.
- A distance close to `2` means the vectors point in opposite directions.

`vector_distance_l2` returns the Euclidean distance instead. It is not supported for `FLOAT1BIT` vectors.

Order by the distance to rank rows from nearest to furthest. This query returns the three teams whose stats are closest to a season of 82 goals scored, 25 conceded, and 63% possession:

```sql
SELECT name,
       vector_distance_cos(stats_embedding, vector('[82, 25, 63]')) AS similarity
FROM teams
ORDER BY similarity ASC
LIMIT 3;
```

A query written this way does not consult a vector index. The index is stored as a separate table, and a statement reaches it only through `vector_top_k`. To rank a large table without comparing the query vector with every row, refer to [Indexing](#indexing).

---

## Indexing

A vector index answers an approximate-nearest-neighbor (ANN) search without comparing the query vector with every row. SQL Database builds the index with the DiskANN algorithm. Wrap the vector column in `libsql_vector_idx` to create one:

```sql
CREATE INDEX teams_idx ON teams (libsql_vector_idx(stats_embedding));
```

The column inside `libsql_vector_idx` is the vector column of the table the index is created on. A second argument sets the distance metric, as in `libsql_vector_idx(stats_embedding, 'metric=cosine')`.

Creating a vector index adds a shadow table named after the index, `teams_idx_shadow`. A table listing returns it beside the tables you created. The `.tables` command of the [EdgeSQL Shell](/en/documentation/platform/sql-database/edgesql-shell/) and the `getTables` call of the [azion/sql library](/en/documentation/devtools/azion-lib/sql/) both show it.

A vector index requires a table with a `ROWID` or with a single-column `PRIMARY KEY`. A composite `PRIMARY KEY` without a `ROWID` is not supported.

---

## Querying

Using a vector index is not automatic. Select from `vector_top_k` and join the table on `rowid` to make a statement consult the index.

`vector_top_k` takes the index name, the query vector, and the number of rows to return. It returns those rows as one column named `id`, which holds the `ROWID` or the `PRIMARY KEY` of each nearest neighbor.

This sequence creates the table, stores four vectors, indexes them, and returns the two teams nearest to the query vector:

```sql
CREATE TABLE teams (
  name TEXT,
  year INT,
  stats_embedding F32_BLOB(3)
);

INSERT INTO teams (name, year, stats_embedding)
VALUES
  ('Red', 2023, vector('[80, 30, 60]')),
  ('Blue', 2023, vector('[85, 25, 65]')),
  ('Yellow', 2023, vector('[78, 28, 62]')),
  ('Green', 2023, vector('[90, 20, 70]'));

CREATE INDEX teams_idx ON teams (libsql_vector_idx(stats_embedding));

SELECT name, year
FROM vector_top_k('teams_idx', vector('[82, 25, 63]'), 2)
JOIN teams ON teams.rowid = id;
```

A `WHERE` clause filters the rows `vector_top_k` already returned. Adding `WHERE year >= 2023` keeps the 2023 rows among those two, and it never makes the index return more than `k` rows.

Generate the query vector with the same embedding model that produced the stored column. For a walkthrough that builds a semantic search end to end, refer to [Build a semantic search with vector embeddings](/en/documentation/guides/application-development/data/sql-database-vector-search/).

---

## Limits

Three bounds apply to vector data. A vector holds at most 65,536 dimensions. `vector_distance_l2` is not supported for `FLOAT1BIT` vectors. A vector index requires a table with a `ROWID` or a single-column `PRIMARY KEY`.

The dimension ceiling is enforced when a vector is built, not when a column is declared. The statement `CREATE TABLE t (v F32_BLOB(65537));` succeeds, because SQLite does not validate the parameter of a column type. The statement that calls `vector` with more than 65,536 dimensions is the one that fails, and it answers `{"error":"vector: max size exceeded 65536"}` inside an HTTP `200` response.

For every other bound SQL Database applies, refer to [SQL Database limits](/en/documentation/platform/sql-database/limits/).

---

## Related resources

- [Build a semantic search with vector embeddings](/en/documentation/guides/application-development/data/sql-database-vector-search.md): A walkthrough that creates the embedding column, fills it, indexes it, and queries it.
- [SQL Database](/en/documentation/platform/sql-database.md): The product the vector columns and functions on this page belong to.
- [Databases and queries](/en/documentation/platform/sql-database/databases-and-queries.md): The envelope a statement returns, and how a failed vector statement reports its error.
- [SQL Database limits](/en/documentation/platform/sql-database/limits.md): Every bound the product applies, including the ones on vector data.
- [AI Inference](/en/documentation/platform/ai-inference.md): Where a RAG application runs its model, beside the vector search that retrieves the context.
