Skip to content

Commit 7832a7f

Browse files
committed
Merge branch 'main' into auxiliary
2 parents 5d9b923 + 6658624 commit 7832a7f

File tree

5 files changed

+319
-5
lines changed

5 files changed

+319
-5
lines changed

.github/workflows/release.yaml

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,18 @@ jobs:
5555
with:
5656
name: sqlite-vec-windows-x86_64-extension
5757
path: dist/*
58+
build-linux-aarch64-extension:
59+
runs-on: ubuntu-latest
60+
steps:
61+
- uses: actions/checkout@v4
62+
- run: sudo apt-get install gcc-arm-linux-gnueabihf
63+
- run: ./scripts/vendor.sh
64+
- run: make sqlite-vec.h
65+
- run: make CC=arm-linux-gnueabihf-gcc loadable static
66+
- uses: actions/upload-artifact@v4
67+
with:
68+
name: sqlite-vec-linux-aarch64-extension
69+
path: dist/*
5870
build-cosmopolitan:
5971
runs-on: macos-latest
6072
permissions:
@@ -190,6 +202,10 @@ jobs:
190202
with:
191203
name: sqlite-vec-linux-x86_64-extension
192204
path: dist/linux-x86_64
205+
- uses: actions/download-artifact@v4
206+
with:
207+
name: sqlite-vec-linux-aarch64-extension
208+
path: dist/linux-aarch64
193209
- uses: actions/download-artifact@v4
194210
with:
195211
name: sqlite-vec-macos-x86_64-extension
@@ -239,7 +255,7 @@ jobs:
239255
name: sqlite-vec-iossimulator-x86_64-extension
240256
path: dist/iossimulator-x86_64
241257
- run: |
242-
curl -L https://github.com/asg017/sqlite-dist/releases/download/v0.0.1-alpha.16/sqlite-dist-x86_64-unknown-linux-gnu.tar.xz \
258+
curl -L https://github.com/asg017/sqlite-dist/releases/download/v0.0.1-alpha.17/sqlite-dist-x86_64-unknown-linux-gnu.tar.xz \
243259
| tar xfJ - --strip-components 1
244260
- run: make sqlite-vec.h
245261
- run: ./sqlite-dist ./sqlite-dist.toml --input dist/ --output distx/ --version $(cat VERSION)

TODO

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
44
- remove previous row from chunk, insert into new one?
55
- [ ] properly sqlite3_vtab_nochange / sqlite3_value_nochange handling
66

7-
87
# auxiliary columns
98

109
- later:
1110
- NOT NULL?
1211
- perf: INSERT stmt should be cached on vec0_vtab
13-
- perf: LEFT JOIN aux table to rowids query in vec0_cursor for rowid/point stmts, to avoid N lookup queries
12+
- perf: LEFT JOIN aux table to rowids query in vec0_cursor for rowid/point
13+
stmts, to avoid N lookup queries

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.1.4-alpha.2
1+
0.1.5

site/metadata-beta.md

Lines changed: 299 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,299 @@
1+
# Experimental Metadata Filtering Builds
2+
3+
The `sqlite-vec` project has a series of pull requests
4+
([#122](https://github.com/asg017/sqlite-vec/pull/122),
5+
[#123](https://github.com/asg017/sqlite-vec/pull/123), and
6+
[#124](https://github.com/asg017/sqlite-vec/pull/124)) that will add proper
7+
metadata column support to `vec0` virtual tables.
8+
9+
But they aren't merged yet! So I've packaged pre-compiled extensions with these
10+
features baked in, so that others can try it for themselves. Once those pull
11+
requests are merged, this page will be removed.
12+
13+
As a quick sample, this is what metadata columns will look like:
14+
15+
```sql
16+
create virtual table vec_movies using vec0(
17+
-- aliased primary key
18+
movie_id integer primary key,
19+
20+
-- vector column
21+
synopsis_embedding float[1024],
22+
23+
-- partition key (internally shards vectors)
24+
user_id integer primary key,
25+
26+
-- metadata columns (indexed alongside vectors)
27+
genre text,
28+
num_reviews int,
29+
mean_rating float,
30+
31+
-- auxiliary columns (not indexed)
32+
+synopsis text
33+
);
34+
35+
select
36+
movie_id,
37+
title,
38+
genre,
39+
num_reviews,
40+
mean_rating,
41+
distance
42+
from vec_movies
43+
where synopsis_embedding match '[...]'
44+
and genre = 'scifi'
45+
and num_reviews between 100 and 500
46+
and mean_rating > 3.5
47+
and k = 5;
48+
/*
49+
┌──────────┬─────────────────────┬─────────┬─────────────┬──────────────────┬──────────┐
50+
│ movie_id │ title │ genre │ num_reviews │ mean_rating │ distance │
51+
├──────────┼─────────────────────┼─────────┼─────────────┼──────────────────┼──────────┤
52+
│ 13 │ 'The Matrix' │ 'scifi' │ 423 │ 4.5 │ 2.5 │
53+
│ 18 │ 'Inception' │ 'scifi' │ 201 │ 5.0 │ 2.5 │
54+
│ 21 │ 'Gravity' │ 'scifi' │ 342 │ 4.0 │ 5.5 │
55+
│ 22 │ 'Dune' │ 'scifi' │ 451 │ 4.40000009536743 │ 6.5 │
56+
│ 8 │ 'Blade Runner 2049' │ 'scifi' │ 301 │ 5.0 │ 7.5 │
57+
└──────────┴─────────────────────┴─────────┴─────────────┴──────────────────┴──────────┘
58+
```
59+
60+
## Install
61+
62+
To try it out youself, download one of the following ZIP files that contain
63+
pre-compiled SQLite extensions. You can manually load them into your
64+
Python/JavaScript/Ruby/etc. projects to try things out.
65+
66+
| Platform | Link |
67+
| ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
68+
| MacOS ARM | [`sqlite-vec-macos-aarch64-extension.zip`](https://fly.storage.tigris.dev/sqlite-vec-public-static/metadata-filtering-beta/v1-052ba4b/sqlite-vec-macos-aarch64-extension.zip) |
69+
| MacOS x86_64 | [`sqlite-vec-macos-x86_64-extension.zip`](https://fly.storage.tigris.dev/sqlite-vec-public-static/metadata-filtering-beta/v1-052ba4b/sqlite-vec-macos-x86_64-extension.zip) |
70+
| Linux ARM | [`sqlite-vec-linux-aarch64-extension.zip`](https://fly.storage.tigris.dev/sqlite-vec-public-static/metadata-filtering-beta/v1-052ba4b/sqlite-vec-linux-aarch64-extension.zip) |
71+
| Linux x86_64 | [`sqlite-vec-linux-x86_64-extension.zip`](https://fly.storage.tigris.dev/sqlite-vec-public-static/metadata-filtering-beta/v1-052ba4b/sqlite-vec-linux-x86_64-extension.zip) |
72+
| Windows x86_64 | [`sqlite-vec-windows-x86_64-extension.zip`](https://fly.storage.tigris.dev/sqlite-vec-public-static/metadata-filtering-beta/v1-052ba4b/sqlite-vec-windows-x86_64-extension.zip) |
73+
| Cosmopolitan (`sqlite3` CLI with `sqlite-vec` baked in) | [`sqlite-vec-cosmopolitan.zip`](https://fly.storage.tigris.dev/sqlite-vec-public-static/metadata-filtering-beta/v1-052ba4b/sqlite-vec-cosmopolitan.zip) |
74+
75+
To check which experimental version you are on, run `SELECT vec_version()`. The
76+
most recent version is `v-metadata-experiment.01`.
77+
78+
The rest of this document is documentation about how to use these new metadata,
79+
auxiliary, and partition columns in these experimental builds.
80+
81+
## Experimental Status
82+
83+
This work isn't complete yet, so there are some subtle bugs and TODOs:
84+
85+
- You cannot `UPDATE` a `PARTITION KEY` value yet.
86+
- KNN queries with a `WHERE` constraint on a `TEXT` metadata column that's
87+
longer than `12` characters will fail.
88+
- `NULL` values are not allowed on metadata columns
89+
- `PARTITION KEY` columns only support `=` operators currently, but `!=`, `<=`, `>=`, `<`, and `>` will operators will be supported.
90+
91+
These will be fixed before the official release.
92+
93+
## Metadata in `vec0` Virtual Tables
94+
95+
There are three ways to store non-vector columns in `vec0` virtual tables:
96+
metadata columns, partition keys, and auxiliary columns. Each options has their
97+
own benefits and limitations.
98+
99+
```sql
100+
create virtual table vec_chunks using vec0(
101+
document_id integer partition key,
102+
contents_embedding float[768],
103+
104+
-- partition key column, denoted by 'partition key'
105+
user_id integer partition key,
106+
107+
-- metadata column, appears as normal column definition
108+
label text,
109+
110+
-- auxiliary column, denoted by '+'
111+
+contents text
112+
);
113+
```
114+
115+
A quick summary of each option:
116+
117+
| Column Type | Description | Benefits | Limitations |
118+
| ----------------- | ----------------------------------------------------------------------- | ---------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
119+
| Metadata columns | Stores boolean, integer, floating point, or text data alongside vectors | Can be included in the `WHERE` clause of a KNN query | Slower full scan, slightly inefficient with long strings (`> 12` characters) |
120+
| Auxiliary columns | Stores any kind of data in a separate internal table | Eliminates need for an external `JOIN` | Cannot appear in the `WHERE` clause of a KNN query |
121+
| Partition Key | Internally shards vector index on a given key | Make selective queries much faster | Can cause oversharding and slow KNN if not used carefully. Should be +100's of vectors per unique partition key value |
122+
123+
### Metadata Columns
124+
125+
Metadata columns are extra "regular" columns that you can include in a `vec0`
126+
table definition. These columns will be indexed along with declared vector
127+
columns, and allow you to include extra `WHERE` constraints during KNN queries.
128+
129+
```sql
130+
create virtual table vec_movies using vec0(
131+
movie_id integer primary key,
132+
synopsis_embedding float[1024],
133+
genre text,
134+
num_reviews int,
135+
mean_rating float,
136+
contains_violence boolean
137+
);
138+
```
139+
140+
In the `vec0` constructor, the `genre`, `num_reviews`, `mean_rating`, and
141+
`contains_violence` columns are metadata columns, with their specified type.
142+
143+
A sample KNN query on this table could look like:
144+
145+
```sql
146+
select *
147+
from vec_movies
148+
where synopsis_embedding match '[...]'
149+
and k = 5
150+
and genre = 'scifi'
151+
and num_reviews between 100 and 500
152+
and mean_rating > 3.5
153+
and contains_violence = false;
154+
```
155+
156+
The first two conditions in the `WHERE` clause (`synopsis_embedding match` and
157+
`k = 5`) denote that the query in a KNN query. The other conditions are metadata
158+
constraints, that `sqlite-vec` will recognize and apply during the KNN
159+
calculation. In other words, for the above query, a maximum of 5 rows would be
160+
returned, all of which would fit under all the `WHERE` constraints for their
161+
metadata column values.
162+
163+
#### Metadata Column Declaration
164+
165+
Metatadata columns are declared in the `vec0` constructor just like regular column definitions, with the column name first then the column type.
166+
167+
Only the following column types are supported in metadata columns. All these
168+
columns are strictly typed.
169+
170+
- `TEXT` for text and strings
171+
- `INTEGER` for 8-byte integers
172+
- `FLOAT` for 8-byte floating-point numbers
173+
- `BOOLEAN` for 1-bit `0` or `1`
174+
175+
Other column types may be supported in the future. Column type names are case
176+
insensitive.
177+
178+
Additional column constraints like `UNIQUE` or `NOT NULL` are not supported.
179+
180+
A maximum of 16 metadata columns can be declared in a `vec0` virtual table.
181+
182+
183+
#### Supported operations
184+
185+
Metadata column `WHERE` conditions in a KNN query will only work on the
186+
following operators:
187+
188+
- `=` Equals to
189+
- `!=` Not equals to
190+
- `>` Greater than
191+
- `>=` Greater than or equal to
192+
- `<` Less than
193+
- `<=` Less than or equal to
194+
195+
Using any other operator like `IS NULL`, `LIKE`, `GLOB`, `REGEXP`, or any scalar
196+
function will result in an error or incorrect results.
197+
198+
Boolean columns only support `=` and `!=` operators.
199+
200+
### Partition Key Columns
201+
202+
Partition key columns allow one to internally shard a vector indexed based on a given key. Any `=` constraint in a `WHERE` clause on a partition key column will
203+
204+
For example, say you're performing vector search on a large dataset of documents. However, each document belongs to a user, and users can only search their own documents. It would be wasteful to perform a brute-force over all documents if you only care about 1 user at a time. So, you can partition the vector index based on user ID like so:
205+
206+
```sql
207+
create virtual table vec_documents using vec0(
208+
document_id integer primary key,
209+
user_id integer partition key,
210+
contents_embedding float[1024]
211+
)
212+
```
213+
214+
Then during a KNN query, you can constrain results to a specific user in the `WHERE` clause like so:
215+
216+
```sql
217+
select
218+
document_id,
219+
user_id,
220+
distance
221+
from vec_documents
222+
where contents_embedding match :query
223+
and k = 20
224+
and user_id = 123;
225+
```
226+
227+
`sqlite-vec` will recognize the `user_id = 123` constraint and pre-filter vectors during a KNN search. Vectors with the same partition key values are collocated together, so this is a fast operation.
228+
229+
Another example: say you're performing vector search on a large dataset of news headlines of the past 100 years. However, in your application, most users only want to search a subset of articles based on when they were written, like "in the past ten years" or "during the obama administration." You can paritition based on published date like so:
230+
231+
```sql
232+
create virtual table vec_articles using vec0(
233+
article_id integer primary key,
234+
published_date text partition key,
235+
headline_embedding float[1024]
236+
);
237+
```
238+
239+
And a KNN query:
240+
241+
```sql
242+
select
243+
article_id,
244+
published_date,
245+
distance
246+
from vec_articles
247+
where headline_embedding match :query
248+
and published_date between '2009-01-20' and '2017-01-20'; -- Obama administration
249+
```
250+
251+
But be careful! over-using partition key columns can lead to over-sharding and slower KNN queries. As a rule of thumb, make sure that every unique partition key value has ~100's of vectors associated with it. In the above examples, make sure that every user has on the magnitude of dozens or hundreds of documents each, or that every article has dozens or hundreds of articles per day. If they don't and you're noticing slow queries, try a more broad partition key value, like `organization_id` or `published_month`.
252+
253+
A maximum of 4 partition key columns can be declared in a `vec0` virtual table, but use caution if you find yourself using more than 1. Vectors are sharded along each unique combination, so over-sharding is more common with more partition key columns.
254+
255+
### Auxiliary Columns
256+
257+
Auxiliary columns store additional unindexed data separate from the internal vector index. They are meant for larger metadata that will never appear in a `WHERE` clause of a KNN query, eliminating the need for a separate `JOIN`.
258+
259+
Auxiliary columns are denoted by a `+` prefix in their column definition, like so:
260+
261+
```sql
262+
create virtual table vec_chunks using vec0(
263+
contents_embedding float[1024],
264+
+contents text
265+
);
266+
267+
select
268+
rowid,
269+
contents,
270+
distance
271+
from vec_chunks
272+
where contents_embedding match :query
273+
and k = 10;
274+
```
275+
276+
Here we store the text contents of each chunk in the `contents` auxiliary column. When we perform a KNN query, we can reference the `contents` column in the `SELECT` clause, to get the raw text contents of the most relevant chunks.
277+
278+
A similar approach can be used for image embeddings:
279+
280+
```sql
281+
create virtual table vec_image_chunks using vec0(
282+
image_embedding float[1024],
283+
+image blob
284+
);
285+
286+
select
287+
rowid,
288+
contents,
289+
distance
290+
from vec_chunks
291+
where contents_embedding match :query
292+
and k = 10;
293+
```
294+
295+
Here the `image` auxiliary column can store the raw image file in a large `BLOB` column. It can appear in the `SELECT` clause of the KNN query, to get the most relevant raw images.
296+
297+
In general, auxiliary columns are good for large text, blobs, URLs, or other datatypes that won't be a part of a `WHERE` clause of a KNN query. If you column will often appear in a `SELECT` clause but not the `WHERE` clause, then auxiliary columns are a good fit.
298+
299+
A maximum of 16 auxiliary columns can be declared in a `vec0` virtual table.

sqlite-vec.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3411,7 +3411,6 @@ struct vec0_vtab {
34113411

34123412
uint8_t user_column_idxs[VEC0_MAX_VECTOR_COLUMNS + VEC0_MAX_PARTITION_COLUMNS + VEC0_MAX_AUXILIARY_COLUMNS];
34133413

3414-
34153414
// Name of all the vector chunk shadow tables.
34163415
// Ex '_vector_chunks00'
34173416
// Only the first numVectorColumns entries will be available.

0 commit comments

Comments
 (0)