git-subtree-dir: vendor/ruvector git-subtree-split: b64c21726f2bb37286d9ee36a7869fef60cc6900
814 lines
14 KiB
Markdown
814 lines
14 KiB
Markdown
# RuVector-Postgres API Reference
|
||
|
||
## Overview
|
||
|
||
Complete API reference for RuVector-Postgres extension, including SQL functions, operators, types, and GUC variables.
|
||
|
||
## Table of Contents
|
||
|
||
- [Data Types](#data-types)
|
||
- [SQL Functions](#sql-functions)
|
||
- [Operators](#operators)
|
||
- [Index Methods](#index-methods)
|
||
- [GUC Variables](#guc-variables)
|
||
- [Operator Classes](#operator-classes)
|
||
- [Usage Examples](#usage-examples)
|
||
|
||
## Data Types
|
||
|
||
### `ruvector(n)`
|
||
|
||
Primary vector type for dense floating-point vectors.
|
||
|
||
**Syntax:**
|
||
|
||
```sql
|
||
ruvector(dimensions)
|
||
```
|
||
|
||
**Parameters:**
|
||
|
||
- `dimensions`: Integer, 1 to 16,000
|
||
|
||
**Storage:**
|
||
|
||
- Header: 8 bytes
|
||
- Data: 4 bytes per dimension (f32)
|
||
- Total: 8 + (4 × dimensions) bytes
|
||
|
||
**Example:**
|
||
|
||
```sql
|
||
CREATE TABLE items (
|
||
id SERIAL PRIMARY KEY,
|
||
embedding ruvector(1536) -- OpenAI ada-002 dimensions
|
||
);
|
||
|
||
INSERT INTO items (embedding) VALUES ('[1.0, 2.0, 3.0]');
|
||
INSERT INTO items (embedding) VALUES (ARRAY[1.0, 2.0, 3.0]::ruvector);
|
||
```
|
||
|
||
### `halfvec(n)`
|
||
|
||
Half-precision (16-bit float) vector type.
|
||
|
||
**Syntax:**
|
||
|
||
```sql
|
||
halfvec(dimensions)
|
||
```
|
||
|
||
**Parameters:**
|
||
|
||
- `dimensions`: Integer, 1 to 16,000
|
||
|
||
**Storage:**
|
||
|
||
- Header: 8 bytes
|
||
- Data: 2 bytes per dimension (f16)
|
||
- Total: 8 + (2 × dimensions) bytes
|
||
|
||
**Benefits:**
|
||
|
||
- 50% memory reduction vs `ruvector`
|
||
- <0.01% accuracy loss for most embeddings
|
||
- SIMD f16 support on modern CPUs
|
||
|
||
**Example:**
|
||
|
||
```sql
|
||
CREATE TABLE items (
|
||
id SERIAL PRIMARY KEY,
|
||
embedding halfvec(1536) -- 3,080 bytes vs 6,152 for ruvector
|
||
);
|
||
|
||
-- Automatic conversion from ruvector
|
||
INSERT INTO items (embedding)
|
||
SELECT embedding::halfvec FROM ruvector_table;
|
||
```
|
||
|
||
### `sparsevec(n)`
|
||
|
||
Sparse vector type for high-dimensional sparse data.
|
||
|
||
**Syntax:**
|
||
|
||
```sql
|
||
sparsevec(dimensions)
|
||
```
|
||
|
||
**Parameters:**
|
||
|
||
- `dimensions`: Integer, 1 to 1,000,000
|
||
|
||
**Storage:**
|
||
|
||
- Header: 12 bytes
|
||
- Data: 8 bytes per non-zero element (u32 index + f32 value)
|
||
- Total: 12 + (8 × nnz) bytes
|
||
|
||
**Use Cases:**
|
||
|
||
- BM25 text embeddings
|
||
- TF-IDF vectors
|
||
- High-dimensional sparse features
|
||
|
||
**Example:**
|
||
|
||
```sql
|
||
CREATE TABLE documents (
|
||
id SERIAL PRIMARY KEY,
|
||
sparse_embedding sparsevec(50000) -- Only stores non-zero values
|
||
);
|
||
|
||
-- Sparse vector with 3 non-zero values
|
||
INSERT INTO documents (sparse_embedding)
|
||
VALUES ('{1:0.5, 100:0.8, 5000:0.3}/50000');
|
||
```
|
||
|
||
## SQL Functions
|
||
|
||
### Information Functions
|
||
|
||
#### `ruvector_version()`
|
||
|
||
Returns the extension version.
|
||
|
||
**Syntax:**
|
||
|
||
```sql
|
||
ruvector_version() → text
|
||
```
|
||
|
||
**Example:**
|
||
|
||
```sql
|
||
SELECT ruvector_version();
|
||
-- Output: '0.1.19'
|
||
```
|
||
|
||
#### `ruvector_simd_info()`
|
||
|
||
Returns detected SIMD capabilities.
|
||
|
||
**Syntax:**
|
||
|
||
```sql
|
||
ruvector_simd_info() → text
|
||
```
|
||
|
||
**Returns:**
|
||
|
||
- `'AVX512'`: AVX-512 support detected
|
||
- `'AVX2'`: AVX2 support detected
|
||
- `'NEON'`: ARM NEON support detected
|
||
- `'Scalar'`: No SIMD support
|
||
|
||
**Example:**
|
||
|
||
```sql
|
||
SELECT ruvector_simd_info();
|
||
-- Output: 'AVX2'
|
||
```
|
||
|
||
### Distance Functions
|
||
|
||
#### `ruvector_l2_distance(a, b)`
|
||
|
||
Compute L2 (Euclidean) distance.
|
||
|
||
**Syntax:**
|
||
|
||
```sql
|
||
ruvector_l2_distance(a ruvector, b ruvector) → float4
|
||
```
|
||
|
||
**Formula:**
|
||
|
||
```
|
||
L2(a, b) = sqrt(Σ(a[i] - b[i])²)
|
||
```
|
||
|
||
**Properties:**
|
||
|
||
- SIMD optimized
|
||
- Parallel safe
|
||
- Immutable
|
||
|
||
**Example:**
|
||
|
||
```sql
|
||
SELECT ruvector_l2_distance(
|
||
'[1.0, 2.0, 3.0]'::ruvector,
|
||
'[4.0, 5.0, 6.0]'::ruvector
|
||
);
|
||
-- Output: 5.196...
|
||
```
|
||
|
||
#### `ruvector_cosine_distance(a, b)`
|
||
|
||
Compute cosine distance.
|
||
|
||
**Syntax:**
|
||
|
||
```sql
|
||
ruvector_cosine_distance(a ruvector, b ruvector) → float4
|
||
```
|
||
|
||
**Formula:**
|
||
|
||
```
|
||
Cosine(a, b) = 1 - (a·b) / (||a|| ||b||)
|
||
```
|
||
|
||
**Range:** [0, 2]
|
||
|
||
- 0: Vectors point in same direction
|
||
- 1: Vectors are orthogonal
|
||
- 2: Vectors point in opposite directions
|
||
|
||
**Example:**
|
||
|
||
```sql
|
||
SELECT ruvector_cosine_distance(
|
||
'[1.0, 0.0]'::ruvector,
|
||
'[0.0, 1.0]'::ruvector
|
||
);
|
||
-- Output: 1.0 (orthogonal)
|
||
```
|
||
|
||
#### `ruvector_ip_distance(a, b)`
|
||
|
||
Compute inner product (negative dot product) distance.
|
||
|
||
**Syntax:**
|
||
|
||
```sql
|
||
ruvector_ip_distance(a ruvector, b ruvector) → float4
|
||
```
|
||
|
||
**Formula:**
|
||
|
||
```
|
||
IP(a, b) = -Σ(a[i] * b[i])
|
||
```
|
||
|
||
**Note:** Negative to work with `ORDER BY ASC`.
|
||
|
||
**Example:**
|
||
|
||
```sql
|
||
SELECT ruvector_ip_distance(
|
||
'[1.0, 2.0, 3.0]'::ruvector,
|
||
'[4.0, 5.0, 6.0]'::ruvector
|
||
);
|
||
-- Output: -32.0 (negative of 1*4 + 2*5 + 3*6)
|
||
```
|
||
|
||
#### `ruvector_l1_distance(a, b)`
|
||
|
||
Compute L1 (Manhattan) distance.
|
||
|
||
**Syntax:**
|
||
|
||
```sql
|
||
ruvector_l1_distance(a ruvector, b ruvector) → float4
|
||
```
|
||
|
||
**Formula:**
|
||
|
||
```
|
||
L1(a, b) = Σ|a[i] - b[i]|
|
||
```
|
||
|
||
**Example:**
|
||
|
||
```sql
|
||
SELECT ruvector_l1_distance(
|
||
'[1.0, 2.0, 3.0]'::ruvector,
|
||
'[4.0, 5.0, 6.0]'::ruvector
|
||
);
|
||
-- Output: 9.0
|
||
```
|
||
|
||
### Utility Functions
|
||
|
||
#### `ruvector_norm(v)`
|
||
|
||
Compute L2 norm (magnitude) of a vector.
|
||
|
||
**Syntax:**
|
||
|
||
```sql
|
||
ruvector_norm(v ruvector) → float4
|
||
```
|
||
|
||
**Formula:**
|
||
|
||
```
|
||
||v|| = sqrt(Σv[i]²)
|
||
```
|
||
|
||
**Example:**
|
||
|
||
```sql
|
||
SELECT ruvector_norm('[3.0, 4.0]'::ruvector);
|
||
-- Output: 5.0
|
||
```
|
||
|
||
#### `ruvector_normalize(v)`
|
||
|
||
Normalize vector to unit length.
|
||
|
||
**Syntax:**
|
||
|
||
```sql
|
||
ruvector_normalize(v ruvector) → ruvector
|
||
```
|
||
|
||
**Formula:**
|
||
|
||
```
|
||
normalize(v) = v / ||v||
|
||
```
|
||
|
||
**Example:**
|
||
|
||
```sql
|
||
SELECT ruvector_normalize('[3.0, 4.0]'::ruvector);
|
||
-- Output: [0.6, 0.8]
|
||
```
|
||
|
||
### Index Maintenance Functions
|
||
|
||
#### `ruvector_index_stats(index_name)`
|
||
|
||
Get statistics for a vector index.
|
||
|
||
**Syntax:**
|
||
|
||
```sql
|
||
ruvector_index_stats(index_name text) → TABLE(
|
||
index_name text,
|
||
index_size_mb numeric,
|
||
vector_count bigint,
|
||
dimensions int,
|
||
build_time_seconds numeric,
|
||
fragmentation_pct numeric
|
||
)
|
||
```
|
||
|
||
**Example:**
|
||
|
||
```sql
|
||
SELECT * FROM ruvector_index_stats('items_embedding_idx');
|
||
|
||
-- Output:
|
||
-- index_name | items_embedding_idx
|
||
-- index_size_mb | 512
|
||
-- vector_count | 1000000
|
||
-- dimensions | 1536
|
||
-- build_time_seconds | 45.2
|
||
-- fragmentation_pct | 2.3
|
||
```
|
||
|
||
#### `ruvector_index_maintenance(index_name)`
|
||
|
||
Perform maintenance on a vector index.
|
||
|
||
**Syntax:**
|
||
|
||
```sql
|
||
ruvector_index_maintenance(index_name text) → void
|
||
```
|
||
|
||
**Operations:**
|
||
|
||
- Removes deleted nodes
|
||
- Rebuilds fragmented layers
|
||
- Updates statistics
|
||
|
||
**Example:**
|
||
|
||
```sql
|
||
SELECT ruvector_index_maintenance('items_embedding_idx');
|
||
```
|
||
|
||
## Operators
|
||
|
||
### Distance Operators
|
||
|
||
| Operator | Name | Distance Metric | Order |
|
||
|----------|------|----------------|-------|
|
||
| `<->` | L2 | Euclidean | ASC |
|
||
| `<#>` | IP | Inner Product (negative) | ASC |
|
||
| `<=>` | Cosine | Cosine Distance | ASC |
|
||
| `<+>` | L1 | Manhattan | ASC |
|
||
|
||
**Properties:**
|
||
|
||
- All operators are IMMUTABLE
|
||
- All operators are PARALLEL SAFE
|
||
- All operators support index scans
|
||
|
||
### L2 Distance Operator (`<->`)
|
||
|
||
**Syntax:**
|
||
|
||
```sql
|
||
vector1 <-> vector2
|
||
```
|
||
|
||
**Example:**
|
||
|
||
```sql
|
||
SELECT * FROM items
|
||
ORDER BY embedding <-> '[1.0, 2.0, 3.0]'::ruvector
|
||
LIMIT 10;
|
||
```
|
||
|
||
### Cosine Distance Operator (`<=>`)
|
||
|
||
**Syntax:**
|
||
|
||
```sql
|
||
vector1 <=> vector2
|
||
```
|
||
|
||
**Example:**
|
||
|
||
```sql
|
||
SELECT * FROM items
|
||
ORDER BY embedding <=> '[1.0, 2.0, 3.0]'::ruvector
|
||
LIMIT 10;
|
||
```
|
||
|
||
### Inner Product Operator (`<#>`)
|
||
|
||
**Syntax:**
|
||
|
||
```sql
|
||
vector1 <#> vector2
|
||
```
|
||
|
||
**Note:** Returns negative dot product for ascending order.
|
||
|
||
**Example:**
|
||
|
||
```sql
|
||
SELECT * FROM items
|
||
ORDER BY embedding <#> '[1.0, 2.0, 3.0]'::ruvector
|
||
LIMIT 10;
|
||
```
|
||
|
||
### Manhattan Distance Operator (`<+>`)
|
||
|
||
**Syntax:**
|
||
|
||
```sql
|
||
vector1 <+> vector2
|
||
```
|
||
|
||
**Example:**
|
||
|
||
```sql
|
||
SELECT * FROM items
|
||
ORDER BY embedding <+> '[1.0, 2.0, 3.0]'::ruvector
|
||
LIMIT 10;
|
||
```
|
||
|
||
## Index Methods
|
||
|
||
### HNSW Index (`ruhnsw`)
|
||
|
||
Hierarchical Navigable Small World graph index.
|
||
|
||
**Syntax:**
|
||
|
||
```sql
|
||
CREATE INDEX index_name ON table_name
|
||
USING ruhnsw (column operator_class)
|
||
WITH (options);
|
||
```
|
||
|
||
**Options:**
|
||
|
||
| Option | Type | Default | Range | Description |
|
||
|--------|------|---------|-------|-------------|
|
||
| `m` | integer | 16 | 2-100 | Max connections per layer |
|
||
| `ef_construction` | integer | 64 | 4-1000 | Build-time search breadth |
|
||
| `quantization` | text | NULL | sq8, pq16, binary | Quantization method |
|
||
|
||
**Operator Classes:**
|
||
|
||
- `ruvector_l2_ops`: For `<->` operator
|
||
- `ruvector_ip_ops`: For `<#>` operator
|
||
- `ruvector_cosine_ops`: For `<=>` operator
|
||
|
||
**Example:**
|
||
|
||
```sql
|
||
-- Basic HNSW index
|
||
CREATE INDEX items_embedding_idx ON items
|
||
USING ruhnsw (embedding ruvector_l2_ops);
|
||
|
||
-- High recall HNSW index
|
||
CREATE INDEX items_embedding_idx ON items
|
||
USING ruhnsw (embedding ruvector_l2_ops)
|
||
WITH (m = 32, ef_construction = 200);
|
||
|
||
-- HNSW with quantization
|
||
CREATE INDEX items_embedding_idx ON items
|
||
USING ruhnsw (embedding ruvector_l2_ops)
|
||
WITH (m = 16, ef_construction = 100, quantization = 'sq8');
|
||
```
|
||
|
||
**Performance:**
|
||
|
||
- Search: O(log n)
|
||
- Insert: O(log n)
|
||
- Memory: ~1.5x vector data size
|
||
- Recall: 95-99%+ with tuned parameters
|
||
|
||
### IVFFlat Index (`ruivfflat`)
|
||
|
||
Inverted file with flat (uncompressed) vectors.
|
||
|
||
**Syntax:**
|
||
|
||
```sql
|
||
CREATE INDEX index_name ON table_name
|
||
USING ruivfflat (column operator_class)
|
||
WITH (lists = n);
|
||
```
|
||
|
||
**Options:**
|
||
|
||
| Option | Type | Default | Range | Description |
|
||
|--------|------|---------|-------|-------------|
|
||
| `lists` | integer | sqrt(rows) | 1-100000 | Number of clusters |
|
||
|
||
**Operator Classes:**
|
||
|
||
- `ruvector_l2_ops`: For `<->` operator
|
||
- `ruvector_ip_ops`: For `<#>` operator
|
||
- `ruvector_cosine_ops`: For `<=>` operator
|
||
|
||
**Example:**
|
||
|
||
```sql
|
||
-- Basic IVFFlat index
|
||
CREATE INDEX items_embedding_idx ON items
|
||
USING ruivfflat (embedding ruvector_l2_ops)
|
||
WITH (lists = 100);
|
||
|
||
-- IVFFlat for large dataset
|
||
CREATE INDEX items_embedding_idx ON items
|
||
USING ruivfflat (embedding ruvector_l2_ops)
|
||
WITH (lists = 1000);
|
||
```
|
||
|
||
**Performance:**
|
||
|
||
- Search: O(√n)
|
||
- Insert: O(1) after training
|
||
- Memory: Minimal overhead
|
||
- Recall: 90-95% with appropriate probes
|
||
|
||
**Training:**
|
||
|
||
IVFFlat requires training to find cluster centroids:
|
||
|
||
```sql
|
||
-- Index is automatically trained during creation
|
||
-- Training uses k-means on a sample of vectors
|
||
```
|
||
|
||
## GUC Variables
|
||
|
||
### `ruvector.ef_search`
|
||
|
||
Controls HNSW search quality (higher = better recall, slower).
|
||
|
||
**Syntax:**
|
||
|
||
```sql
|
||
SET ruvector.ef_search = value;
|
||
```
|
||
|
||
**Default:** 40
|
||
|
||
**Range:** 1-1000
|
||
|
||
**Scope:** Session, transaction, or global
|
||
|
||
**Example:**
|
||
|
||
```sql
|
||
-- Session-level
|
||
SET ruvector.ef_search = 200;
|
||
|
||
-- Transaction-level
|
||
BEGIN;
|
||
SET LOCAL ruvector.ef_search = 100;
|
||
SELECT ... ORDER BY embedding <-> query;
|
||
COMMIT;
|
||
|
||
-- Global
|
||
ALTER SYSTEM SET ruvector.ef_search = 100;
|
||
SELECT pg_reload_conf();
|
||
```
|
||
|
||
### `ruvector.probes`
|
||
|
||
Controls IVFFlat search quality (higher = better recall, slower).
|
||
|
||
**Syntax:**
|
||
|
||
```sql
|
||
SET ruvector.probes = value;
|
||
```
|
||
|
||
**Default:** 1
|
||
|
||
**Range:** 1-10000
|
||
|
||
**Recommended:** sqrt(lists) for 90%+ recall
|
||
|
||
**Example:**
|
||
|
||
```sql
|
||
-- For lists = 100, use probes = 10
|
||
SET ruvector.probes = 10;
|
||
```
|
||
|
||
## Operator Classes
|
||
|
||
### `ruvector_l2_ops`
|
||
|
||
For L2 (Euclidean) distance queries.
|
||
|
||
**Usage:**
|
||
|
||
```sql
|
||
CREATE INDEX ... USING ruhnsw (embedding ruvector_l2_ops);
|
||
SELECT ... ORDER BY embedding <-> query;
|
||
```
|
||
|
||
### `ruvector_ip_ops`
|
||
|
||
For inner product distance queries.
|
||
|
||
**Usage:**
|
||
|
||
```sql
|
||
CREATE INDEX ... USING ruhnsw (embedding ruvector_ip_ops);
|
||
SELECT ... ORDER BY embedding <#> query;
|
||
```
|
||
|
||
### `ruvector_cosine_ops`
|
||
|
||
For cosine distance queries.
|
||
|
||
**Usage:**
|
||
|
||
```sql
|
||
CREATE INDEX ... USING ruhnsw (embedding ruvector_cosine_ops);
|
||
SELECT ... ORDER BY embedding <=> query;
|
||
```
|
||
|
||
## Usage Examples
|
||
|
||
### Basic Vector Search
|
||
|
||
```sql
|
||
-- Create table
|
||
CREATE TABLE documents (
|
||
id SERIAL PRIMARY KEY,
|
||
content TEXT,
|
||
embedding ruvector(1536)
|
||
);
|
||
|
||
-- Insert vectors
|
||
INSERT INTO documents (content, embedding) VALUES
|
||
('Document 1', '[0.1, 0.2, ...]'::ruvector),
|
||
('Document 2', '[0.3, 0.4, ...]'::ruvector);
|
||
|
||
-- Create index
|
||
CREATE INDEX documents_embedding_idx ON documents
|
||
USING ruhnsw (embedding ruvector_l2_ops);
|
||
|
||
-- Search
|
||
SELECT content, embedding <-> '[0.5, 0.6, ...]'::ruvector AS distance
|
||
FROM documents
|
||
ORDER BY distance
|
||
LIMIT 10;
|
||
```
|
||
|
||
### Filtered Vector Search
|
||
|
||
```sql
|
||
-- Search with WHERE clause
|
||
SELECT content, embedding <-> query AS distance
|
||
FROM documents
|
||
WHERE category = 'technology'
|
||
ORDER BY distance
|
||
LIMIT 10;
|
||
```
|
||
|
||
### Batch Distance Calculation
|
||
|
||
```sql
|
||
-- Compute distances to multiple vectors
|
||
WITH queries AS (
|
||
SELECT id, embedding AS query FROM queries_table
|
||
)
|
||
SELECT
|
||
q.id AS query_id,
|
||
d.id AS doc_id,
|
||
d.embedding <-> q.query AS distance
|
||
FROM documents d
|
||
CROSS JOIN queries q
|
||
ORDER BY q.id, distance
|
||
LIMIT 100;
|
||
```
|
||
|
||
### Vector Arithmetic
|
||
|
||
```sql
|
||
-- Add vectors
|
||
SELECT (embedding1 + embedding2) AS sum FROM ...;
|
||
|
||
-- Subtract vectors
|
||
SELECT (embedding1 - embedding2) AS diff FROM ...;
|
||
|
||
-- Scalar multiplication
|
||
SELECT (embedding * 2.0) AS scaled FROM ...;
|
||
```
|
||
|
||
### Hybrid Search (Vector + Text)
|
||
|
||
```sql
|
||
-- Combine vector similarity with text search
|
||
SELECT
|
||
content,
|
||
embedding <-> query_vector AS vector_score,
|
||
ts_rank(to_tsvector(content), to_tsquery('search terms')) AS text_score,
|
||
(0.7 * (1 / (1 + embedding <-> query_vector)) +
|
||
0.3 * ts_rank(to_tsvector(content), to_tsquery('search terms'))) AS combined_score
|
||
FROM documents
|
||
WHERE to_tsvector(content) @@ to_tsquery('search terms')
|
||
ORDER BY combined_score DESC
|
||
LIMIT 10;
|
||
```
|
||
|
||
### Index Parameter Tuning
|
||
|
||
```sql
|
||
-- Test different ef_search values
|
||
DO $$
|
||
DECLARE
|
||
ef_val INTEGER;
|
||
BEGIN
|
||
FOR ef_val IN 10, 20, 40, 80, 160 LOOP
|
||
EXECUTE format('SET LOCAL ruvector.ef_search = %s', ef_val);
|
||
RAISE NOTICE 'ef_search = %', ef_val;
|
||
|
||
PERFORM * FROM items
|
||
ORDER BY embedding <-> '[...]'::ruvector
|
||
LIMIT 10;
|
||
END LOOP;
|
||
END $$;
|
||
```
|
||
|
||
## Performance Tips
|
||
|
||
1. **Choose the right index:**
|
||
- HNSW: Best for high recall, fast queries
|
||
- IVFFlat: Best for memory-constrained environments
|
||
|
||
2. **Tune index parameters:**
|
||
- Higher `m` and `ef_construction`: Better recall, larger index
|
||
- Higher `ef_search`: Better recall, slower queries
|
||
|
||
3. **Use appropriate vector type:**
|
||
- `ruvector`: Full precision
|
||
- `halfvec`: 50% memory savings, minimal accuracy loss
|
||
- `sparsevec`: Massive savings for sparse data
|
||
|
||
4. **Enable parallelism:**
|
||
```sql
|
||
SET max_parallel_workers_per_gather = 4;
|
||
```
|
||
|
||
5. **Use quantization for large datasets:**
|
||
```sql
|
||
WITH (quantization = 'sq8') -- 4x memory reduction
|
||
```
|
||
|
||
## See Also
|
||
|
||
- [ARCHITECTURE.md](./ARCHITECTURE.md) - System architecture
|
||
- [SIMD_OPTIMIZATION.md](./SIMD_OPTIMIZATION.md) - Performance details
|
||
- [MIGRATION.md](./MIGRATION.md) - Migrating from pgvector
|