PingKai Logo下载

向量搜索

向量搜索利用语义相似性,帮助你找到最相关的记录,即使你的查询没有明确包含所有关键字。

基本用法

本节展示了如何通过几个简单步骤在你的应用中使用向量搜索。在开始之前,你需要先连接到数据库

步骤 1. 创建包含向量字段的表

你可以使用 client.create_table() 创建表,并用 VectorField 定义向量字段。

以下示例创建了一个包含四个列的 documents 表:

  • id:表的主键。
  • text:文档的文本内容。
  • text_vec:文本内容的向量嵌入。
  • meta:文档的元信息,是一个 JSON 对象。
from pytidb.schema import TableModel, Field, VectorField
from pytidb.datatype import TEXT, JSON

class Document(TableModel):
    __tablename__ = "documents"

    id: int = Field(primary_key=True)
    text: str = Field(sa_type=TEXT)
    text_vec: list[float] = VectorField(dimensions=3)
    meta: dict = Field(sa_type=JSON, default_factory=dict)

table = client.create_table(schema=Document, if_exists="overwrite")

VectorField 类接受以下参数:

  • dimensions:向量的维度。指定后,该字段只能存储具有该精确维度的向量。
  • index:是否为该向量字段创建向量索引。默认为 True
  • distance_metric:用于向量索引的距离度量。支持的取值:
    • DistanceMetric.COSINE(默认):余弦距离度量,适合衡量文本相似性
    • DistanceMetric.L2:L2 距离度量,适合衡量整体差异

步骤 2. 向表中插入向量数据

为了演示,向表中插入一些文本及其对应的嵌入向量。

以下示例插入了三条文档记录,每条都带有一个简单的 3 维向量嵌入:

  • dog 的向量嵌入为 [1, 2, 1]
  • fish 的向量嵌入为 [1, 2, 4]
  • tree 的向量嵌入为 [1, 0, 0]
table.bulk_insert([
    Document(text="dog", text_vec=[1,2,1], meta={"category": "animal"}),
    Document(text="fish", text_vec=[1,2,4], meta={"category": "animal"}),
    Document(text="tree", text_vec=[1,0,0], meta={"category": "plant"}),
])

步骤 3. 执行向量搜索

向量搜索使用向量距离度量来衡量向量之间的相似性和相关性。距离越近,记录越相关。要在表中查找最相关的文档,你需要指定一个查询向量。

以下示例假设查询为 A swimming animal,其向量嵌入为 [1, 2, 3]

使用 table.search() 方法执行向量搜索。默认使用 search_mode="vector"

table.search([1, 2, 3]).limit(3).to_list()
[
    {"id": 2, "text": "fish", "text_vec": [1,2,4], "_distance": 0.00853986601633272},
    {"id": 1, "text": "dog", "text_vec": [1,2,1], "_distance": 0.12712843905603044},
    {"id": 3, "text": "tree", "text_vec": [1,0,0], "_distance": 0.7327387580875756},
]

结果显示,最相关的文档是 fish,其距离为 0.00853986601633272

距离度量

距离度量用于衡量一对向量之间的相似性。目前,TiDB 支持以下距离度量:

table.search() API 支持以下距离度量:

度量名称描述最佳应用场景
DistanceMetric.COSINE计算两个向量的余弦距离(默认)。衡量向量之间的夹角。文本嵌入、语义搜索
DistanceMetric.L2计算两个向量的 L2 距离(欧氏距离)。衡量直线距离。图像特征

要更改向量搜索使用的距离度量,可使用 .distance_metric() 方法。

示例:使用 L2 距离度量

from pytidb.schema import DistanceMetric

results = (
    table.search([1, 2, 3])
        .distance_metric(DistanceMetric.L2)
        .limit(10)
        .to_list()
)

距离阈值

table.search() API 允许你设置距离阈值,以控制返回结果的相似性。通过指定该阈值,你可以排除不够相似的向量,仅返回满足相关性标准的结果。

使用 .distance_threshold() 方法为搜索结果设置最大距离。只有距离小于该阈值的记录才会被返回。

示例:仅返回距离小于 0.5 的文档

results = table.search([1, 2, 3]).distance_threshold(0.5).limit(10).to_list()

距离范围

table.search() API 还支持指定距离范围,以进一步细化结果。

使用 .distance_range() 方法同时设置最小和最大距离值。只有距离在该范围内的记录才会被返回。

示例:仅返回距离在 0.01 到 0.05 之间的文档

results = table.search([1, 2, 3]).distance_range(0.01, 0.05).limit(10).to_list()

元信息过滤

作为关系型数据库,TiDB 支持丰富的 SQL 运算符,并允许灵活组合过滤条件。

在 TiDB 中进行向量搜索时,你可以对标量字段(如整数型和字符串)或 JSON 字段进行元信息过滤。

通常,向量搜索结合元信息过滤有两种模式:

  • 后过滤:TiDB 首先在整个向量空间中执行向量搜索,获取 top-k 候选,然后对候选集应用过滤条件。向量搜索阶段通常会使用向量索引以提升性能。
  • 预过滤:TiDB 在向量搜索前先应用过滤条件。如果过滤条件选择性高,且被过滤字段有标量索引,该模式可以减少搜索空间并提升性能。

后过滤

使用 .filter() 方法和过滤字典为向量搜索添加过滤条件。

默认情况下,table.search() API 使用后过滤模式,以最大化向量索引的搜索性能。

示例:向量搜索结合后过滤

results = (
    table.search([1, 2, 3])
        # `meta` 是 JSON 字段,其值为类似 {"category": "animal"} 的 JSON 对象
        .filter({"meta.category": "animal"})
        .num_candidate(50)
        .limit(10)
        .to_list()
)

预过滤

要启用预过滤,在 .filter() 方法中设置 prefilter=True

示例:向量搜索结合预过滤

results = (
    table.search([1, 2, 3])
        .filter({"meta.category": "animal"}, prefilter=True)
        .limit(10)
        .to_list()
)

有关支持的过滤运算符,请参见 过滤

多向量字段

TiDB 支持在单个表中定义多个向量列,便于存储和搜索不同类型的向量嵌入。

例如,你可以在同一张表中同时存储文本嵌入和图像嵌入,方便管理多模态数据。

你可以在 schema 中定义多个向量字段,并通过 .vector_column() 方法指定要搜索的向量字段。

示例:指定搜索的向量字段

# 创建包含多个向量字段的表
class RichTextDocument(TableModel):
    __tablename__ = "rich_text_documents"
    id: int = Field(primary_key=True)
    text: str = Field(sa_type=TEXT)
    text_vec: list[float] = VectorField(dimensions=3)
    image_url: str
    image_vec: list[float] = VectorField(dimensions=3)

table = client.create_table(schema=RichTextDocument, if_exists="overwrite")

# 插入示例数据 ...

# 使用图像向量字段进行搜索
results = (
    table.search([1, 2, 3])
        .vector_column("image_vec")
        .distance_metric(DistanceMetric.COSINE)
        .limit(10)
        .to_list()
)

输出搜索结果

table.search() API 支持将搜索结果转换为多种常见数据处理格式:

作为 SQLAlchemy 结果行

如需处理原始 SQLAlchemy 结果行,可使用:

table.search([1, 2, 3]).limit(10).to_rows()

作为 Python 字典列表

如需在 Python 中便于操作,可将结果转换为字典列表:

table.search([1, 2, 3]).limit(10).to_list()

作为 pandas DataFrame

如需以用户友好的表格方式展示结果(尤其适用于 Jupyter notebook),可转换为 pandas DataFrame:

table.search([1, 2, 3]).limit(10).to_pandas()

作为 Pydantic 模型实例列表

TableModel 类也可作为 Pydantic 模型用于表示数据实体。如需以 Pydantic 模型实例处理结果,可使用:

table.search([1, 2, 3]).limit(10).to_pydantic()