六维教程

元数据过滤与索引管理

光靠向量相似度检索有时不够精准。比如一个文档库里只想搜某个分类的文档,一个商品库里只想召回在售商品。这种”先按条件筛再按相似度排”的需求,要靠元数据过滤实现。这篇讲清楚怎么给向量挂元数据、建元数据索引、写过滤条件,以及怎么查看和管理索引配置。

为什么需要元数据过滤

向量相似度解决的是”语义近不近”,但很多业务检索还要叠加结构化条件。举几个常见场景。

场景 不加过滤 加过滤
多租户知识库 全库混搜,可能返回别家数据 只在本租户内搜
商品搜索 下架商品也命中 只召回在售商品
时间序列 历史旧内容挤掉新内容 限定近 30 天
多分类文档 跨分类混杂 限定某个分类

过滤逻辑是先按 filter 条件圈出一批向量,再从中取相似度最高的 topK 条。所以过滤不会改变 topK 的数量,只是缩小了候选范围。

给向量加元数据

元数据是插入或 upsert 时附在每条向量上的键值对。结构很简单

const vectors = [
  {
    id: "doc-1",
    values: [0.12, 0.45, 0.78],
    metadata: {
      category: "tech",
      status: "published",
      views: 1024,
      featured: true,
    },
  },
];
await env.MY_INDEX.upsert(vectors);

元数据的值支持三种类型

类型 示例 典型用途
string “tech”、”published” 分类、状态、租户 id
number 1024、1734242400 浏览量、时间戳、价格
boolean true、false 是否置顶、是否上架

元数据的规则和限制

限制项
单条向量上限 10 KB
键名禁止字符 点号、双引号
键名禁止前缀 美元符号开头
键名长度 最长 512 字符

设计元数据要克制。元数据是给过滤用的,不要把文档原文塞进去,原文应存在 D1(Cloudflare 的 SQLite 数据库)或 R2(Cloudflare 的对象存储)里,metadata 里只放用于筛选的字段和一个指向原文的 id 或路径。

创建元数据索引

这里有个最容易踩的坑。元数据默认不能直接过滤,要先给某个字段建索引,过滤才能生效。

用 wrangler 命令建元数据索引

npx wrangler vectorize create-metadata-index my-index --property-name='category' --type='string'

执行后输出一个 mutation 标识符,异步构建,稍等片刻生效。

参数说明

参数 作用
property-name 要建索引的元数据字段名
type 字段类型,string、number、boolean

每个 Vectorize 索引最多建 10 个元数据索引。类型一旦确定改不了,要换只能先删再建。

最关键的时序问题。在元数据索引建好之前插入的向量,那个字段的值不会被索引到。要让旧向量也支持过滤,必须建完索引后重新 upsert 一遍。所以正确顺序是,先建索引、建元数据索引,再批量导入向量,避免返工。

过滤操作符

filter 参数用一种类似 MongoDB 的语法,支持八个操作符

操作符 含义 值类型
$eq 等于 单值
$ne 不等于 单值
$in 属于集合 数组
$nin 不属于集合 数组
$lt 小于 string 或 number
$lte 小于等于 string 或 number
$gt 大于 string 或 number
$gte 大于等于 string 或 number

几个要点。$eq 可以省略,直接写字段值就是隐式等于。范围查询里 $lt 和 $lte 只能和 $gt、$gte 组合,不能两个上界或两个下界混用。string 的范围查询按字典序,能实现前缀匹配。filter 的 JSON 表示不能超过 2048 字节。

过滤查询示例

把过滤条件加到 query 的 filter 参数里

export default {
  async fetch(request, env) {
    const { vector } = await request.json();

    const matches = await env.MY_INDEX.query(vector, {
      topK: 5,
      returnMetadata: "all",
      filter: { category: "tech", status: "published" },
    });

    return Response.json(matches);
  },
};

这表示只在 category 等于 tech 且 status 等于 published 的向量里找相似度最高的 5 条。

更复杂的条件示例

// 等于加不等于
filter: { category: "tech", status: { $ne: "draft" } }

// 集合匹配
filter: { category: { $in: ["tech", "science"] } }

// 数值范围,近 30 天
filter: { timestamp: { $gte: 1734242400, $lt: 1736834400 } }

// 字符串前缀,匹配以 net 开头的值
filter: { tag: { $gte: "net", $lt: "neu" } }

设计过滤字段时要考虑基数。基数指一个字段有多少种不同的值。高基数字段(比如用户 id)用 $eq 过滤很高效,能快速定位一小撮向量。但高基数字段做范围查询会扫大量索引项,性能下降。比如毫秒级时间戳做跨长时间的范围查询会退化。一个常见优化是把时间戳按 5 分钟取整存一份专门用于过滤,原始时间戳另存一个字段。

命名空间

除了元数据过滤,Vectorize 还有个 namespace 概念,也能切分检索范围。插入时给向量加 namespace

{
  id: "doc-1",
  values: [0.12, 0.45, 0.78],
  namespace: "tenant-001",
  metadata: { category: "tech" },
}

namespace 和 metadata 过滤的对比

对比项 namespace metadata 过滤
切分方式 一条向量只属一个 namespace 多个字段组合条件
灵活度 单一维度 多维度组合
值类型 字符串 string、number、boolean
是否需建索引 不需要 需要建元数据索引
执行顺序 先按 namespace 切分 再按 metadata 过滤

经验法则。如果只有一个维度要硬隔离(比如多租户),用 namespace 更简单。如果要按多个字段组合筛选(分类加状态加时间),用 metadata 过滤更灵活。两者可以叠加,先 namespace 再 metadata。

索引配置管理

Vectorize 提供几个手段查看和管理索引配置。

在 Worker 代码里用 describe 方法拿到索引的基本配置

export default {
  async fetch(request, env) {
    const info = await env.MY_INDEX.describe();
    return Response.json(info);
  },
};

返回包含索引名、配置的维度和度量。describe 用来在运行时校验配置,比如代码里硬编码了维度数,可以读出来对一下。

命令行管理索引

命令 作用
wrangler vectorize info my-index 查看索引详情和用量
wrangler vectorize list-vectors my-index 分页列出向量 id
wrangler vectorize list-metadata-index my-index 查看建了哪些元数据索引
wrangler vectorize delete-metadata-index my-index –property-name=’category’ 删除某个元数据索引

list-vectors 支持分页,每页最多 1000 条

npx wrangler vectorize list-vectors my-index --count=100

元数据索引不能随便删。删掉某个字段的元数据索引后,该字段就不能再用于过滤,要重新建并重新 upsert 向量才能恢复。

指标监控

生产环境要盯几个关键指标,确保检索质量和成本可控。

主要监控项

指标 关注点 异常信号
向量总数 索引规模增长 突增可能是重复插入
查询延迟 用户体验 p99 超过百毫秒要排查
查询成功率 服务健康 错误率上升看维度是否错配
元数据索引命中 过滤效率 命中低说明字段基数不合理
维度配置 一致性 和嵌入模型输出不一致会报错

Cloudflare 仪表盘的 Vectorize 页面能看到索引的向量数和用量。命令行用 wrangler vectorize info 也能查。延迟和错误率建议接到 Cloudflare 的分析或外部监控里持续观察。

成本方面,Vectorize 按存储的向量维度数和查询次数计费。控制好 topK 和 returnValues 的使用能省不少,returnValues 开着会传回完整向量,维度高时流量和耗时都上去了,非必要不开。

小结

元数据过滤让向量检索从”纯语义相似”升级到”带业务条件的精准检索”。关键是先建元数据索引再插数据,按字段类型选对操作符,高基数字段慎做范围查询。配合 namespace 能实现多租户硬隔离,配合 describe 和 wrangler info 能持续盯住索引健康。下一篇把这些组合起来,加上 Workers AI 的嵌入和生成能力,搭一个能问答的 RAG 系统。

上一篇 向量索引与相似度搜索

下一篇 实现 RAG 检索

上一篇
向量索引与相似度搜索
下一篇
实现 RAG 检索