Component Interactions¶
This document systematically outlines GoVector's component interaction relationships, focusing on:
- Persistent interaction flow between Collection and Storage (write order, consistency guarantee, rollback strategy)
- Index operation collaboration between Collection and VectorIndex (transparent switching of Flat/HNSW)
- Business logic calls between Server and Collection (REST API to internal method mapping)
- Interface abstraction design philosophy (how VectorIndex supports multi-strategy switching)
- Inter-component communication protocols and data transfer formats (JSON/Protobuf)
- Interaction timing diagrams for typical operation scenarios
- Decoupling design principles and extensibility considerations
Project Structure¶
GoVector adopts a clear layered organization: the core library core provides vector collections, indexing, storage and model definitions; the api layer provides Qdrant-compatible HTTP service; cmd/govector provides standalone microservice entry; examples and tests cover key paths.
graph TB
subgraph "Application Entry"
CLI["Command Line Entry
cmd/govector/main.go"]
end
subgraph "API Layer"
Srv["HTTP Server
api/server.go"]
end
subgraph "Core Library"
Col["Collection
core/collection.go"]
Stor["Storage(BoltDB)
core/storage.go"]
VIface["VectorIndex Interface
core/index.go"]
Flat["FlatIndex Implementation
core/flat_index.go"]
HNSW["HNSWIndex Implementation
core/hnsw_index.go"]
Models["Data Models/Filters
core/models.go"]
end
CLI --> Srv
Srv --> Col
Col --> Stor
Col --> VIface
VIface --> Flat
VIface --> HNSW
Srv --> Models
Col --> Models
Stor --> Models
Core Components¶
- Collection: User-facing logical container encapsulating Upsert/Search/Delete operations, coordinating Storage and VectorIndex, ensuring consistency between persistence and in-memory index.
- Storage: bbolt-based local persistence responsible for Collection metadata and point data read/write, supports optional vector quantization.
- VectorIndex Interface: Unified index abstraction shielding Flat and HNSW implementation differences, enabling transparent strategy switching.
- FlatIndex: In-memory brute-force search suitable for small to medium scale data, provides exact results.
- HNSWIndex: Graph-based approximate nearest neighbor search suitable for large-scale data, provides sub-linear complexity.
- Server: Provides Qdrant-compatible REST API, manages Collection and forwards requests to Collection.
- Data Models and Filters: PointStruct, ScoredPoint, Filter, Condition, supporting data structures and query filtering.
Architecture Overview¶
The diagram below shows the complete chain from HTTP requests to internal processing to persistence, and the internal Upsert/delete flow within Collection.
sequenceDiagram
participant Client as "Client"
participant API as "Server(HTTP)"
participant Col as "Collection"
participant Stor as "Storage"
participant Idx as "VectorIndex(Flat/HNSW)"
Client->>API : "POST /collections/{name}/points"
API->>Col : "Upsert(points)"
Col->>Col : "Validate dimension/set version"
Col->>Stor : "UpsertPoints(collection, points)"
Stor-->>Col : "Persistence success"
Col->>Idx : "Upsert(points)"
Idx-->>Col : "Index update success"
Col-->>API : "Return status"
API-->>Client : "200 OK"
Client->>API : "POST /collections/{name}/points/search"
API->>Col : "Search(vector, filter, limit)"
Col->>Idx : "Search(query, filter, topK)"
Idx-->>Col : "TopK results"
Col-->>API : "Return results"
API-->>Client : "200 OK"
Client->>API : "POST /collections/{name}/points/delete"
API->>Col : "Delete(points, filter)"
Col->>Stor : "DeletePoints(collection, ids)"
Col->>Idx : "Delete(id)"
Col-->>API : "Return delete count"
API-->>Client : "200 OK"
Detailed Component Analysis¶
Collection and Storage Persistence Interaction¶
- Write order and consistency
- Upsert: Persist first then update index, on failure try to delete written points from storage (best-effort rollback).
- Delete: Delete points from storage first, then from index.
- Storage metadata
- Collection metadata saved in special bucket, rebuilt from metadata after restart.
- Protocol and format
- Point data uses Protobuf serialization; metadata uses JSON.
- Optional vector quantization: Compress before storage, decompress on load.
flowchart TD
Start(["Upsert entry"]) --> Validate["Validate vector dimension/set version"]
Validate --> Persist["Storage.UpsertPoints"]
Persist --> PersistOK{"Persistence success?"}
PersistOK --> |No| Rollback["Rollback storage (best effort)"]
Rollback --> Error["Return error"]
PersistOK --> |Yes| UpdateIdx["VectorIndex.Upsert"]
UpdateIdx --> UpdateOK{"Index update success?"}
UpdateOK --> |No| Cleanup["Delete written points from storage (best effort)"]
Cleanup --> Error
UpdateOK --> Done(["Complete"])
StartDel(["Delete entry"]) --> Decide["Determine target ID list"]
Decide --> DelStore["Storage.DeletePoints"]
DelStore --> DelIdx["VectorIndex.Delete"]
DelIdx --> Done
Collection and VectorIndex Index Operations¶
- Interface abstraction
- VectorIndex defines capabilities like Upsert/Search/Delete/Count/GetIDsByFilter/DeleteByFilter, shielding implementation differences.
- Strategy switching
- NewCollection/NewCollectionWithParams selects FlatIndex or HNSWIndex based on useHNSW parameter.
- HNSW supports parameterized configuration (M, EfConstruction, EfSearch, K), FlatIndex only needs distance metric.
- Filtering and sorting
- FlatIndex calculates distance for all points in memory and sorts.
- HNSW uses graph search combined with post-filtering strategy (over-fetch + filter) to meet filtering requirements.
classDiagram
class VectorIndex {
+Upsert(points) error
+Search(query, filter, topK) []ScoredPoint
+Delete(id) error
+Count() int
+GetIDsByFilter(filter) []string
+DeleteByFilter(filter) ([]string, error)
}
class FlatIndex {
-points map[string]*PointStruct
-metric Distance
+Upsert(points) error
+Search(query, filter, topK) []ScoredPoint
+Delete(id) error
+Count() int
+GetIDsByFilter(filter) []string
+DeleteByFilter(filter) ([]string, error)
}
class HNSWIndex {
-graph *Graph[string]
-points map[string]*PointStruct
-metric Distance
-params HNSWParams
+Upsert(points) error
+Search(query, filter, topK) []ScoredPoint
+Delete(id) error
+Count() int
+GetIDsByFilter(filter) []string
+DeleteByFilter(filter) ([]string, error)
}
VectorIndex <|.. FlatIndex
VectorIndex <|.. HNSWIndex
Server and Collection Business Logic Calls¶
- Routing and endpoints
- /collections, /collections/{name}, /collections/{name}/points, /collections/{name}/points/search, /collections/{name}/points/delete.
- Request parsing and forwarding
- handleUpsert/handleSearch/handleDelete parse JSON requests into internal structures, call corresponding Collection methods.
- Startup and recovery
- Start automatically loads Collection metadata from storage and rebuilds Collection.
- Error handling
- Returns 404 for non-existent collections, 400 for invalid JSON, 500 for internal errors.
sequenceDiagram
participant Client as "Client"
participant S as "Server"
participant C as "Collection"
participant I as "VectorIndex"
participant D as "Storage"
Client->>S : "POST /collections"
S->>C : "NewCollectionWithParams(...)"
C->>D : "SaveCollectionMeta"
C->>I : "Initialize index"
S-->>Client : "200 OK"
Client->>S : "PUT /collections/{name}/points"
S->>C : "Upsert(points)"
C->>D : "UpsertPoints"
C->>I : "Upsert"
S-->>Client : "200 OK"
Client->>S : "POST /collections/{name}/points/search"
S->>C : "Search(vector, filter, limit)"
C->>I : "Search"
S-->>Client : "200 OK"
Client->>S : "POST /collections/{name}/points/delete"
S->>C : "Delete(points, filter)"
C->>D : "DeletePoints"
C->>I : "Delete"
S-->>Client : "200 OK"
Data Models and Filters¶
- PointStruct/ScoredPoint: Carries vector, version number and metadata.
- Filter/Condition: Supports must/must_not conditions, types include exact, range, prefix, contains, regex.
- Matching algorithm: MatchFilter iterates through must/must_not, matches condition by condition; range, prefix, contains, regex have dedicated functions.
flowchart TD
F["Filter(Must/MustNot)"] --> CheckMust{"Iterate Must"}
CheckMust --> Cond1["Condition(Key, Type, Match/Range)"]
Cond1 --> MatchType{"Match type?"}
MatchType --> |exact| Exact["Value equals"]
MatchType --> |range| Range["Range judgment"]
MatchType --> |prefix| Prefix["Prefix match"]
MatchType --> |contains| Contains["Contains match"]
MatchType --> |regex| Regex["Regex match"]
Exact --> NextMust["Next Must"]
Range --> NextMust
Prefix --> NextMust
Contains --> NextMust
Regex --> NextMust
NextMust --> CheckMust
CheckMust --> |All satisfied| CheckMustNot{"Iterate MustNot"}
CheckMustNot --> Cond2["Condition match?"]
Cond2 --> |Match exists| Reject["Reject"]
Cond2 --> |None match| Accept["Accept"]
CheckMustNot --> |All satisfied| Accept
Dependency Analysis¶
- Component coupling and cohesion
- Collection couples with Storage and VectorIndex through interfaces, cohesive within "collection" semantics, clear responsibilities.
- Server only depends on Collection interface, does not directly care about underlying storage or index implementation.
- External dependencies
- bbolt for persistence; coder/hnsw for HNSW graph search; Protobuf for serialization.
- Circular dependencies
- No circular dependencies found, module boundaries are clear.
graph LR
Server["Server"] --> Collection["Collection"]
Collection --> Storage["Storage"]
Collection --> VectorIndex["VectorIndex"]
VectorIndex --> Flat["FlatIndex"]
VectorIndex --> HNSW["HNSWIndex"]
Server --> Models["Models(Filter/Point)"]
Collection --> Models
Storage --> Models
Performance Considerations¶
- Index selection
- Small-scale data recommends FlatIndex, avoids construction overhead; large-scale data recommends HNSWIndex, adjust parameters according to scenario (M, EfConstruction, EfSearch, K).
- Write path
- Upsert persists first then updates index, ensuring data not lost after crash; but may produce brief inconsistency window, recommend combining with idempotent strategy in high-concurrency scenarios.
- Query path
- HNSW uses post-filtering strategy; the heavier the filter, the larger fetchK needs to be to reduce missed candidates; can consider adding filtering during graph traversal (current implementation is post-filtering).
- Storage optimization
- Enabling SQ8 quantization can significantly reduce disk usage, auto-decompresses on load, pay attention to CPU pressure and precision trade-off.
Troubleshooting Guide¶
- Common errors and localization
- Collection does not exist: Server returns 404 when handling /points/* if collection not found.
- JSON parsing failed: handleUpsert/handleSearch/handleDelete return 400 for invalid JSON.
- Write failed: Collection.Upsert tries best-effort rollback of storage-side writes if index update fails.
- Dimension mismatch: Upsert/Search/Delete validates vector dimension before operation, returns error on mismatch.
- Test references
- Collection tests cover no-storage/with-storage, Upsert/search/delete, error scenarios.
- Server tests cover end-to-end verification of create/delete/list/get collections, Upsert/Search/Delete.
Conclusion¶
GoVector achieves high availability and high performance of "embedded vector database" through clear interface abstraction and layered design: - VectorIndex interface enables transparent Flat/HNSW strategy switching, facilitating scale-based evolution. - Strict write order and rollback strategy between Collection and Storage ensure data consistency. - Server uses Qdrant-compatible API as unified entry, facilitating integration and migration. - For extensibility, new VectorIndex implementations (like IVF/PQ) or alternative storage backends (like LSM) can be added to meet different scenario requirements.