Hi Mythz,
I’m implementing a custom type converter for PostgreSQL’s pgvector extension to store vector embeddings. The converter works for SELECT operations but fails on INSERT/UPDATE due to PostgreSQL’s type system requirements.
The Problem:
- PostgreSQL’s pgvector type requires explicit
::vector
casting in parameterized queries - OrmLite parameters are sent as text type
- PostgreSQL won’t implicitly cast text to vector, causing:
column is of type vector but expression is of type text
My PgVector Type:
public class PgVector
{
public float[] Values { get; set; }
public PgVector(float[] values)
{
Values = values;
}
public override string ToString()
{
if (Values == null || Values.Length == 0)
return "[]";
// Returns PostgreSQL vector format: "[0.1,0.2,0.3]"
var formattedValues = Values.Select(v => v.ToString("G9", CultureInfo.InvariantCulture));
return $"[{string.Join(",", formattedValues)}]";
}
}
My Converter:
public class PgVectorConverter : OrmLiteConverter
{
public override string ColumnDefinition => "vector";
public override DbType DbType => DbType.Object;
public override void InitDbParam(IDbDataParameter p, Type fieldType)
{
if (p is NpgsqlParameter npgsqlParam)
{
npgsqlParam.NpgsqlDbType = NpgsqlDbType.Unknown;
}
}
public override string ToQuotedString(Type fieldType, object value)
{
if (value == null) return "NULL";
var vector = value as PgVector;
if (vector == null) return "NULL";
return $"'{vector}'::vector"; // Works for raw SQL only
}
public override object ToDbValue(Type fieldType, object value)
{
if (value == null) return DBNull.Value;
if (value is PgVector vector)
return vector.ToString(); // Returns "[0.1,0.2,0.3]"
return DBNull.Value;
}
}
What Fails:
var result = new UrlScrapeResult
{
ContentVector = new PgVector(new float[] { 0.1f, 0.2f, 0.3f }),
// other properties...
};
// This fails with "column is of type vector but expression is of type text"
await db.InsertAsync(result);
What Works (Raw SQL):
await db.ExecuteSqlAsync(
"INSERT INTO url_scrape_result (content_vector) VALUES (@vector::vector)",
new { vector = pgVector.ToString() });
Question:
Is there a way to make the converter properly handle the pgvector type so that standard OrmLite Insert/Update operations work? Specifically:
- Can I force the parameter to include the
::vector
cast? - Is there a different approach to parameter typing I should use?
- Any other converter methods I should override?
I’d prefer to use standard OrmLite operations rather than raw SQL for every vector operation.
Thanks for any guidance!