数据模型¶
本文件系统性梳理 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
-
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
-
CollectionMeta 示例
- 字段:name、vector_size、distance、hnsw、parameters
- 参考路径:collection.go:56-66