Définir votre modèle de données

Microsoft.Extensions.VectorData utilise une approche de premier modèle pour interagir avec les bases de données.

Toutes les méthodes pour upsert ou obtenir des enregistrements utilisent des classes de modèle fortement typées. Il existe deux façons de définir le modèle de données :

  • En décorant des propriétés sur les classes de modèle avec des attributs qui indiquent l’objectif de chaque propriété.
  • En définissant votre schéma de stockage à l’aide d’une définition d’enregistrement que vous fournissez séparément du modèle de données. La définition d’enregistrement est une VectorStoreCollectionDefinition propriété qui contient des propriétés.

Voici un exemple de classe ou de modèle de données dont les propriétés sont décorées avec VectorStore*Attribute des attributs.

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; }
}

Propriétés du modèle de données

Note

Les types de propriétés .NET pris en charge pour les clés, les données et les vecteurs varient selon les bases de données. Pour plus d’informations sur les types pris en charge, consultez la documentation de votre fournisseur de magasin de vecteurs choisi.

Propriété clé

Chaque modèle de données doit avoir une propriété clé qui identifie de manière unique chaque enregistrement de la collection.

Utilisez l’attribut VectorStoreKeyAttribute pour indiquer que votre propriété est la clé primaire de l’enregistrement.

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

Le tableau suivant présente les paramètres pour VectorStoreKeyAttribute.

Paramètre Obligatoire Description
IsAutoGenerated Non Indique si la valeur de clé est générée automatiquement par la base de données. La valeur par défaut est false.
StorageName Non Peut être utilisé pour fournir un autre nom pour la propriété dans la base de données. Ce paramètre n’est pas pris en charge par tous les fournisseurs, par exemple, où des alternatives comme JsonPropertyNameAttribute celles-ci sont prises en charge.

Propriété de données

Les propriétés de données contiennent du contenu universel, tel que du texte, des balises ou d’autres métadonnées récupérées lors de la recherche d’enregistrements, et peuvent également être indexées pour le filtrage.

Utilisez l’attribut VectorStoreDataAttribute pour indiquer que votre propriété contient des données générales qui ne sont pas une clé ou un vecteur.

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

Le tableau suivant présente les paramètres pour VectorStoreDataAttribute.

Paramètre Obligatoire Description
IsIndexed Non Indique si la propriété doit être indexée pour le filtrage dans les cas où une base de données nécessite d’opter pour l’indexation par propriété. La valeur par défaut est false.
IsFullTextIndexed Non Indique si la propriété doit être indexée pour la recherche en texte intégral pour les bases de données qui prennent en charge la recherche en texte intégral. La valeur par défaut est false.
StorageName Non Peut être utilisé pour fournir un autre nom pour la propriété dans la base de données. Ce paramètre n’est pas pris en charge par tous les fournisseurs, par exemple, où des alternatives comme JsonPropertyNameAttribute celles-ci sont prises en charge.

Vector, propriété

Les propriétés de vecteur contiennent les vecteurs d’incorporation utilisés pour la recherche de similarité ; dans les scénarios avancés, un modèle de données peut avoir plusieurs propriétés vectorielles pour prendre en charge la recherche sur différents aspects de l’enregistrement.

Utilisez l’attribut VectorStoreVectorAttribute pour indiquer que votre propriété contient un vecteur.

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

Il est également possible d’utiliser VectorStoreVectorAttribute sur les propriétés qui n’ont pas de type vecteur, par exemple une propriété de type string. Lorsqu’une propriété est décorée de cette façon, vous devez fournir une IEmbeddingGenerator instance au magasin vectoriel. Lors de l’upserting de l’enregistrement, le texte qui se trouve dans la string propriété est automatiquement converti et stocké en tant que vecteur dans la base de données. (Il n’est pas possible de récupérer un vecteur à l’aide de ce mécanisme.)

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

Conseil / Astuce

Pour plus d’informations sur l’utilisation de la génération d’incorporation intégrée, consultez Les propriétés Vector et la génération d’incorporation.

Le tableau suivant présente les paramètres pour VectorStoreVectorAttribute.

Paramètre Obligatoire Description
Dimensions Oui Nombre de dimensions dont le vecteur a. Cela est nécessaire lors de la création d’un index vectoriel pour une collection.
IndexKind Non Type d’index avec lequel indexer le vecteur. La valeur par défaut varie selon le type de magasin de vecteurs.
DistanceFunction Non Type de fonction à utiliser lors de la comparaison de vecteurs pendant la recherche vectorielle sur ce vecteur. La valeur par défaut varie selon le type de magasin de vecteurs.
StorageName Non Peut être utilisé pour fournir un autre nom pour la propriété dans la base de données. Ce paramètre n’est pas pris en charge par tous les fournisseurs, par exemple, où des alternatives comme JsonPropertyNameAttribute celles-ci sont prises en charge.

Les types de fonctions d’index courants et de distance sont fournis sous forme de valeurs statiques sur les classes et IndexKind les DistanceFunction classes. Les implémentations de magasin de vecteurs individuels peuvent également utiliser leurs propres types d’index et fonctions de distance, où la base de données prend en charge des types inhabituels.

Propriétés vectorielles et génération d’incorporation

Les bases de données vectorielles concernent le stockage des incorporations ( ou des représentations numériques de vos données) qui sont générées par un modèle d’incorporation. Lors du stockage ou de la recherche de données, la génération d’incorporation doit d’abord être effectuée pour convertir les données pouvant faire l’objet d’une recherche en ces incorporations. MEVD fournit deux approches pour incorporer la génération : manuelle et automatique.

Génération manuelle d’incorporation de bas niveau

Vous pouvez définir votre propriété vectorielle en tant que float[] ou ReadOnlyMemory<float>, représentant directement l’incorporation et générer des incorporations vous-même avant chaque opération :

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

Lorsque vous effectuez une recherche, vous générez l’incorporation de votre texte de requête et passez-la à SearchAsync:

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

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

Bien que cela fonctionne, vous devez gérer la génération d’incorporation sur chaque site d’appel.

L’approche recommandée consiste à configurer une IEmbeddingGenerator<TInput,TEmbedding> valeur sur votre magasin vectoriel. Cela vous permet de définir votre propriété vectorielle à l’aide du type source (par exemple, string) au lieu de float[] ou ReadOnlyMemory<float>. MEVD gère ensuite la génération d’incorporation automatiquement pendant les opérations upsert et de recherche.

Tout d’abord, définissez la propriété vectorielle comme stringsuit :

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

Ensuite, configurez un générateur d’incorporation lors de la création de votre magasin vectoriel :

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

Vous pouvez maintenant passer du texte directement - MEVD génère des incorporations sous le capot :

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

Important

Les propriétés vectorielles configurées de cette façon ne prennent pas en charge la récupération du vecteur généré ou du texte d’origine de la base de données. Si vous devez stocker le texte d’origine, ajoutez une propriété de données distincte.

Les générateurs d’incorporation peuvent également être configurés au niveau de la collection, de la définition d’enregistrement ou de la propriété vectorielle individuelle. Différents modèles d’incorporation prennent en charge différentes tailles de vecteurs ; Vérifiez que la Dimensions valeur correspond au modèle que vous avez configuré. Pour plus d’informations sur l’incorporation de générateurs et les abstractions Microsoft.Extensions.AI, consultez Embeddings dans .NET.

Mappage dynamique à un dictionnaire .NET

Il existe des cas où il n'est pas souhaitable ou possible de mapper un type .NET fortement typé à la base de données. Par exemple, imaginez que vous ne savez pas au moment de la compilation ce que votre schéma de base de données ressemble, et que le schéma est fourni uniquement par le biais de la configuration. La création d’un type .NET qui reflète le schéma serait impossible dans ce cas. Au lieu de cela, vous pouvez mapper dynamiquement à l’aide d’un Dictionary<string, object?> type d’enregistrement. Les propriétés sont ajoutées à la Dictionary clé comme nom de propriété et à la valeur comme valeur de propriété.

Note

La plupart des applications utilisent simplement des types .NET fortement typés pour modéliser leurs données. Le mappage dynamique via Dictionary<string, object?> est destiné aux scénarios avancés de mappage de données arbitraires.

Fournir des informations de schéma lors de l’utilisation Dictionary

Lorsque vous utilisez un Dictionaryfournisseur, vous devez toujours savoir à quoi ressemble le schéma de base de données. Sans les informations de schéma, le fournisseur ne serait pas en mesure de créer une collection ou de savoir comment mapper vers et à partir de la représentation de stockage utilisée par chaque base de données.

Vous pouvez utiliser une définition d’enregistrement pour fournir les informations de schéma. Contrairement à un modèle de données, une définition d’enregistrement peut être créée à partir de la configuration au moment de l’exécution lorsque les informations de schéma ne sont pas connues au moment de la compilation.

Exemple

Pour utiliser Dictionary un fournisseur, spécifiez-le comme modèle de données lorsque vous créez la collection. Fournissez également une définition d’enregistrement.

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"]);