Skip to main content

sz_orm_vector/
lib.rs

1//! SZ-ORM pgvector 扩展
2//!
3//! 提供 PostgreSQL pgvector 向量相似度搜索能力,支持三种实现:
4//!
5//! - **内存实现**(`InMemoryVectorStore`):纯 Rust 向量计算,不连接数据库,适用于测试和基准
6//! - **Stub 实现**(`StubVectorStore`):所有方法返回 Unsupported,适用于调试占位
7//! - **真实实现**(`RealPgVectorStore`,需启用 `real-pg` feature):通过 tokio-postgres 连接 PostgreSQL + pgvector
8//!
9//! # 快速入门
10//!
11//! ```rust
12//! use sz_orm_vector::{InMemoryVectorStore, PgVectorStore, VectorRecord, VectorMetric};
13//!
14//! # #[tokio::main]
15//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
16//! let store = InMemoryVectorStore::new();
17//! store.create_collection("docs", 3, None).await?;
18//!
19//! let record = VectorRecord::new("doc1", vec![1.0, 0.0, 0.0]);
20//! store.insert("docs", vec![record]).await?;
21//!
22//! let results = store.search("docs", &[1.0, 0.0, 0.0], 5).await?;
23//! println!("found {} results", results.len());
24//! # Ok(())
25//! # }
26//! ```
27
28pub mod error;
29pub mod extensions;
30pub mod memory;
31pub mod stub;
32
33#[cfg(feature = "real-pg")]
34pub mod real_pg;
35
36pub use error::VectorError;
37pub use extensions::{
38    AnnIndexDef, AnnIndexRegistry, AnnIndexType, BatchOpsExt, DimensionValidator, HnswParams,
39    IvfflatParams, MemoryBatchOps, SimilarityAlgorithms, VectorNormalizer, MAX_VECTOR_DIMENSION,
40    MIN_VECTOR_DIMENSION,
41};
42pub use memory::InMemoryVectorStore;
43pub use stub::StubVectorStore;
44
45#[cfg(feature = "real-pg")]
46pub use real_pg::{RealPgConfig, RealPgVectorStore};
47
48use async_trait::async_trait;
49use std::collections::HashMap;
50use std::str::FromStr;
51
52/// M-16 修复:top_k 最大限制
53///
54/// 限制 top_k 上限以防止:
55/// - 大 k 值导致内存爆炸(每个 SearchResult 包含完整向量)
56/// - 数据库/向量引擎执行超大 k 查询的性能问题
57/// - 恶意调用方通过 top_k=usize::MAX 触发 OOM
58pub const MAX_TOP_K: usize = 10_000;
59
60/// M-16 修复:校验 top_k 是否在合理范围内
61///
62/// - `top_k = 0`:返回 `TopKExceeded` 错误(无意义的查询)
63/// - `top_k > MAX_TOP_K`:返回 `TopKExceeded` 错误
64/// - `1 <= top_k <= MAX_TOP_K`:返回 Ok
65pub fn validate_top_k(top_k: usize) -> Result<usize, VectorError> {
66    if top_k == 0 {
67        return Err(VectorError::TopKExceeded {
68            requested: top_k,
69            max: MAX_TOP_K,
70        });
71    }
72    if top_k > MAX_TOP_K {
73        return Err(VectorError::TopKExceeded {
74            requested: top_k,
75            max: MAX_TOP_K,
76        });
77    }
78    Ok(top_k)
79}
80
81/// 向量记录
82#[derive(Debug, Clone)]
83pub struct VectorRecord {
84    pub id: String,
85    pub vector: Vec<f32>,
86    pub score: Option<f32>,
87    pub metadata: Option<HashMap<String, serde_json::Value>>,
88}
89
90impl VectorRecord {
91    pub fn new(id: impl Into<String>, vector: Vec<f32>) -> Self {
92        Self {
93            id: id.into(),
94            vector,
95            score: None,
96            metadata: None,
97        }
98    }
99
100    pub fn with_score(mut self, score: f32) -> Self {
101        self.score = Some(score);
102        self
103    }
104
105    pub fn with_metadata(mut self, metadata: HashMap<String, serde_json::Value>) -> Self {
106        self.metadata = Some(metadata);
107        self
108    }
109}
110
111/// 搜索结果
112#[derive(Debug, Clone)]
113pub struct SearchResult {
114    pub id: String,
115    pub score: f32,
116    pub vector: Vec<f32>,
117    pub text: Option<String>,
118    pub metadata: Option<HashMap<String, serde_json::Value>>,
119}
120
121impl SearchResult {
122    pub fn new(id: impl Into<String>, score: f32, vector: Vec<f32>) -> Self {
123        Self {
124            id: id.into(),
125            score,
126            vector,
127            text: None,
128            metadata: None,
129        }
130    }
131
132    pub fn with_text(mut self, text: impl Into<String>) -> Self {
133        self.text = Some(text.into());
134        self
135    }
136
137    pub fn with_metadata(mut self, metadata: HashMap<String, serde_json::Value>) -> Self {
138        self.metadata = Some(metadata);
139        self
140    }
141}
142
143/// 向量距离度量
144#[derive(Debug, Clone, Copy, Default, PartialEq)]
145pub enum VectorMetric {
146    #[default]
147    Cosine,
148    Euclidean,
149    DotProduct,
150}
151
152impl VectorMetric {
153    /// pgvector 操作符映射
154    pub fn pg_operator(&self) -> &'static str {
155        match self {
156            VectorMetric::Cosine => "<=>",
157            VectorMetric::Euclidean => "<->",
158            VectorMetric::DotProduct => "<#>",
159        }
160    }
161
162    pub fn as_str(&self) -> &'static str {
163        match self {
164            VectorMetric::Cosine => "cosine",
165            VectorMetric::Euclidean => "euclidean",
166            VectorMetric::DotProduct => "dotproduct",
167        }
168    }
169}
170
171impl FromStr for VectorMetric {
172    type Err = String;
173
174    fn from_str(s: &str) -> Result<Self, Self::Err> {
175        match s {
176            "cosine" => Ok(VectorMetric::Cosine),
177            "euclidean" => Ok(VectorMetric::Euclidean),
178            "dotproduct" => Ok(VectorMetric::DotProduct),
179            _ => Err(format!("unknown vector metric: {}", s)),
180        }
181    }
182}
183
184/// Vector Store 核心 trait
185///
186/// 提供向量集合的 CRUD 和相似度搜索能力。
187/// 所有方法均为 async,适用于真实数据库 I/O。
188#[async_trait]
189pub trait PgVectorStore: Send + Sync {
190    /// 创建集合
191    async fn create_collection(
192        &self,
193        name: &str,
194        dimension: usize,
195        metric: Option<VectorMetric>,
196    ) -> Result<(), VectorError>;
197
198    /// 删除集合
199    async fn delete_collection(&self, name: &str) -> Result<(), VectorError>;
200
201    /// 插入向量记录(upsert 语义:相同 id 会覆盖)
202    async fn insert(&self, collection: &str, records: Vec<VectorRecord>)
203        -> Result<(), VectorError>;
204
205    /// 相似度搜索
206    ///
207    /// M-16 修复:`top_k` 必须在 `[1, MAX_TOP_K]` 范围内。
208    /// 实现方应在执行搜索前调用 `validate_top_k(top_k)?` 进行校验。
209    async fn search(
210        &self,
211        collection: &str,
212        query: &[f32],
213        top_k: usize,
214    ) -> Result<Vec<SearchResult>, VectorError>;
215
216    /// 获取单个记录
217    async fn get(&self, collection: &str, id: &str) -> Result<Option<VectorRecord>, VectorError>;
218
219    /// 删除记录
220    async fn delete(&self, collection: &str, ids: Vec<String>) -> Result<u64, VectorError>;
221
222    /// 统计记录数
223    async fn count(&self, collection: &str) -> Result<usize, VectorError>;
224}