use crate::api_core::{
BulkInsertError as CoreBatchError, BulkInsertItemType as CoreBatchItemType,
BulkInsertOperation as CoreBatchOperation, BulkInsertResult as CoreBatchResult,
};
use crate::api_core::{CoreError, CoreResult};
use crate::embedded::session::Session;
use linkrs_core::{Edge, Vertex};
use linkrs_storage::StorageClient;
pub struct BatchInserter<'sess, S: StorageClient + Clone + Send + Sync + 'static> {
session: &'sess Session<S>,
core_operation: CoreBatchOperation,
}
#[derive(Debug, Clone, Default)]
pub struct BatchResult {
pub vertices_inserted: usize,
pub edges_inserted: usize,
pub errors: Vec<BatchError>,
}
impl BatchResult {
pub fn total_inserted(&self) -> usize {
self.vertices_inserted + self.edges_inserted
}
pub fn has_errors(&self) -> bool {
!self.errors.is_empty()
}
pub fn error_count(&self) -> usize {
self.errors.len()
}
pub fn merge(&mut self, other: BatchResult) {
self.vertices_inserted += other.vertices_inserted;
self.edges_inserted += other.edges_inserted;
self.errors.extend(other.errors);
}
}
impl From<CoreBatchResult> for BatchResult {
fn from(result: CoreBatchResult) -> Self {
Self {
vertices_inserted: result.vertices_inserted,
edges_inserted: result.edges_inserted,
errors: result.errors.into_iter().map(Into::into).collect(),
}
}
}
#[derive(Debug, Clone)]
pub struct BatchError {
pub index: usize,
pub item_type: BatchItemType,
pub error: String,
}
impl BatchError {
pub fn new(index: usize, item_type: BatchItemType, error: impl Into<String>) -> Self {
Self {
index,
item_type,
error: error.into(),
}
}
}
impl From<CoreBatchError> for BatchError {
fn from(error: CoreBatchError) -> Self {
Self {
index: error.index,
item_type: error.item_type.into(),
error: error.message,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BatchItemType {
Vertex,
Edge,
}
impl From<CoreBatchItemType> for BatchItemType {
fn from(item_type: CoreBatchItemType) -> Self {
match item_type {
CoreBatchItemType::Vertex => BatchItemType::Vertex,
CoreBatchItemType::Edge => BatchItemType::Edge,
}
}
}
impl<'sess, S: StorageClient + Clone + Send + Sync + 'static + linkrs_storage::UndoTarget>
BatchInserter<'sess, S>
{
pub(crate) fn new(session: &'sess Session<S>, batch_size: usize) -> Self {
let config = crate::api_core::BulkInsertConfig::new().with_batch_size(batch_size);
Self {
session,
core_operation: CoreBatchOperation::new(config),
}
}
pub fn add_vertex(&mut self, vertex: Vertex) -> &mut Self {
self.core_operation.add_vertex(vertex);
if self.core_operation.should_flush() {
let _ = self.flush();
}
self
}
pub fn add_edge(&mut self, edge: Edge) -> &mut Self {
self.core_operation.add_edge(edge);
if self.core_operation.should_flush() {
let _ = self.flush();
}
self
}
pub fn add_vertices(&mut self, vertices: Vec<Vertex>) -> &mut Self {
for vertex in vertices {
self.add_vertex(vertex);
}
self
}
pub fn add_edges(&mut self, edges: Vec<Edge>) -> &mut Self {
for edge in edges {
self.add_edge(edge);
}
self
}
pub fn execute(mut self) -> CoreResult<BatchResult> {
let space_name = self
.session
.space_name()
.ok_or_else(|| CoreError::InvalidParameter("No graph space selected".to_string()))?;
let mut storage = self.session.storage_mut();
let core_result = self
.core_operation
.execute_sync(&mut *storage, &space_name)?;
Ok(core_result.into())
}
fn flush(&mut self) -> CoreResult<()> {
if self.core_operation.is_empty() {
return Ok(());
}
let space_name = self
.session
.space_name()
.ok_or_else(|| CoreError::InvalidParameter("No graph space selected".to_string()))?;
let mut storage = self.session.storage_mut();
let _ = self
.core_operation
.execute_sync(&mut *storage, &space_name)?;
Ok(())
}
pub fn buffered_count(&self) -> usize {
self.core_operation.len()
}
pub fn buffered_vertices(&self) -> usize {
self.core_operation.len()
}
pub fn buffered_edges(&self) -> usize {
self.core_operation.len()
}
pub fn batch_size(&self) -> usize {
self.core_operation.batch_size()
}
pub fn has_buffered_data(&self) -> bool {
!self.core_operation.is_empty()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_batch_result_from_core() {
let core_result = CoreBatchResult {
vertices_inserted: 10,
edges_inserted: 5,
failed_count: 1,
errors: vec![CoreBatchError {
index: 0,
item_type: CoreBatchItemType::Vertex,
message: "Test error".to_string(),
}],
};
let result: BatchResult = core_result.into();
assert_eq!(result.vertices_inserted, 10);
assert_eq!(result.edges_inserted, 5);
assert_eq!(result.errors.len(), 1);
}
}