跳转至

数据模型

本文件系统性梳理 GoVector 的数据模型,覆盖核心数据结构(PointStruct、ScoredPoint、Filter、Payload、CollectionMeta 等),解释其字段定义、数据类型、约束条件与使用场景;详述 Protocol Buffers 定义的二进制格式与 JSON 映射关系;说明向量数据的存储格式、序列化机制与性能考量;给出数据模型之间的关系图与转换规则,并提供实际数据示例以帮助理解。

项目结构与数据模型概览

  • 核心数据模型位于 core 包中:
  • models.go:定义 Payload、PointStruct、ScoredPoint、Filter、RangeValue、MatchValue、CollectionMeta 及过滤匹配逻辑
  • proto/point.proto:定义 Protobuf 消息结构(PointStruct、Value、ScoredPoint、Condition、MatchValue、Filter)
  • proto/point.pb.go:由 protoc-gen-go 生成的 Protobuf Go 代码
  • storage.go:基于 bbolt 的持久化层,负责将 PointStruct 序列化为 Protobuf 并写入磁盘
  • collection.go:集合抽象,封装 Upsert/Search/Delete 等操作,并协调存储与索引
  • math.go:距离度量枚举与计算函数(Cosine、Euclid、Dot)
  • hnsw_index.go:HNSW 参数与索引实现
  • models_test.go:过滤器与匹配逻辑的单元测试,展示典型用法
  • README.md:项目特性与使用说明
graph TB
subgraph "核心数据模型"
A["Payload
字符串键到任意值"] B["PointStruct
ID/Version/Vector/Payload"] C["ScoredPoint
ID/Version/Score/Payload"] D["Filter
Must/MustNot 条件"] E["Condition
Key/Type/Match/Range"] F["MatchValue
oneof 字符串/整数/浮点/布尔/字节"] G["RangeValue
GT/GTE/LT/LTE"] H["CollectionMeta
名称/维度/度量/HNSW参数"] end subgraph "序列化与存储" P["Protobuf 消息
PointStruct/Value/ScoredPoint/Condition/MatchValue/Filter"] S["Storage
bbolt + Protobuf"] end subgraph "索引与集合" I["Collection
Upsert/Search/Delete"] J["HNSWIndex
参数与距离函数"] end B --> P C --> P D --> E E --> F E --> G P --> S I --> S I --> J

核心数据结构总览

  • Payload:字符串键到任意值的元数据映射,用于过滤与检索
  • PointStruct:向量点实体,包含唯一标识、版本号、向量数组与可选元数据
  • ScoredPoint:查询结果,包含相似度/距离分值与点信息
  • Filter:查询过滤器,支持 Must 与 MustNot 条件集合
  • Condition:单个过滤条件,指定键、类型与匹配值或范围
  • MatchValue:匹配值,采用 oneof 支持多种基础类型
  • RangeValue:范围值,支持大于/大于等于/小于/小于等于
  • CollectionMeta:集合元数据,记录集合名、向量维度、距离度量、是否使用 HNSW 及参数

架构与数据流概览

  • 写入流程:应用调用 Collection.Upsert → 校验维度与设置版本 → 存储层先写入 bbolt(Protobuf 序列化)→ 更新内存索引(Flat 或 HNSW)
  • 查询流程:Collection.Search → 锁定读取 → 调用索引 Search → 返回 ScoredPoint 列表
  • 删除流程:Collection.Delete → 确定目标 ID(显式 ID 或按过滤器匹配)→ 先删除存储再删除索引
  • 过滤匹配:MatchFilter 遍历 Must/MustNot 条件,逐条调用 matchCondition → 分支处理 exact/range/prefix/contains/regex
sequenceDiagram
participant App as "应用"
participant Col as "Collection"
participant St as "Storage"
participant Idx as "VectorIndex(HNSW/Flat)"
App->>Col : Upsert(points)
Col->>Col : 校验维度/设置版本
Col->>St : UpsertPoints(Protobuf序列化)
St-->>Col : 成功/失败
Col->>Idx : Upsert(points)
Idx-->>Col : 成功/失败
Col-->>App : 返回
App->>Col : Search(query, filter, topK)
Col->>Idx : Search(query, filter, topK)
Idx-->>Col : []ScoredPoint
Col-->>App : []ScoredPoint

详细数据模型解析

Payload(元数据)

  • 类型:map[string]interface{}
  • 约束:键必须为字符串;值可为字符串、整数、浮点、布尔、字节切片或数组/字符串/整数切片(在过滤器中被识别)
  • 使用场景:作为 Filter 的匹配对象,支持多条件组合
  • JSON 映射:直接映射为 JSON 对象

PointStruct(向量点)

  • 字段
  • ID:字符串,唯一标识
  • Version:uint64,版本号(时间戳纳秒级)
  • Vector:[]float32,向量嵌入
  • Payload:Payload,可选元数据
  • 约束
  • 向量长度需与集合维度一致
  • 版本号在批量 Upsert 时统一设置
  • 使用场景:插入/更新向量点;作为 Protobuf 序列化的目标

ScoredPoint(带分数的结果)

  • 字段
  • ID:字符串
  • Version:uint64
  • Score:float32,相似度或距离分值(由集合度量决定“越小/越大”更相似)
  • Payload:Payload
  • 约束:Score 值由底层索引返回,排序时遵循度量约定
  • 使用场景:Search 查询返回结果

Filter(过滤器)

  • 字段
  • Must:[]Condition,全部满足
  • MustNot:[]Condition,均不满足
  • 约束:Must 与 MustNot 同时存在时,二者都必须满足才命中
  • 使用场景:Search/Delete 时按元数据筛选

Condition(条件)

  • 字段
  • Key:string,元数据键
  • Type:ConditionType,匹配类型(exact/range/prefix/contains/regex)
  • Match:MatchValue,精确匹配值
  • Range:*RangeValue,范围匹配
  • 约束:Type 为 range 时使用 Range;否则使用 Match
  • 使用场景:描述单个键的过滤规则

MatchValue(匹配值)

  • oneof 支持:string/int/float/bool/bytes
  • 使用场景:Filter 中精确匹配的值载体

RangeValue(范围值)

  • 字段:GT/GTE/LT/LTE,支持 int 与 float64
  • 使用场景:数值范围过滤

CollectionMeta(集合元数据)

  • 字段
  • Name:string
  • VectorLen:int
  • Metric:Distance
  • UseHNSW:bool
  • HNSWParams:HNSWParams
  • 使用场景:持久化集合配置,重启后恢复

协议缓冲与JSON映射关系

Protobuf 消息定义与字段映射

  • PointStruct
  • id:string
  • vector:repeated float
  • payload:map
  • Value(oneof)
  • stringvalue、intvalue、doublevalue、boolvalue、bytes_value
  • ScoredPoint
  • id:string
  • version:uint64
  • score:float
  • payload:map
  • Condition
  • key:string
  • match:MatchValue
  • MatchValue(oneof)
  • stringvalue、intvalue、doublevalue、boolvalue、bytes_value
  • Filter
  • must:[]Condition
  • must_not:[]Condition

JSON 映射规则

  • Protobuf 字段名与 JSON 名称通常一致(如 id、version、score、must、must_not)
  • map 在 JSON 中表现为对象,键为字符串,值为 Value 对象
  • oneof 字段在 JSON 中仅输出被设置的那个字段(如 stringValue、intValue 等)

Go 结构体与 Protobuf 的转换

  • toProtoPoint/fromProtoPoint:将 core.Payload 映射为 pb.Value.oneof,反之亦然
  • 注意:Go 端 Payload 的 interface{} 值在转换时会根据具体类型选择 oneof 字段;不支持的类型会被跳过

向量存储与序列化机制

存储引擎与容器

  • bbolt(BoltDB):每个集合一个桶(bucket),键为点 ID,值为 Protobuf 序列化后的字节
  • 元数据桶:collections_meta,保存 CollectionMeta(JSON)

序列化与反序列化

  • 写入:PointStruct → toProtoPoint → proto.Marshal → bbolt.Put
  • 读取:bbolt.Get → proto.Unmarshal → fromProtoPoint
  • 量化:可选 SQ8 量化,写入时将向量压缩并存入 Payload,读取时解压还原

版本控制与一致性

  • Upsert 批量写入时统一设置 Version(纳秒级时间戳)
  • 写入顺序:先存储,再更新索引;若索引更新失败,尝试回滚存储(尽力而为)

数据模型关系图与转换规则

classDiagram
class Payload {

    +map[string]interface{
}
}
class PointStruct {

    +string ID
    +uint64 Version
    +[]float32 Vector
    +Payload Payload

}
class ScoredPoint {

    +string ID
    +uint64 Version
    +float32 Score
    +Payload Payload

}
class Filter {

    +[]Condition Must
    +[]Condition MustNot

}
class Condition {

    +string Key
    +ConditionType Type
    +MatchValue Match
    +RangeValue Range

}
class MatchValue {

    +oneof Value

}
class RangeValue {

    +interface{
} GT
+interface{} GTE
+interface{} LT
+interface{} LTE
}
class CollectionMeta {

    +string Name
    +int VectorLen
    +Distance Metric
    +bool UseHNSW
    +HNSWParams HNSWParams

}
PointStruct --> Payload : "包含"
ScoredPoint --> Payload : "包含"
Filter --> Condition : "包含"
Condition --> MatchValue : "使用"
Condition --> RangeValue : "使用"

转换规则

  • Go Payload ↔ Protobuf Value.oneof
  • string/int/int64/float32/float64/bool/[]byte 映射到对应 oneof 字段
  • 不支持类型会被忽略
  • CollectionMeta 以 JSON 形式保存在 collections_meta 桶中

性能考量与优化建议

  • 距离度量
  • Cosine:归一化,适合不同尺度向量;HNSW 默认使用 1 - cos 直接距离
  • Euclid:L2 距离,较小更相似;HNSW 使用标准欧氏距离
  • Dot:内积,较大更相似;HNSW 将其转为负值以适配最小化策略
  • HNSW 参数
  • M:每节点最大连接数
  • EfConstruction/EfSearch:构建/搜索候选列表大小
  • K:返回近邻数量
  • 量化
  • SQ8 量化可显著降低磁盘占用,加载时自动解压;适用于大规模数据
  • 写入顺序
  • 先存储后索引,保证存储优先一致性;索引失败时尽力回滚存储

故障排查指南

  • 维度不匹配
  • 现象:Upsert/Search 报错“维度不一致”
  • 处理:确保 PointStruct.Vector 长度与集合 VectorLen 一致
  • 过滤器无效
  • 现象:Filter 未生效或误判
  • 排查:确认 Key 是否存在于 Payload;RangeValue 类型与值类型一致(int/float64)
  • 正则表达式错误
  • 现象:matchRegex 返回 false
  • 处理:检查正则语法;非法正则将直接返回不匹配
  • 存储关闭或不可用
  • 现象:UpsertPoints/LoadCollection 报错“storage is closed”
  • 处理:确保 Storage 已正确打开且未关闭

结论

GoVector 的数据模型围绕 PointStruct、ScoredPoint、Filter、Payload、CollectionMeta 展开,结合 Protobuf 的强类型与高效序列化能力,以及 bbolt 的轻量持久化,实现了高性能、可扩展的嵌入式向量数据库。通过明确的字段定义、严格的约束与完善的过滤匹配逻辑,用户可以在本地或边缘设备上快速构建向量检索与过滤能力。

附录:数据示例

以下示例展示各数据结构的完整表示形式(以 JSON 形式呈现,便于理解字段与取值):

  • PointStruct 示例
  • 字段:id、version、vector、payload
  • payload 示例键值:category(string)、price(int)、in_stock(bool)、tags([]string)、name(string)
  • 参考路径:models_test.go:8-15

  • ScoredPoint 示例

  • 字段:id、version、score、payload
  • 参考路径:collection.go:135-147

  • Filter/Condition/MatchValue/RangeValue 示例

  • Filter:must/must_not 数组,每个元素为 Condition
  • Condition:key、type、match 或 range
  • MatchValue:oneof 字段(stringvalue/intvalue/doublevalue/boolvalue/bytes_value)
  • RangeValue:gt/gte/lt/lte
  • 参考路径:models_test.go:132-218point.proto:30-49

  • CollectionMeta 示例

  • 字段:name、vector_size、distance、hnsw、parameters
  • 参考路径:collection.go:56-66