/pgvector-dotnet

pgvector support for .NET (C#, F#, and Visual Basic)

Primary LanguageC#MIT LicenseMIT

pgvector-dotnet

pgvector support for .NET (C#, F#, and Visual Basic)

Supports Npgsql, Dapper, Entity Framework Core, and Npgsql.FSharp

Build Status

Getting Started

Follow the instructions for your database library:

Or check out an example:

Npgsql (C#)

Run

dotnet add package Pgvector

Create a connection

var dataSourceBuilder = new NpgsqlDataSourceBuilder(connString);
dataSourceBuilder.UseVector();
await using var dataSource = dataSourceBuilder.Build();

var conn = dataSource.OpenConnection();

Enable the extension

await using (var cmd = new NpgsqlCommand("CREATE EXTENSION IF NOT EXISTS vector", conn))
{
    await cmd.ExecuteNonQueryAsync();
}

conn.ReloadTypes();

Create a table

await using (var cmd = new NpgsqlCommand("CREATE TABLE items (id serial PRIMARY KEY, embedding vector(3))", conn))
{
    await cmd.ExecuteNonQueryAsync();
}

Insert a vector

await using (var cmd = new NpgsqlCommand("INSERT INTO items (embedding) VALUES ($1)", conn))
{
    var embedding = new Vector(new float[] { 1, 1, 1 });
    cmd.Parameters.AddWithValue(embedding);
    await cmd.ExecuteNonQueryAsync();
}

Get the nearest neighbors

await using (var cmd = new NpgsqlCommand("SELECT * FROM items ORDER BY embedding <-> $1 LIMIT 5", conn))
{
    var embedding = new Vector(new float[] { 1, 1, 1 });
    cmd.Parameters.AddWithValue(embedding);

    await using (var reader = await cmd.ExecuteReaderAsync())
    {
        while (await reader.ReadAsync())
        {
            Console.WriteLine(reader.GetValue(0));
        }
    }
}

Add an approximate index

await using (var cmd = new NpgsqlCommand("CREATE INDEX ON items USING ivfflat (embedding vector_l2_ops) WITH (lists = 100)", conn))
{
    await cmd.ExecuteNonQueryAsync();
}

Use vector_ip_ops for inner product and vector_cosine_ops for cosine distance

See a full example

Dapper

Run

dotnet add package Pgvector.Dapper

Import the library

using Pgvector.Dapper;

Create a connection

SqlMapper.AddTypeHandler(new VectorTypeHandler());

var dataSourceBuilder = new NpgsqlDataSourceBuilder(connString);
dataSourceBuilder.UseVector();
await using var dataSource = dataSourceBuilder.Build();

var conn = dataSource.OpenConnection();

Enable the extension

conn.Execute("CREATE EXTENSION IF NOT EXISTS vector");
conn.ReloadTypes();

Define a class

public class Item
{
    public int Id { get; set; }
    public Vector? Embedding { get; set; }
}

Create a table

conn.Execute("CREATE TABLE items (id serial PRIMARY KEY, embedding vector(3))");

Insert a vector

var embedding = new Vector(new float[] { 1, 1, 1 });
conn.Execute(@"INSERT INTO items (embedding) VALUES (@embedding)", new { embedding });

Get the nearest neighbors

var embedding = new Vector(new float[] { 1, 1, 1 });
var items = conn.Query<Item>("SELECT * FROM items ORDER BY embedding <-> @embedding LIMIT 5", new { embedding });
foreach (Item item in items)
{
    Console.WriteLine(item.Embedding);
}

Add an approximate index

conn.Execute("CREATE INDEX ON items USING ivfflat (embedding vector_l2_ops) WITH (lists = 100)");
// or
conn.Execute("CREATE INDEX ON items USING hnsw (embedding vector_l2_ops)");

Use vector_ip_ops for inner product and vector_cosine_ops for cosine distance

See a full example

Entity Framework Core

Run

dotnet add package Pgvector.EntityFrameworkCore

The latest version works with .NET 8. For .NET 6 and 7, use version 0.1.2 and this readme.

Import the library

using Pgvector.EntityFrameworkCore;

Enable the extension

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.HasPostgresExtension("vector");
}

Configure the connection

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder.UseNpgsql("connString", o => o.UseVector());
}

Define a model

public class Item
{
    public int Id { get; set; }

    [Column(TypeName = "vector(3)")]
    public Vector? Embedding { get; set; }
}

Insert a vector

ctx.Items.Add(new Item { Embedding = new Vector(new float[] { 1, 1, 1 }) });
ctx.SaveChanges();

Get the nearest neighbors

var embedding = new Vector(new float[] { 1, 1, 1 });
var items = await ctx.Items
    .OrderBy(x => x.Embedding!.L2Distance(embedding))
    .Take(5)
    .ToListAsync();

foreach (Item item in items)
{
    if (item.Embedding != null)
    {
        Console.WriteLine(item.Embedding);
    }
}

Also supports MaxInnerProduct and CosineDistance

Get the distance

var items = await ctx.Items
    .Select(x => new { Entity = x, Distance = x.Embedding!.L2Distance(embedding) })
    .ToListAsync();

Get items within a certain distance

var items = await ctx.Items
    .Where(x => x.Embedding!.L2Distance(embedding) < 5)
    .ToListAsync();

Add an approximate index

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Item>()
        .HasIndex(i => i.Embedding)
        .HasMethod("ivfflat")
        .HasOperators("vector_l2_ops")
        .HasStorageParameter("lists", 100);
    // or
    modelBuilder.Entity<Item>()
        .HasIndex(i => i.Embedding)
        .HasMethod("hnsw")
        .HasOperators("vector_l2_ops")
        .HasStorageParameter("m", 16)
        .HasStorageParameter("ef_construction", 64);
}

Use vector_ip_ops for inner product and vector_cosine_ops for cosine distance

See a full example

Npgsql.FSharp

Run

dotnet add package Pgvector

Import the library

open Pgvector

Create a connection

let dataSourceBuilder = new NpgsqlDataSourceBuilder(connString)
dataSourceBuilder.UseVector()
use dataSource = dataSourceBuilder.Build()

Enable the extension

dataSource
|> Sql.fromDataSource
|> Sql.query "CREATE EXTENSION IF NOT EXISTS vector"
|> Sql.executeNonQuery

Create a table

dataSource
|> Sql.fromDataSource
|> Sql.query "CREATE TABLE items (id serial PRIMARY KEY, embedding vector(3))"
|> Sql.executeNonQuery

Insert a vector

let embedding = new Vector([| 1f; 1f; 1f |])
let parameter = new NpgsqlParameter("", embedding)

dataSource
|> Sql.fromDataSource
|> Sql.query "INSERT INTO items (embedding) VALUES (@embedding)"
|> Sql.parameters [ "embedding", Sql.parameter parameter ]
|> Sql.executeNonQuery

Get the nearest neighbors

type Item = {
    Id: int
    Embedding: Vector
}

dataSource
|> Sql.fromDataSource
|> Sql.query "SELECT * FROM items ORDER BY embedding <-> @embedding LIMIT 5"
|> Sql.parameters [ "embedding", Sql.parameter parameter ]
|> Sql.execute (fun read ->
    {
        Id = read.int "id"
        Embedding = read.fieldValue<Vector> "embedding"
    })
|> printfn "%A"

Add an approximate index

dataSource
|> Sql.fromDataSource
|> Sql.query "CREATE INDEX ON items USING hnsw (embedding vector_l2_ops)"
|> Sql.executeNonQuery

Use vector_ip_ops for inner product and vector_cosine_ops for cosine distance

See a full example

Npgsql (Visual Basic)

Run

dotnet add package Pgvector

Create a connection

Dim dataSourceBuilder As New NpgsqlDataSourceBuilder(connString)
dataSourceBuilder.UseVector()
Dim dataSource = dataSourceBuilder.Build()

Dim conn = dataSource.OpenConnection()

Enable the extension

Using cmd As New NpgsqlCommand("CREATE EXTENSION IF NOT EXISTS vector", conn)
    cmd.ExecuteNonQuery()
End Using

conn.ReloadTypes()

Create a table

Using cmd As New NpgsqlCommand("CREATE TABLE items (id serial PRIMARY KEY, embedding vector(3))", conn)
    cmd.ExecuteNonQuery()
End Using

Insert a vector

Using cmd As New NpgsqlCommand("INSERT INTO items (embedding) VALUES ($1)", conn)
    Dim embedding As New Vector(New Single() {1, 1, 1})
    cmd.Parameters.AddWithValue(embedding)
    cmd.ExecuteNonQuery()
End Using

Get the nearest neighbors

Using cmd As New NpgsqlCommand("SELECT * FROM items ORDER BY embedding <-> $1 LIMIT 5", conn)
    Dim embedding As New Vector(New Single() {1, 1, 1})
    cmd.Parameters.AddWithValue(embedding)

    Using reader As NpgsqlDataReader = cmd.ExecuteReader()
        While reader.Read()
            Console.WriteLine(reader.GetValue(0))
        End While
    End Using
End Using

Add an approximate index

Using cmd As New NpgsqlCommand("CREATE INDEX ON items USING hnsw (embedding vector_l2_ops)", conn)
    cmd.ExecuteNonQuery()
End Using

Use vector_ip_ops for inner product and vector_cosine_ops for cosine distance

See a full example

History

Contributing

Everyone is encouraged to help improve this project. Here are a few ways you can help:

To get started with development:

git clone https://github.com/pgvector/pgvector-dotnet.git
cd pgvector-dotnet
createdb pgvector_dotnet_test
dotnet test