Definiowanie modelu danych

Microsoft.Extensions.VectorData używa podejścia opartego na modelu do interakcji z bazami danych.

Wszystkie metody operacji upsert lub pobierania rekordów używają silnie typiowanych klas modeli. Istnieją dwa sposoby definiowania modelu danych:

  • Dekorując właściwości klas modelu z atrybutami wskazującymi przeznaczenie każdej właściwości.
  • Definiując schemat magazynu przy użyciu definicji rekordu , która jest dostarczana oddzielnie od modelu danych. Definicja rekordu VectorStoreCollectionDefinition to element zawierający właściwości.

Oto przykład klasy lub modelu danych, którego właściwości są ozdobione atrybutami VectorStore*Attribute .

public class Hotel
{
    [VectorStoreKey]
    public ulong HotelId { get; set; }

    [VectorStoreData(IsIndexed = true)]
    public required string HotelName { get; set; }

    [VectorStoreData(IsFullTextIndexed = true)]
    public required string Description { get; set; }

    [VectorStoreVector(Dimensions: 4, DistanceFunction = DistanceFunction.CosineSimilarity, IndexKind = IndexKind.Hnsw)]
    public ReadOnlyMemory<float>? DescriptionEmbedding { get; set; }

    [VectorStoreData(IsIndexed = true)]
    public required string[] Tags { get; set; }
}

Właściwości modelu danych

Uwaga / Notatka

Typy właściwości .NET obsługiwane dla kluczy, danych i wektorów różnią się w różnych bazach danych. Aby uzyskać informacje na temat obsługiwanych typów, zapoznaj się z dokumentacją wybranego dostawcy magazynu wektorów.

Kluczowa właściwość

Każdy model danych musi mieć właściwość klucza, która unikatowo identyfikuje każdy rekord w kolekcji.

Użyj atrybutu VectorStoreKeyAttribute , aby wskazać, że właściwość jest kluczem podstawowym rekordu.

[VectorStoreKey]
public ulong HotelId { get; set; }

W poniższej tabeli przedstawiono parametry dla elementu VectorStoreKeyAttribute.

Parametr Required Opis
IsAutoGenerated Nie. Wskazuje, czy wartość klucza jest generowana automatycznie przez bazę danych. Wartość domyślna to false.
StorageName Nie. Może służyć do podawania alternatywnej nazwy właściwości w bazie danych. Ten parametr nie jest obsługiwany przez wszystkich dostawców, na przykład jeśli są obsługiwane alternatywne metody, takie jak JsonPropertyNameAttribute .

Właściwość danych

Właściwości danych przechowują zawartość ogólnego przeznaczenia, taką jak tekst, tagi lub inne metadane pobierane podczas wyszukiwania rekordów i mogą być również indeksowane do filtrowania.

Użyj atrybutu VectorStoreDataAttribute , aby wskazać, że właściwość zawiera ogólne dane, które nie są kluczem lub wektorem.

[VectorStoreData(IsIndexed = true)]
public required string HotelName { get; set; }

W poniższej tabeli przedstawiono parametry dla elementu VectorStoreDataAttribute.

Parametr Required Opis
IsIndexed Nie. Wskazuje, czy właściwość powinna być indeksowana do filtrowania w przypadkach, gdy baza danych wymaga zgody na indeksowanie poszczególnych właściwości. Wartość domyślna to false.
IsFullTextIndexed Nie. Wskazuje, czy właściwość powinna być indeksowana w celu wyszukiwania pełnotekstowego baz danych obsługujących wyszukiwanie pełnotekstowe. Wartość domyślna to false.
StorageName Nie. Może służyć do podawania alternatywnej nazwy właściwości w bazie danych. Ten parametr nie jest obsługiwany przez wszystkich dostawców, na przykład w przypadku, gdy są obsługiwane alternatywy, takie jak JsonPropertyNameAttribute .

Właściwość wektora

Właściwości wektorów zawierają wektory osadzania używane do wyszukiwania podobieństwa; w zaawansowanych scenariuszach model danych może mieć wiele właściwości wektorów do obsługi wyszukiwania w różnych aspektach rekordu.

Użyj atrybutu VectorStoreVectorAttribute , aby wskazać, że właściwość zawiera wektor.

[VectorStoreVector(Dimensions: 4, DistanceFunction = DistanceFunction.CosineSimilarity, IndexKind = IndexKind.Hnsw)]
public ReadOnlyMemory<float>? DescriptionEmbedding { get; set; }

Można również użyć VectorStoreVectorAttribute właściwości, które nie mają typu wektora, na przykład właściwości typu string. Gdy właściwość zostanie w ten sposób ozdobiona, musisz podać IEmbeddingGenerator wystąpienie do magazynu wektorów. Podczas upserting rekordu tekst, który znajduje się we string właściwości, jest automatycznie konwertowany i przechowywany jako wektor w bazie danych. (Nie można pobrać wektora przy użyciu tego mechanizmu).

[VectorStoreVector(Dimensions: 4, DistanceFunction = DistanceFunction.CosineSimilarity, IndexKind = IndexKind.Hnsw)]
public string DescriptionEmbedding { get; set; }

Wskazówka

Aby uzyskać więcej informacji na temat używania wbudowanej generacji osadzania, zobacz Właściwości wektora i generowanie osadzania.

W poniższej tabeli przedstawiono parametry dla elementu VectorStoreVectorAttribute.

Parametr Required Opis
Dimensions Yes Liczba wymiarów, które ma wektor. Jest to wymagane podczas tworzenia indeksu wektorowego dla kolekcji.
IndexKind Nie. Typ indeksu do indeksowania wektora. Wartość domyślna różni się w zależności od typu magazynu wektorów.
DistanceFunction Nie. Typ funkcji do użycia podczas porównywania wektorów podczas wyszukiwania wektorów w tym wektorze. Wartość domyślna różni się w zależności od typu magazynu wektorów.
StorageName Nie. Może służyć do podawania alternatywnej nazwy właściwości w bazie danych. Ten parametr nie jest obsługiwany przez wszystkich dostawców, na przykład jeśli są obsługiwane alternatywy, takie jak JsonPropertyNameAttribute .

Typowe rodzaje indeksów i typy funkcji odległości są dostarczane jako wartości statyczne w klasach IndexKind i DistanceFunction . Implementacje poszczególnych magazynów wektorów mogą również używać własnych typów indeksów i funkcji odległości, w których baza danych obsługuje nietypowe typy.

Właściwości wektorów i generowanie osadzania

Wektorowe bazy danych dotyczą przechowywania osadzania — lub liczbowych reprezentacji danych — które są generowane przez model osadzania. Podczas przechowywania lub wyszukiwania danych należy najpierw przeprowadzić generowanie osadzania, aby przekonwertować dane z możliwością wyszukiwania na takie osadzanie. Usługa MEVD oferuje dwa podejścia do generowania osadzania: ręczne i automatyczne.

Ręczne generowanie osadzania niskiego poziomu

Możesz zdefiniować właściwość wektora jako float[] lub ReadOnlyMemory<float>, reprezentującą osadzanie bezpośrednio i samodzielnie wygenerować osadzanie przed każdą operacją:

[VectorStoreVector(Dimensions: 1536)]
public ReadOnlyMemory<float>? DescriptionEmbedding { get; set; }

Podczas wyszukiwania wygenerujesz osadzanie dla tekstu zapytania i przekażesz go do elementu SearchAsync:

ReadOnlyMemory<float> searchEmbedding =
    (await embeddingGenerator.GenerateAsync("Find a happy hotel")).Vector;

var searchResult = collection.SearchAsync(searchEmbedding, top: 3);

Mimo że działa to, wymaga ona zarządzania generowaniem osadzania w każdej lokacji wywołań.

Zalecaną metodą jest skonfigurowanie IEmbeddingGenerator<TInput,TEmbedding> elementu w magazynie wektorów. Dzięki temu można zdefiniować właściwość wektora przy użyciu typu źródłowego (na przykład string) zamiast float[] lub ReadOnlyMemory<float>. Usługa MEVD automatycznie obsługuje generowanie osadzania zarówno podczas operacji upsert, jak i wyszukiwania.

Najpierw zdefiniuj właściwość wektora jako string:

[VectorStoreVector(Dimensions: 1536)]
public string DescriptionEmbedding { get; set; }

Następnie skonfiguruj generator osadzania podczas tworzenia magazynu wektorów:

VectorStore vectorStore = new QdrantVectorStore(
    new QdrantClient("localhost"),
    ownsClient: true,
    new QdrantVectorStoreOptions
    {
        EmbeddingGenerator = embeddingGenerator
    });

Teraz możesz przekazać tekst bezpośrednio — usługa MEVD generuje osadzanie pod maską:

// Search with a plain text query - embedding is generated automatically.
var searchResult = collection.SearchAsync("Find a happy hotel", top: 3);

Ważna

Właściwości wektorów skonfigurowane w ten sposób nie obsługują pobierania wygenerowanego wektora ani oryginalnego tekstu z bazy danych. Jeśli musisz przechowywać oryginalny tekst, dodaj oddzielną właściwość danych.

Generatory osadzania można również skonfigurować na poziomie kolekcji, definicji rekordu lub poszczególnych właściwości wektorów. Różne modele osadzania obsługują różne rozmiary wektorów; upewnij się, że wartość jest zgodna Dimensions ze skonfigurowanym modelem. Aby uzyskać więcej informacji na temat osadzania generatorów i abstrakcji Microsoft.Extensions.AI, zobacz Embeddings w .NET.

Dynamiczne mapowanie do słownika .NET

Istnieją przypadki, w których nie jest pożądane lub możliwe jest mapowanie silnie typizowanego .NET do bazy danych. Załóżmy na przykład, że w czasie kompilacji nie wiesz, jak wygląda schemat bazy danych, a schemat jest udostępniany tylko za pośrednictwem konfiguracji. Utworzenie typu .NET, który odzwierciedla schemat, byłoby niemożliwe w tym przypadku. Zamiast tego można mapować dynamicznie przy użyciu elementu dla Dictionary<string, object?> typu rekordu. Właściwości są dodawane do Dictionary elementu z kluczem jako nazwa właściwości i wartość jako wartość właściwości.

Uwaga / Notatka

Większość aplikacji będzie po prostu używać silnie typowanych .NET typów do modelowania danych. Dynamiczne mapowanie za pośrednictwem Dictionary<string, object?> jest przeznaczone dla zaawansowanych, dowolnych scenariuszy mapowania danych.

Podawanie informacji o schemacie podczas używania Dictionary

Jeśli używasz Dictionaryklasy , dostawcy nadal muszą wiedzieć, jak wygląda schemat bazy danych. Bez informacji o schemacie dostawca nie będzie mógł utworzyć kolekcji ani wiedzieć, jak mapować na i z reprezentacji magazynu używanej przez każdą bazę danych.

Aby podać informacje o schemacie, możesz użyć definicji rekordu. W przeciwieństwie do modelu danych definicję rekordu można utworzyć na podstawie konfiguracji w czasie wykonywania , gdy informacje o schemacie nie są znane w czasie kompilacji.

Przykład

Aby używać Dictionary z dostawcą, określ go jako model danych podczas tworzenia kolekcji. Podaj również definicję rekordu.

VectorStoreCollectionDefinition definition = new()
{
    Properties =
    [
        new VectorStoreKeyProperty("Key", typeof(string)),
        new VectorStoreDataProperty("Term", typeof(string)),
        new VectorStoreDataProperty("Definition", typeof(string)),
        new VectorStoreVectorProperty("DefinitionEmbedding", typeof(ReadOnlyMemory<float>), dimensions: 1536)
    ]
};

// Use GetDynamicCollection instead of the regular GetCollection method
// to get an instance of a collection using Dictionary<string, object?>.
VectorStoreCollection<object, Dictionary<string, object?>> dynamicDataModelCollection =
    vectorStore.GetDynamicCollection("glossary", definition);

// Since schema information is available from the record definition,
// it's possible to create a collection with the right vectors,
// dimensions, indexes, and distance functions.
await dynamicDataModelCollection.EnsureCollectionExistsAsync();

// When retrieving a record from the collection,
// access key, data, and vector values via the dictionary entries.
Dictionary<string, object?>? record = await dynamicDataModelCollection.GetAsync("SK");
Console.WriteLine(record["Definition"]);