定义数据模型并获取数据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。 |