SQL Server EF Core プロバイダーでのベクター検索

ベクターのサポートは EF Core 10.0 で導入され、SQL Server 2025 以降でのみサポートされています。

SQL Server ベクター データ型を使用すると、embeddings を格納できます。これは、類似性を効率的に検索できる意味を表し、セマンティック検索や取得拡張生成 (RAG) などの AI ワークロードを強化します。

ベクター プロパティの設定

vector データ型を使用するには、エンティティ型に SqlVector<float> 型の.NET プロパティを追加し、次のようにディメンションを指定します。

public class Blog
{
    // ...

    [Column(TypeName = "vector(1536)")]
    public SqlVector<float> Embedding { get; set; }
}

プロパティが追加され、対応する列がデータベースに作成されたら、埋め込みの挿入を開始できます。 埋め込み生成は、通常はサービスを介してデータベースの外部で行われます。このドキュメントでは、これを行うための詳細は範囲外です。 ただし、.NET Microsoft.Extensions.AI ライブラリには IEmbeddingGenerator が含まれています。これは、主要なプロバイダーの実装を持つジェネレーターを埋め込む抽象化です。

埋め込みジェネレーターを選択して設定したら、それを使用して埋め込みを生成し、次のように挿入します。

IEmbeddingGenerator<string, Embedding<float>> embeddingGenerator = /* Set up your preferred embedding generator */;

var embedding = await embeddingGenerator.GenerateVectorAsync("Some text to be vectorized");
context.Blogs.Add(new Blog
{
    Name = "Some blog",
    Embedding = new SqlVector<float>(embedding)
});
await context.SaveChangesAsync();

データベースに埋め込みを保存したら、ベクター類似度検索を実行する準備が整います。

EF Core 11 以降では、ベクタープロパティはエンティティのクエリ時に既定では読み込まれません。ベクターは通常は大きく、読み取り返す必要はほとんどないためです。 EF Core 11 より前では、ベクター プロパティは常に他のプロパティと同様に読み込まれていました。

VECTOR_DISTANCE() を使用した正確な検索

EF.Functions.VectorDistance()関数は、2 つのベクトル間の正確な距離を計算します。 これを使用して、特定のユーザー クエリの類似性検索を実行します。

var sqlVector = new SqlVector<float>(await embeddingGenerator.GenerateVectorAsync("Some user query to be vectorized"));
var topSimilarBlogs = await context.Blogs
    .OrderBy(b => EF.Functions.VectorDistance("cosine", b.Embedding, sqlVector))
    .Take(3)
    .ToListAsync();

この関数は、クエリ ベクターとテーブル内のすべての行の間の距離を計算し、最も近い一致を返します。 これにより完全に正確な結果が得られますが、SQL Serverでは各行のすべての行をスキャンし、距離を計算する必要があるため、大規模なデータセットでは低速になる可能性があります。

EF 10 の組み込みサポートでは、以前の EFCore.SqlServer.VectorSearch 拡張機能が置き換えられました。この拡張機能では、 vector データ型が導入される前にベクター検索を実行できます。 EF 10 へのアップグレードの一環として、プロジェクトから拡張機能を削除します。

Warnung

VECTOR_SEARCH() インデックスとベクター インデックスは現在、SQL Serverの試験的な特徴であり、変更される可能性があります。 これらの機能の EF Core の API も変更される可能性があります。

SQL Serverの VECTOR_SEARCH() テーブル値関数は、ベクトルの類似性に基づいて行を取得します。 2 つの特定のベクトル間の距離を計算する VECTOR_DISTANCE() とは異なり、 VECTOR_SEARCH() は、特定のクエリ ベクターと最も類似したベクトルをテーブル全体で検索します。

VectorSearch()DbSet拡張メソッドを使用し、OrderBy()Take()、およびWithApproximate()をチェーンして、ベクトル インデックスを使用する近似最近傍 (ANN) 検索を実行します。

var results = await context.Blogs
    .VectorSearch(b => b.Embedding, embedding, "cosine")
    .OrderBy(r => r.Distance)
    .Take(5)
    .WithApproximate()
    .ToListAsync();

foreach (var result in results)
{
    Console.WriteLine($"Blog {result.Value.Id} with distance {result.Distance}");
}

これは次の SQL に変換されます。

SELECT TOP(@__p_1) WITH APPROXIMATE [b].[Id], [b].[Name], [v].[Distance]
FROM VECTOR_SEARCH(
    TABLE = [Blogs] AS [b],
    COLUMN = [Embedding],
    SIMILAR_TO = @__embedding_0,
    METRIC = 'cosine'
) AS [v]
ORDER BY [v].[Distance]

VectorSearch()VectorSearchResult<TEntity>を返します。これにより、エンティティと計算された距離の両方にアクセスできます。

var searchResults = await context.Blogs
    .VectorSearch(b => b.Embedding, embedding, "cosine")
    .Where(r => r.Distance < 0.05)
    .OrderBy(r => r.Distance)
    .Select(r => new { Blog = r.Value, Distance = r.Distance })
    .Take(3)
    .WithApproximate()
    .ToListAsync();

これにより、類似性スコアをフィルター処理し、ユーザーに提示することができます。

WithApproximate()

WithApproximate() は、近似最近傍 (ANN) 検索にベクター インデックスを使用するようにSQL Serverに指示します。これにより、大規模なデータセットのパフォーマンスが大幅に向上します。 これにより、 WITH APPROXIMATE が SQL TOP 句に追加されます。 WithApproximate() は、返される結果の数を指定する Take()の後に呼び出す必要があります。

WithApproximate()を使用しない場合、クエリでは、ベクトル インデックスを使用せずに、すべての行をスキャンする正確な k ニアレスト ネイバー (kNN) 検索が実行されます。

// Exact kNN search (no vector index used)
var blogs = await context.Blogs
    .VectorSearch(b => b.Embedding, embedding, "cosine")
    .OrderBy(r => r.Distance)
    .Take(5)
    .ToListAsync();

ベクトル インデックス

WithApproximate()で近似検索を使用するには、ベクター列にベクター インデックスを作成する必要があります。 モデル構成で HasVectorIndex() メソッドを使用します。

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasVectorIndex(b => b.Embedding, "cosine");
}

これにより、次の SQL 移行が生成されます。

CREATE VECTOR INDEX [IX_Blogs_Embedding]
    ON [Blogs] ([Embedding])
    WITH (METRIC = COSINE)

ベクター インデックスでは、次の距離メトリックがサポートされています。

メトリクス 説明
cosine コサインの類似性 (角度の距離)
euclidean ユークリッド距離 (L2 ノルム)
dot ドット積 (負の内積)

埋め込みモデルとユース ケースに最適なメトリックを選択します。 コサインの類似性はテキスト埋め込みでよく使用されますが、ユークリッド距離は画像埋め込みでよく使用されます。

ハイブリッド検索 では、ベクター類似性検索と従来 のフルテキスト検索 を組み合わせて、より関連性の高い結果を提供します。 ベクター検索は意味的に似たコンテンツを見つけるのに優れていますが、フルテキスト検索は正確なキーワードマッチングの方が優れています。 両方のアプローチを組み合わせ、逆ランク 融合 (RRF) を使用して結果をマージすることで、よりインテリジェントな検索エクスペリエンスを構築できます。

次の例では、EF Core を使用してハイブリッド検索を実装し、 FreeTextTable()VectorSearch() を 1 つのクエリで組み合わせる方法を示します。

var k = 20;
string textualQuery = ...;
SqlVector<float> queryEmbedding = ...;

var results = await context.Articles
    // Perform full-text search
    .FreeTextTable<Article, int>(textualQuery, topN: k)
    // Perform vector (semantic) search, joining the results of both searches together
    .LeftJoin(
        context.Articles.VectorSearch(b => b.Embedding, queryEmbedding, "cosine")
            .OrderBy(r => r.Distance)
            .Take(k)
            .WithApproximate(),
        fts => fts.Key,
        vs => vs.Value.Id,
        (fts, vs) => new
        {
            Article = vs.Value,
            FullTextRank = fts.Rank,
            VectorDistance = (double?)vs.Distance
        })
    // Apply Reciprocal Rank Fusion (RRF) to combine the results
    .Select(x => new
    {
        x.Article,
        RrfScore = (1.0 / (k + x.FullTextRank)) + (1.0 / (k + x.VectorDistance) ?? 0.0)
    })
    .OrderByDescending(x => x.RrfScore)
    .Take(10)
    .Select(x => x.Article)
    .ToListAsync();

このクエリ:

  1. フルテキスト検索を実行します。 Article
  2. Articleに対してベクター検索を実行し、LEFT JOIN を使用して結果をフルテキスト検索結果に結合します。
  3. フルテキストとセマンティック ランク付けの両方を組み合わせて RRF スコアを計算します
  4. RRF スコア順に並べ替え、目的の数の結果を取得し、元の Article エンティティを投影します。

LEFT JOIN を使用する代わりに、FULL OUTER JOIN がこのシナリオに適しています。これにより、結果が一方の側にまったく表示されない場合でも、どちらの検索側からの高位の結果も最終的な結果に含められます。 上記の LEFT JOIN アプローチでは、結果にベクターの類似性スコアが非常に高い場合、その結果にフルテキスト スコアも高くない場合、最終的な結果には含まれません。 ただし、EF では現在、FULL OUTER JOIN はサポートされていません。この機能のサポートを希望する場合は、#37633 に投票してください。

このクエリでは、次の SQL が生成されます。

SELECT TOP(@__p_4) [a0].[Id], [a0].[Content], [a0].[Title]
FROM FREETEXTTABLE([Articles], *, @__textualQuery_0, @__k_1) AS [f]
LEFT JOIN (
    SELECT TOP(@__k_1) WITH APPROXIMATE [a].[Id], [a].[Content], [a].[Title], [v].[Distance]
    FROM VECTOR_SEARCH(
        TABLE = [Articles] AS [a],
        COLUMN = [Embedding],
        SIMILAR_TO = @__queryEmbedding_2,
        METRIC = 'cosine'
    ) AS [v]
    ORDER BY [v].[Distance]
) AS [t] ON [f].[KEY] = [t].[Id]
ORDER BY 1.0E0 / CAST(@__k_1 + [f].[RANK] AS float) + ISNULL(1.0E0 / (CAST(@__k_1 AS float) + [t].[Distance]), 0.0E0) DESC