linkrs_api/api_core/
bulk_insert.rs1use crate::CoreResult;
15use linkrs_core::{Edge, Vertex};
16use linkrs_storage::StorageClient;
17
18#[derive(Debug, Clone)]
20pub struct BulkInsertConfig {
21 pub batch_size: usize,
23 pub auto_flush: bool,
25 pub continue_on_error: bool,
27 pub max_errors: Option<usize>,
29}
30
31impl Default for BulkInsertConfig {
32 fn default() -> Self {
33 Self {
34 batch_size: 1000,
35 auto_flush: true,
36 continue_on_error: true,
37 max_errors: Some(100),
38 }
39 }
40}
41
42impl BulkInsertConfig {
43 pub fn new() -> Self {
45 Self::default()
46 }
47
48 pub fn with_batch_size(mut self, size: usize) -> Self {
50 self.batch_size = size.max(1);
51 self
52 }
53
54 pub fn with_auto_flush(mut self, auto_flush: bool) -> Self {
56 self.auto_flush = auto_flush;
57 self
58 }
59
60 pub fn with_continue_on_error(mut self, continue_on_error: bool) -> Self {
62 self.continue_on_error = continue_on_error;
63 self
64 }
65
66 pub fn with_max_errors(mut self, max_errors: Option<usize>) -> Self {
68 self.max_errors = max_errors;
69 self
70 }
71}
72
73#[derive(Debug, Clone)]
75pub enum BulkInsertItem {
76 Vertex(Vertex),
78 Edge(Edge),
80}
81
82#[derive(Debug, Clone, Default)]
84pub struct BulkInsertResult {
85 pub vertices_inserted: usize,
87 pub edges_inserted: usize,
89 pub failed_count: usize,
91 pub errors: Vec<BulkInsertError>,
93}
94
95#[derive(Debug, Clone)]
97pub struct BulkInsertError {
98 pub index: usize,
100 pub item_type: BulkInsertItemType,
102 pub message: String,
104}
105
106#[derive(Debug, Clone, Copy, PartialEq, Eq)]
108pub enum BulkInsertItemType {
109 Vertex,
111 Edge,
113}
114
115#[derive(Debug)]
121pub struct BulkInsertOperation {
122 items: Vec<BulkInsertItem>,
123 config: BulkInsertConfig,
124}
125
126impl BulkInsertOperation {
127 pub fn new(config: BulkInsertConfig) -> Self {
129 Self {
130 items: Vec::with_capacity(config.batch_size),
131 config,
132 }
133 }
134
135 pub fn add_vertex(&mut self, vertex: Vertex) {
137 self.items.push(BulkInsertItem::Vertex(vertex));
138 }
139
140 pub fn add_edge(&mut self, edge: Edge) {
142 self.items.push(BulkInsertItem::Edge(edge));
143 }
144
145 pub fn add_items(&mut self, items: Vec<BulkInsertItem>) {
147 self.items.extend(items);
148 }
149
150 pub fn len(&self) -> usize {
152 self.items.len()
153 }
154
155 pub fn is_empty(&self) -> bool {
157 self.items.is_empty()
158 }
159
160 pub fn should_flush(&self) -> bool {
162 self.config.auto_flush && self.items.len() >= self.config.batch_size
163 }
164
165 pub fn batch_size(&self) -> usize {
167 self.config.batch_size
168 }
169
170 pub fn clear(&mut self) {
172 self.items.clear();
173 }
174
175 pub fn take_items(&mut self) -> Vec<BulkInsertItem> {
177 std::mem::take(&mut self.items)
178 }
179
180 pub fn execute_sync<S: StorageClient>(
190 &mut self,
191 storage: &mut S,
192 space_name: &str,
193 ) -> CoreResult<BulkInsertResult> {
194 let items = self.take_items();
195 Self::execute_items_sync(storage, space_name, items, &self.config)
196 }
197
198 fn execute_items_sync<S: StorageClient>(
199 storage: &mut S,
200 space_name: &str,
201 items: Vec<BulkInsertItem>,
202 config: &BulkInsertConfig,
203 ) -> CoreResult<BulkInsertResult> {
204 let mut result = BulkInsertResult::default();
205 let mut vertices = Vec::new();
206 let mut edges = Vec::new();
207
208 for item in items {
209 match item {
210 BulkInsertItem::Vertex(v) => vertices.push(v),
211 BulkInsertItem::Edge(e) => edges.push(e),
212 }
213 }
214
215 if !vertices.is_empty() {
216 let vertex_count = vertices.len();
217 match storage.batch_insert_vertices(space_name, vertices) {
218 Ok(_) => {
219 result.vertices_inserted = vertex_count;
220 }
221 Err(e) => {
222 let error = BulkInsertError {
223 index: 0,
224 item_type: BulkInsertItemType::Vertex,
225 message: format!("Failed to insert vertices: {}", e),
226 };
227 result.errors.push(error);
228 result.failed_count += 1;
229 if !config.continue_on_error {
230 return Ok(result);
231 }
232 }
233 }
234 }
235
236 if !edges.is_empty() {
237 let edge_count = edges.len();
238 match storage.batch_insert_edges(space_name, edges) {
239 Ok(()) => {
240 result.edges_inserted = edge_count;
241 }
242 Err(e) => {
243 let error = BulkInsertError {
244 index: 0,
245 item_type: BulkInsertItemType::Edge,
246 message: format!("Failed to insert edges: {}", e),
247 };
248 result.errors.push(error);
249 result.failed_count += 1;
250 }
251 }
252 }
253
254 Ok(result)
255 }
256}
257
258#[derive(Debug)]
260pub struct BulkInsertOperationBuilder {
261 config: BulkInsertConfig,
262}
263
264impl BulkInsertOperationBuilder {
265 pub fn new() -> Self {
267 Self {
268 config: BulkInsertConfig::default(),
269 }
270 }
271
272 pub fn batch_size(mut self, size: usize) -> Self {
274 self.config.batch_size = size;
275 self
276 }
277
278 pub fn auto_flush(mut self, auto_flush: bool) -> Self {
280 self.config.auto_flush = auto_flush;
281 self
282 }
283
284 pub fn continue_on_error(mut self, continue_on_error: bool) -> Self {
286 self.config.continue_on_error = continue_on_error;
287 self
288 }
289
290 pub fn build(self) -> BulkInsertOperation {
292 BulkInsertOperation::new(self.config)
293 }
294}
295
296impl Default for BulkInsertOperationBuilder {
297 fn default() -> Self {
298 Self::new()
299 }
300}
301
302#[cfg(test)]
303mod tests {
304 use super::*;
305
306 #[test]
307 fn bulk_insert_config_default() {
308 let config = BulkInsertConfig::default();
309 assert_eq!(config.batch_size, 1000);
310 assert!(config.auto_flush);
311 assert!(config.continue_on_error);
312 }
313
314 #[test]
315 fn bulk_insert_config_builder() {
316 let config = BulkInsertConfig::new()
317 .with_batch_size(500)
318 .with_auto_flush(false)
319 .with_continue_on_error(true);
320
321 assert_eq!(config.batch_size, 500);
322 assert!(!config.auto_flush);
323 assert!(config.continue_on_error);
324 }
325
326 #[test]
327 fn bulk_insert_operation_add_items() {
328 let mut operation = BulkInsertOperation::new(BulkInsertConfig::default());
329
330 let vertex = Vertex::new(
331 linkrs_core::types::VertexId::try_from_int64(1).expect("test vertex id"),
332 linkrs_core::Tag::new("test".into(), std::collections::HashMap::new()),
333 );
334 operation.add_vertex(vertex);
335
336 assert_eq!(operation.len(), 1);
337 assert!(!operation.is_empty());
338 }
339
340 #[test]
341 fn bulk_insert_operation_should_flush() {
342 let config = BulkInsertConfig::new().with_batch_size(2);
343 let mut operation = BulkInsertOperation::new(config);
344
345 assert!(!operation.should_flush());
346
347 let vertex = Vertex::new(
348 linkrs_core::types::VertexId::try_from_int64(1).expect("test vertex id"),
349 linkrs_core::Tag::new("test".into(), std::collections::HashMap::new()),
350 );
351 operation.add_vertex(vertex.clone());
352 operation.add_vertex(vertex);
353
354 assert!(operation.should_flush());
355 }
356}