oxirs-star 0.3.1

RDF-star and SPARQL-star grammar support for quoted triples
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
//! Core RDF-star store implementation.
//!
//! This sibling module of `crate::store` defines the [`StarStore`] struct,
//! its internal state helpers, basic constructor / mutator operations, and the
//! conversion helpers that turn [`StarTerm`] values into core RDF terms.

use std::sync::{Arc, RwLock};
use std::time::Instant;

use oxirs_core::rdf_store::{ConcreteStore as CoreStore, Store};
use tracing::{debug, info, span, Level};

use crate::model::{StarTerm, StarTriple};
use crate::store::{cache_mod, conversion, index};
use crate::{StarConfig, StarError, StarResult, StarStatistics};

use cache_mod::{CacheConfig, StarCache};
use index::QuotedTripleIndex;

/// RDF-star storage backend with support for quoted triples
#[derive(Clone)]
pub struct StarStore {
    /// Core RDF storage backend
    pub(crate) core_store: Arc<RwLock<CoreStore>>,
    /// RDF-star specific triples (those containing quoted triples)
    pub(crate) star_triples: Arc<RwLock<Vec<StarTriple>>>,
    /// Enhanced B-tree based quoted triple index for efficient lookup
    pub(crate) quoted_triple_index: Arc<RwLock<QuotedTripleIndex>>,
    /// Configuration for the store
    pub(crate) config: StarConfig,
    /// Statistics tracking
    pub(crate) statistics: Arc<RwLock<StarStatistics>>,
    /// Cache for frequently accessed data
    pub(crate) cache: Arc<StarCache>,
    /// Bulk insertion state
    pub(crate) bulk_insert_state: Arc<RwLock<BulkInsertState>>,
    /// Memory-mapped storage state
    pub(crate) memory_mapped: Arc<RwLock<MemoryMappedState>>,
}

/// State tracking for bulk insertion operations
#[derive(Debug, Default)]
pub(crate) struct BulkInsertState {
    /// Whether bulk insertion is currently active
    pub(crate) active: bool,
    /// Pending triples waiting to be indexed
    pub(crate) pending_triples: Vec<StarTriple>,
    /// Memory usage tracking for bulk operations
    pub(crate) current_memory_usage: usize,
    /// Batch count for monitoring
    pub(crate) batch_count: usize,
}

/// State for memory-mapped storage operations
#[derive(Debug, Default)]
pub(crate) struct MemoryMappedState {
    /// Whether memory mapping is enabled
    pub(crate) enabled: bool,
    /// Path to the memory-mapped file
    pub(crate) file_path: Option<String>,
    /// Compression settings for stored data
    pub(crate) compression_enabled: bool,
    /// Last sync timestamp
    pub(crate) last_sync: Option<Instant>,
}

impl StarStore {
    /// Create a new RDF-star store with default configuration
    pub fn new() -> Self {
        Self::with_config(StarConfig::default())
    }

    /// Create a new RDF-star store with custom configuration
    pub fn with_config(config: StarConfig) -> Self {
        let span = span!(Level::INFO, "new_star_store");
        let _enter = span.enter();

        info!("Creating new RDF-star store with optimizations");
        debug!("Configuration: {:?}", config);

        Self {
            core_store: Arc::new(RwLock::new(
                CoreStore::new().expect("Failed to create core store"),
            )),
            star_triples: Arc::new(RwLock::new(Vec::new())),
            quoted_triple_index: Arc::new(RwLock::new(QuotedTripleIndex::new())),
            config: config.clone(),
            statistics: Arc::new(RwLock::new(StarStatistics::default())),
            cache: Arc::new(StarCache::new(CacheConfig::default())),
            bulk_insert_state: Arc::new(RwLock::new(BulkInsertState::default())),
            memory_mapped: Arc::new(RwLock::new(MemoryMappedState::default())),
        }
    }

    /// Get the store configuration
    pub fn config(&self) -> &StarConfig {
        &self.config
    }

    /// Insert a RDF-star triple into the store
    pub fn insert(&self, triple: &StarTriple) -> StarResult<()> {
        let span = span!(Level::DEBUG, "insert_triple");
        let _enter = span.enter();

        let start_time = Instant::now();

        // Validate the triple
        triple.validate()?;

        // Check nesting depth
        crate::validate_nesting_depth(&triple.subject, self.config.max_nesting_depth)?;
        crate::validate_nesting_depth(&triple.predicate, self.config.max_nesting_depth)?;
        crate::validate_nesting_depth(&triple.object, self.config.max_nesting_depth)?;

        // Insert into appropriate storage
        eprintln!(
            "DEBUG INSERT: Triple contains quoted triples: {}",
            triple.contains_quoted_triples()
        );
        if triple.contains_quoted_triples() {
            eprintln!("DEBUG INSERT: Inserting as star triple");
            self.insert_star_triple(triple)?;
        } else {
            eprintln!("DEBUG INSERT: Inserting as regular triple");
            self.insert_regular_triple(triple)?;
        }

        // Update statistics
        {
            let mut stats = self.statistics.write().unwrap_or_else(|e| e.into_inner());
            stats.processing_time_us += start_time.elapsed().as_micros() as u64;
            if triple.contains_quoted_triples() {
                stats.quoted_triples_count += 1;
                stats.max_nesting_encountered =
                    stats.max_nesting_encountered.max(triple.nesting_depth());
            }
        }

        debug!("Inserted triple: {}", triple);
        Ok(())
    }

    /// Insert a regular RDF triple (no quoted triples) into core store
    pub(crate) fn insert_regular_triple(&self, triple: &StarTriple) -> StarResult<()> {
        eprintln!("DEBUG: Inserting regular triple into core store");

        // Convert StarTriple to core RDF triple
        let core_triple = self.convert_to_core_triple(triple)?;
        eprintln!("DEBUG: Converted to core triple successfully");

        // Insert into core store (convert triple to quad in default graph)
        let core_quad = oxirs_core::model::Quad::from_triple(core_triple);
        eprintln!("DEBUG: Created core quad for insertion: {core_quad:?}");
        let core_store = self.core_store.write().unwrap_or_else(|e| e.into_inner());
        let result = CoreStore::insert_quad(&core_store, core_quad).map_err(StarError::CoreError);
        eprintln!("DEBUG: Core store insert result: {result:?}");
        result?;

        eprintln!("DEBUG: Successfully inserted regular triple");
        Ok(())
    }

    /// Convert a StarTriple (without quoted triples) to a core RDF Triple
    pub(crate) fn convert_to_core_triple(
        &self,
        triple: &StarTriple,
    ) -> StarResult<oxirs_core::model::Triple> {
        let subject = conversion::star_term_to_subject(&triple.subject)?;
        let predicate = conversion::star_term_to_predicate(&triple.predicate)?;
        let object = conversion::star_term_to_object(&triple.object)?;

        Ok(oxirs_core::model::Triple::new(subject, predicate, object))
    }

    /// Insert a RDF-star triple (containing quoted triples) into star storage
    pub(crate) fn insert_star_triple(&self, triple: &StarTriple) -> StarResult<()> {
        let mut star_triples = self.star_triples.write().unwrap_or_else(|e| e.into_inner());
        let mut index = self
            .quoted_triple_index
            .write()
            .unwrap_or_else(|e| e.into_inner());

        let triple_index = star_triples.len();
        star_triples.push(triple.clone());

        // Build index for quoted triples
        self.index_quoted_triples(triple, triple_index, &mut index);

        debug!(
            "Inserted star triple with {} quoted triples",
            self.count_quoted_triples_in_triple(triple)
        );
        Ok(())
    }

    /// Remove a triple from the store
    pub fn remove(&self, triple: &StarTriple) -> StarResult<bool> {
        let span = span!(Level::DEBUG, "remove_triple");
        let _enter = span.enter();

        eprintln!("DEBUG: Attempting to remove triple: {triple}");
        eprintln!(
            "DEBUG: Triple contains quoted triples: {}",
            triple.contains_quoted_triples()
        );

        // First try to remove from star triples
        if triple.contains_quoted_triples() {
            let mut star_triples = self.star_triples.write().unwrap_or_else(|e| e.into_inner());

            if let Some(pos) = star_triples.iter().position(|t| t == triple) {
                star_triples.remove(pos);

                // Update all indices
                let mut index = self
                    .quoted_triple_index
                    .write()
                    .unwrap_or_else(|e| e.into_inner());

                // Update signature index
                for (_, indices) in index.signature_to_indices.iter_mut() {
                    Self::update_indices_after_removal(indices, pos);
                }

                // Update subject index
                for (_, indices) in index.subject_index.iter_mut() {
                    Self::update_indices_after_removal(indices, pos);
                }

                // Update predicate index
                for (_, indices) in index.predicate_index.iter_mut() {
                    Self::update_indices_after_removal(indices, pos);
                }

                // Update object index
                for (_, indices) in index.object_index.iter_mut() {
                    Self::update_indices_after_removal(indices, pos);
                }

                // Update nesting depth index
                for (_, indices) in index.nesting_depth_index.iter_mut() {
                    Self::update_indices_after_removal(indices, pos);
                }

                debug!("Removed star triple: {}", triple);
                return Ok(true);
            }
        } else {
            // Try to remove from core store for regular triples
            eprintln!("DEBUG: Attempting to remove regular triple from core store");
            let core_store = self.core_store.write().unwrap_or_else(|e| e.into_inner());
            if let Ok(core_triple) = self.convert_to_core_triple(triple) {
                eprintln!("DEBUG: Successfully converted to core triple");
                let core_quad = oxirs_core::model::Quad::from_triple(core_triple);
                eprintln!("DEBUG: Created core quad: {core_quad:?}");
                match CoreStore::remove_quad(&core_store, &core_quad) {
                    Ok(removed) => {
                        eprintln!("DEBUG: Core store remove_quad returned: {removed}");
                        if removed {
                            eprintln!("DEBUG: Removed regular triple: {triple}");
                            return Ok(true);
                        } else {
                            eprintln!(
                                "DEBUG: Core store remove_quad returned false - triple not found"
                            );
                        }
                    }
                    Err(e) => {
                        eprintln!("DEBUG: Core store remove_quad failed with error: {e:?}");
                    }
                }
            } else {
                eprintln!("DEBUG: Failed to convert triple to core triple");
            }
        }

        Ok(false)
    }

    /// Check if the store contains a specific triple
    pub fn contains(&self, triple: &StarTriple) -> bool {
        // First check star triples
        let star_triples = self.star_triples.read().unwrap_or_else(|e| e.into_inner());
        if star_triples.contains(triple) {
            return true;
        }

        // Then check regular triples in core store
        if !triple.contains_quoted_triples() {
            let core_store = self.core_store.read().unwrap_or_else(|e| e.into_inner());
            if let Ok(core_triple) = self.convert_to_core_triple(triple) {
                // Convert triple to quad with default graph
                let core_quad = oxirs_core::model::Quad::from_triple(core_triple);
                if let Ok(quads) = core_store.find_quads(
                    Some(core_quad.subject()),
                    Some(core_quad.predicate()),
                    Some(core_quad.object()),
                    Some(core_quad.graph_name()),
                ) {
                    return !quads.is_empty();
                }
            }
        }

        false
    }

    /// Get the number of triples in the store
    pub fn len(&self) -> usize {
        let star_triples = self.star_triples.read().unwrap_or_else(|e| e.into_inner());
        let core_store = self.core_store.read().unwrap_or_else(|e| e.into_inner());

        // Count both star triples and regular triples from core store
        let regular_count = core_store.len().unwrap_or(0);
        let star_count = star_triples.len();

        regular_count + star_count
    }

    /// Check if the store is empty
    pub fn is_empty(&self) -> bool {
        let star_triples = self.star_triples.read().unwrap_or_else(|e| e.into_inner());
        let core_store = self.core_store.read().unwrap_or_else(|e| e.into_inner());

        // Empty only if both stores are empty
        star_triples.is_empty() && core_store.is_empty().unwrap_or(true)
    }

    /// Clear all triples from the store
    pub fn clear(&self) -> StarResult<()> {
        let span = span!(Level::INFO, "clear_store");
        let _enter = span.enter();

        {
            let mut star_triples = self.star_triples.write().unwrap_or_else(|e| e.into_inner());
            star_triples.clear();
        }

        // Clear the core store by recreating it
        // Note: This is a workaround since clear_all/remove_quad have trait/impl conflicts
        {
            let mut core_store = self.core_store.write().unwrap_or_else(|e| e.into_inner());
            *core_store = CoreStore::new().map_err(StarError::CoreError)?;
        }

        {
            let mut index = self
                .quoted_triple_index
                .write()
                .unwrap_or_else(|e| e.into_inner());
            index.clear();
        }

        {
            let mut stats = self.statistics.write().unwrap_or_else(|e| e.into_inner());
            *stats = StarStatistics::default();
        }

        info!("Cleared all triples from store");
        Ok(())
    }

    /// Get statistics about the store
    pub fn statistics(&self) -> StarStatistics {
        let stats = self.statistics.read().unwrap_or_else(|e| e.into_inner());
        stats.clone()
    }

    /// Check if a triple matches the given pattern
    pub(crate) fn triple_matches(
        &self,
        triple: &StarTriple,
        subject: Option<&StarTerm>,
        predicate: Option<&StarTerm>,
        object: Option<&StarTerm>,
    ) -> bool {
        if let Some(s) = subject {
            if &triple.subject != s {
                return false;
            }
        }
        if let Some(p) = predicate {
            if &triple.predicate != p {
                return false;
            }
        }
        if let Some(o) = object {
            if &triple.object != o {
                return false;
            }
        }
        true
    }

    /// Update configuration (requires store recreation for some settings)
    pub fn update_config(&mut self, config: StarConfig) -> StarResult<()> {
        // Validate new configuration
        crate::init_star_system(config.clone())?;
        self.config = config;
        Ok(())
    }
}

impl Default for StarStore {
    fn default() -> Self {
        Self::new()
    }
}