通过


管理数据

定义数据模型并获取数据VectorStoreCollection<TKey,TRecord>模型后,即可管理矢量存储集合中的记录。 本文介绍核心数据管理操作:更新插入、删除和检索记录。 在矢量搜索中,我们介绍了如何使用矢量相似性搜索数据。

以下示例都使用以下数据模型,该模型使用 自动嵌入生成

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: 1536, DistanceFunction = DistanceFunction.CosineSimilarity)]
    public required string DescriptionEmbedding { get; set; }

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

请注意,该 DescriptionEmbedding 属性是一种 string 而不是向量类型,例如 ReadOnlyMemory<float>。 这意味着在更新插入记录时,MEVD 会自动生成嵌入内容,并使用在矢量存储上配置的 IEmbeddingGenerator<TInput,TEmbedding>

以下安装代码获取集合并确保其存在:

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

var collection = vectorStore.GetCollection<ulong, Hotel>("hotels");
await collection.EnsureCollectionExistsAsync();

更新或插入记录

用于 UpsertAsync 插入新记录或更新现有记录。 如果已存在具有相同键的记录,则会替换该记录;否则,将创建新记录:

var hotel = new Hotel
{
    HotelId = 1,
    HotelName = "Hotel Happy",
    Description = "A happy hotel with good vibes.",
    DescriptionEmbedding = "A happy hotel with good vibes.",
    Tags = ["happy", "vibes"]
};

await collection.UpsertAsync(hotel);

若要一次向上插入多个记录,请传递集合:

var hotels = new[]
{
    new Hotel
    {
        HotelId = 1,
        HotelName = "Hotel Happy",
        Description = "A happy hotel with good vibes.",
        DescriptionEmbedding = "A happy hotel with good vibes.",
        Tags = ["happy", "vibes"]
    },
    new Hotel
    {
        HotelId = 2,
        HotelName = "Hotel Luxury",
        Description = "A luxurious hotel with top-notch amenities.",
        DescriptionEmbedding = "A luxurious hotel with top-notch amenities.",
        Tags = ["luxury", "amenities"]
    }
};

await collection.UpsertAsync(hotels);

小窍门

在更新插入多个记录时首选批处理重载,因为这样可以减少数据库往返。 此外,使用自动嵌入生成时,批处理重载通常会为嵌入模型调用中的所有记录生成嵌入内容,这比一次生成一个记录更有效。

删除记录

使用DeleteAsync按键值从集合中删除记录。

await collection.DeleteAsync(key: 1);

若要一次性删除多个记录,请传递集合:

await collection.DeleteAsync(keys: [1, 2, 3]);

注释

如果具有指定键的记录不存在,则以无提示方式忽略删除操作。

根据键检索记录

使用 GetAsync 按记录键检索记录。

Hotel? hotel = await collection.GetAsync(key: 1);

GetAsync 返回记录(如果找到); null 如果没有具有给定键的记录。

若要一次检索多个记录,请传递键集合。 此重载仅返回一个 IAsyncEnumerable<TRecord> 包含找到的记录:

IAsyncEnumerable<Hotel> hotels = collection.GetAsync(keys: [1, 2, 3]);

await foreach (Hotel hotel in hotels)
{
    Console.WriteLine(hotel.HotelName);
}

注释

仅返回找到的记录,因此结果集可能小于请求的密钥集。 无法保证返回的记录的排序。

在结果中包含向量

默认情况下,检索到的记录中不包含矢量属性,从而减少数据传输。 若要包含它们,请传递一个RecordRetrievalOptions,并将IncludeVectors设置为true

var hotel = await collection.GetAsync(
    key: 1,
    new() { IncludeVectors = true });

按筛选器检索记录

使用筛选器重载 GetAsync 来检索符合给定条件的记录,而不是通过键或向量相似性进行检索:

IAsyncEnumerable<Hotel> hotels = collection.GetAsync(
    filter: h => h.HotelName == "Hotel Happy",
    top: 10);

await foreach (Hotel hotel in hotels)
{
    Console.WriteLine(hotel.HotelName);
}

筛选器表示为 LINQ 表达式。 支持的表达式因数据库提供程序而异,但所有提供程序都支持常见的比较,例如相等、不等和逻辑&&||

重要

要使属性在筛选器中可用,许多数据库需要对其进行索引。 即使不需要索引,它们也能大大提高筛选器的性能。 在定义数据模型时在VectorStoreDataAttribute上设置IsIndexed = true。 有关详细信息,请参阅 Data 属性

筛选器选项

用于 FilteredRecordRetrievalOptions<TRecord> 控制分页、排序以及结果中是否包括向量:

IAsyncEnumerable<Hotel> hotels = collection.GetAsync(
    filter: h => h.Tags.Contains("luxury"),
    top: 10,
    new()
    {
        Skip = 20,
        IncludeVectors = false,
        OrderBy = order => order.Ascending(h => h.HotelName)
    });

下表描述了可用选项:

选项 说明
Skip 返回结果之前要跳过的匹配记录数。 可用于分页。 默认值为 0
OrderBy 指定要按其对结果进行排序的属性和方向。 使用 .Ascending().Descending() 定义排序。 如果未提供,则顺序是不确定的。
IncludeVectors 是否在返回的记录中包含矢量数据。 默认值为 false