joelpaulkoch
Call SQL function when dumping custom Ecto type with SQLite and virtual table
I have a custom Ecto type and want to call a SQL function when dumping a value of this type into a virtual table with SQLite.
I’m developing this library to use sqlite-vec with Elixir.
There are 3 vector types in sqlite-vec: float32, int8, and bit.
The most performant way to use sqlite-vec is to create a virtual table.
Then you can create vectors with SQL functions as constructors.
So, in SQL that would be something like this:
CREATE VIRTUAL TABLE vt USING vec0(id INTEGER PRIMARY KEY, embedding int8[2])
INSERT INTO vt(id, embedding) VALUES(1, vec_int8('[1, 2]'))
I’ve created a custom Ecto type for each of the three vector types, e.g. here for int8:
defmodule SqliteVec.Ecto.Int8 do
@moduledoc """
`Ecto.Type` for `SqliteVec.Int8`
"""
use Ecto.Type
def type, do: :binary
def cast(value) do
{:ok, SqliteVec.Int8.new(value)}
end
def load(data) do
{:ok, SqliteVec.Int8.from_binary(data)}
end
def dump(%SqliteVec.Int8{} = vector) do
{:ok, SqliteVec.Int8.to_binary(vector)}
end
def dump(_), do: :error
end
What I want to achieve is inserting vectors with regular Ecto.Repo functions.
For that, I define a schema:
defmodule VT do
use Ecto.Schema
schema "vt" do
field(:embedding, SqliteVec.Ecto.Int8)
end
end
And now I want to insert using
MyApp.Repo.insert(%VT{
embedding: SqliteVec.Int8.new([1, 2])
})
The problem is that this fails as I’m dumping the binary data directly instead of calling the vec_int8 constructor function.
Actually, sqlite-vec interprets plain binary data as float32 (as if I would call the vec_f32 constructor function).
So it works for float32 vectors without constructor function but fails for int8 and bit vectors because the types don’t match.
What I’ve tried:
- parameterized types, I don’t think they add any capabilities that would work here
- changing the type of the custom type from
:binaryto:stringand dump a string of the constructor function with interpolated arguments, e.g. “vec_int8(‘[1, 2]’)”
I also had a look at ecto_sqlite3 as I’m assuming that the correct place for this functionality would be the adapter, and in particular the codec.
I guess I would need a way to define my own codec for the custom types, and call the constructor function there, not sure if that would work though.
Thanks in advance for any input!
First Post!
rhcarvalho
I’m not sure it would work in this context, but have you considered using Ecto’s fragment/1?
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #javascript
- #podcasts
- #code-sync
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance








