Skip to content

Commit 27600e0

Browse files
committed
Add match macro to perform KNN query
1 parent 522865b commit 27600e0

File tree

5 files changed

+79
-1
lines changed

5 files changed

+79
-1
lines changed

lib/sqlite_vec/ecto/query.ex

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,15 @@ if Code.ensure_loaded?(Ecto) do
3131
end
3232
end
3333

34+
@doc """
35+
Performs a K-nearest-neighbors (KNN) query. You must specify a LIMIT or 'k = ?' constraint.
36+
"""
37+
defmacro match(left, right) do
38+
quote do
39+
fragment("? match ?", unquote(left), unquote(right))
40+
end
41+
end
42+
3443
@doc """
3544
Calculates the L2 euclidian distance between vectors a and b. Only valid for float32 or int8 vectors.
3645

notebooks/usage_with_ecto.livemd

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,8 @@ MyApp.Repo.insert(%VirtualEmbedding{
160160
})
161161
```
162162

163+
You can perform a K-nearest-neighbors query using `match` and `limit`.
164+
163165
```elixir
164166
import Ecto.Query
165167
import SqliteVec.Ecto.Query
@@ -168,7 +170,8 @@ v = SqliteVec.Float32.new([2, 2])
168170

169171
MyApp.Repo.all(
170172
from(i in VirtualEmbedding,
171-
where: fragment("? match ? and k=?", i.embedding, vec_f32(v), 3)
173+
where: match(i.embedding, vec_f32(v)),
174+
limit: 3
172175
)
173176
)
174177
```

test/sqlite_vec/bit_ecto_test.exs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,30 @@ defmodule BitEctoTest do
3939
)
4040
end
4141

42+
test "match performs a KNN query" do
43+
items =
44+
Repo.all(
45+
from(i in BitItem,
46+
where:
47+
match(
48+
i.embedding,
49+
vec_bit(SqliteVec.Bit.new([0, 0, 0, 0, 0, 0, 0, 1]))
50+
),
51+
limit: 3
52+
)
53+
)
54+
55+
assert Enum.map(items, fn v ->
56+
v.id
57+
end) == [2, 3, 1]
58+
59+
assert Enum.map(items, fn v -> v.embedding |> SqliteVec.Bit.to_list() end) == [
60+
[0, 0, 0, 0, 0, 0, 0, 0],
61+
[0, 0, 0, 0, 1, 0, 1, 0],
62+
[1, 1, 1, 1, 1, 1, 1, 1]
63+
]
64+
end
65+
4266
test "vector hamming distance" do
4367
items =
4468
Repo.all(

test/sqlite_vec/float32_ecto_test.exs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,28 @@ defmodule EctoTest do
4545
})
4646
end
4747

48+
test "match performs a KNN query" do
49+
v = SqliteVec.Float32.new([2, 2])
50+
51+
items =
52+
Repo.all(
53+
from(i in Float32Item,
54+
where: match(i.embedding, vec_f32(v)),
55+
limit: 3
56+
)
57+
)
58+
59+
assert Enum.map(items, fn v ->
60+
v.id
61+
end) == [1, 3, 2]
62+
63+
assert Enum.map(items, fn v -> v.embedding |> SqliteVec.Float32.to_list() end) == [
64+
[1.0, 2.0],
65+
[3.0, 4.0],
66+
[52.0, 43.0]
67+
]
68+
end
69+
4870
test "vector l2 distance" do
4971
v = SqliteVec.Float32.new([2, 2])
5072

test/sqlite_vec/int8_ecto_test.exs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,26 @@ defmodule Int8EctoTest do
7171
]
7272
end
7373

74+
test "match performs a KNN query" do
75+
items =
76+
Repo.all(
77+
from(i in Int8Item,
78+
where: match(i.embedding, vec_int8(SqliteVec.Int8.new([2, 2]))),
79+
limit: 3
80+
)
81+
)
82+
83+
assert Enum.map(items, fn v ->
84+
v.id
85+
end) == [1, 3, 2]
86+
87+
assert Enum.map(items, fn v -> v.embedding |> SqliteVec.Int8.to_list() end) == [
88+
[1, 2],
89+
[3, 4],
90+
[52, 43]
91+
]
92+
end
93+
7494
test "vector cosine distance" do
7595
items =
7696
Repo.all(

0 commit comments

Comments
 (0)