1pub 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
52pub const MAX_TOP_K: usize = 10_000;
59
60pub 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#[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#[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#[derive(Debug, Clone, Copy, Default, PartialEq)]
145pub enum VectorMetric {
146 #[default]
147 Cosine,
148 Euclidean,
149 DotProduct,
150}
151
152impl VectorMetric {
153 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#[async_trait]
189pub trait PgVectorStore: Send + Sync {
190 async fn create_collection(
192 &self,
193 name: &str,
194 dimension: usize,
195 metric: Option<VectorMetric>,
196 ) -> Result<(), VectorError>;
197
198 async fn delete_collection(&self, name: &str) -> Result<(), VectorError>;
200
201 async fn insert(&self, collection: &str, records: Vec<VectorRecord>)
203 -> Result<(), VectorError>;
204
205 async fn search(
210 &self,
211 collection: &str,
212 query: &[f32],
213 top_k: usize,
214 ) -> Result<Vec<SearchResult>, VectorError>;
215
216 async fn get(&self, collection: &str, id: &str) -> Result<Option<VectorRecord>, VectorError>;
218
219 async fn delete(&self, collection: &str, ids: Vec<String>) -> Result<u64, VectorError>;
221
222 async fn count(&self, collection: &str) -> Result<usize, VectorError>;
224}