跳转至

数据模型

GoVector 的数据模型设计与 Qdrant 兼容,同时提供高效的向量数据存储和检索功能。本文档描述了核心数据结构、它们的关系以及使用最佳实践。

📊 核心数据结构

点(Point)

点表示带有元数据的单个向量记录。

{
  "id": "doc_1",
  "vector": [0.1, 0.2, 0.3, /* ... */],
  "payload": {
    "category": "tech",
    "author": "John",
    "timestamp": 1620000000,
    "tags": ["AI", "vector"]
  }
}

字段

  • id:(必填) 点的唯一标识符
  • vector:(必填) 表示向量嵌入的浮点数组
  • payload:(可选) 用于过滤和附加上下文的键值元数据

带分数的点(Scored Point)

带分数的点是搜索操作返回的结果,包含相似度分数。

{
  "id": "doc_1",
  "score": 0.999,
  "payload": {
    "category": "tech",
    "author": "John"
  }
}

字段

  • id:匹配点的唯一标识符
  • score:基于距离度量的相似度分数(对于 Cosine/Dot,值越大越相似;对于 Euclidean,值越小越相似)
  • payload:匹配点的元数据

过滤器(Filter)

过滤器定义基于点的 payload 进行过滤的条件。

{
  "must": [
    {
      "key": "category",
      "type": "exact",
      "match": {"value": "tech"}
    },
    {
      "key": "timestamp",
      "type": "range",
      "range": {"gte": 1620000000, "lte": 1630000000}
    }
  ],
  "must_not": [
    {
      "key": "tags",
      "type": "contains",
      "match": {"value": "deprecated"}
    }
  ]
}

字段

  • must:必须满足的条件
  • must_not:必须不满足的条件

条件(Condition)

条件定义单个过滤条件。

类型

  • exact:精确值匹配
  • range:数值范围比较(gt, gte, lt, lte)
  • prefix:字符串前缀匹配
  • contains:数组或字符串包含匹配
  • regex:字符串正则表达式匹配

🚀 数据生命周期

写入(Upsert)

  1. 验证:检查向量维度是否匹配集合配置
  2. 版本控制:生成版本号用于并发控制
  3. 持久化:将点序列化为 Protocol Buffers 并写入 BoltDB
  4. 索引:更新内存索引以实现快速检索
  1. 验证:检查查询向量维度
  2. 索引搜索:使用 HNSW 或 Flat 索引查找近似最近邻
  3. 过滤:对搜索结果应用 payload 过滤器
  4. 评分:为过滤后的结果重新计算精确分数
  5. 结果返回:返回前 K 个结果

删除(Delete)

  1. 目标识别:通过 ID 或过滤器确定要删除的点
  2. 持久化:从 BoltDB 中移除点
  3. 索引:从内存索引中移除点
  4. 结果返回:返回删除的点数

🔧 序列化

GoVector 使用 Protocol Buffers 进行向量数据的高效序列化:

  • PointStruct:序列化为字节并存储在 BoltDB 中
  • 值类型:支持 string、int64、double、bool 和 bytes
  • 向后兼容性:使用 proto3 语法确保版本兼容性

📈 距离度量

GoVector 支持三种用于相似度计算的距离度量:

度量 描述 相似度解释
Cosine 测量向量之间夹角的余弦值 值越大 = 越相似
Euclidean 测量向量之间的直线距离 值越小 = 越相似
Dot 测量向量的点积 值越大 = 越相似

注意:余弦相似度是默认度量,推荐用于大多数用例,尤其是使用归一化向量时。

💡 最佳实践

点设计

  • ID:使用稳定、有意义的标识符(例如业务主键)
  • 向量:确保维度匹配集合配置;为余弦相似度归一化向量
  • Payload:保持元数据简洁;使用适当的类型进行过滤

过滤器使用

  • Must vs MustNot:优先使用 must 缩小结果范围,谨慎使用 must_not
  • 条件类型:对精确匹配使用 exact,对数值范围使用 range,对字符串前缀使用 prefix,对数组使用 contains,对复杂字符串模式使用 regex
  • 性能:避免过于复杂的过滤器,尤其是正则表达式

性能优化

  • 索引选择:对大规模数据使用 HNSW,对小型数据集使用 Flat
  • 量化:对大规模数据启 SQ8 量化以减少存储使用
  • 批量操作:使用批量写入多个点以提高性能
  • TopK:限制结果数量以减少网络开销

🚩 常见问题

维度不匹配

  • 错误:"dimension mismatch"
  • 解决方案:确保向量长度匹配集合的配置维度

过滤器不工作

  • 可能原因:键名不正确、类型不匹配或键不存在
  • 解决方案:验证键名和类型;记住不存在的键会通过 must_not 条件

查询缓慢

  • 可能原因:使用 Flat 索引的大型数据集、复杂过滤器或结果过多
  • 解决方案:使用 HNSW 索引,简化过滤器,减少 topK 值

📖 示例用法

创建点

point := core.PointStruct{
    ID:     "doc_1",
    Vector: []float32{0.1, 0.2, 0.3, /* ... */},
    Payload: core.Payload{
        "category": "tech",
        "author":  "John",
    },
}

使用过滤器

filter := &core.Filter{
    Must: []core.Condition{
        {
            Key:   "category",
            Type:  core.MatchTypeExact,
            Match: core.MatchValue{Value: "tech"},
        },
    },
    MustNot: []core.Condition{
        {
            Key:   "author",
            Type:  core.MatchTypeExact,
            Match: core.MatchValue{Value: "Jane"},
        },
    },
}

带过滤器搜索

results, err := collection.Search(queryVector, filter, 10)
for _, result := range results {
    fmt.Printf("ID: %s, Score: %.4f, Category: %s\n", 
        result.ID, result.Score, result.Payload["category"])
}

🎯 结论

GoVector 的数据模型提供了一种灵活高效的方式来存储和检索向量数据。通过遵循点设计、过滤器使用和性能优化的最佳实践,您可以构建高性能的向量搜索应用程序,满足您的需求。

🔗 相关文档