oxigraph/store.rs
1//! API to access an on-disk [RDF dataset](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-dataset).
2//!
3//! The entry point of the module is the [`Store`] struct.
4//!
5//! Usage example:
6//! ```
7//! use oxigraph::model::*;
8//! use oxigraph::sparql::QueryResults;
9//! use oxigraph::store::Store;
10//!
11//! let store = Store::new()?;
12//!
13//! // insertion
14//! let ex = NamedNode::new("http://example.com")?;
15//! let quad = Quad::new(ex.clone(), ex.clone(), ex.clone(), GraphName::DefaultGraph);
16//! store.insert(&quad)?;
17//!
18//! // quad filter
19//! let results: Result<Vec<Quad>, _> = store.quads_for_pattern(None, None, None, None).collect();
20//! assert_eq!(vec![quad], results?);
21//!
22//! // SPARQL query
23//! if let QueryResults::Solutions(mut solutions) = store.query("SELECT ?s WHERE { ?s ?p ?o }")? {
24//! assert_eq!(solutions.next().unwrap()?.get("s"), Some(&ex.into()));
25//! };
26//! # Result::<_, Box<dyn std::error::Error>>::Ok(())
27//! ```
28use crate::io::{RdfFormat, RdfParseError, RdfParser, RdfSerializer};
29use crate::model::*;
30use crate::sparql::{
31 evaluate_query, evaluate_update, EvaluationError, Query, QueryExplanation, QueryOptions,
32 QueryResults, Update, UpdateOptions,
33};
34use crate::storage::numeric_encoder::{Decoder, EncodedQuad, EncodedTerm};
35pub use crate::storage::{CorruptionError, LoaderError, SerializerError, StorageError};
36use crate::storage::{
37 DecodingGraphIterator, DecodingQuadIterator, Storage, StorageBulkLoader, StorageReader,
38 StorageWriter,
39};
40use std::error::Error;
41use std::io::{Read, Write};
42#[cfg(all(not(target_family = "wasm"), feature = "rocksdb"))]
43use std::path::Path;
44use std::{fmt, str};
45
46/// An on-disk [RDF dataset](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-dataset).
47/// Allows to query and update it using SPARQL.
48/// It is based on the [RocksDB](https://rocksdb.org/) key-value store.
49///
50/// This store ensures the "repeatable read" isolation level: the store only exposes changes that have
51/// been "committed" (i.e. no partial writes) and the exposed state does not change for the complete duration
52/// of a read operation (e.g. a SPARQL query) or a read/write operation (e.g. a SPARQL update).
53///
54/// Usage example:
55/// ```
56/// use oxigraph::model::*;
57/// use oxigraph::sparql::QueryResults;
58/// use oxigraph::store::Store;
59/// # use std::fs::remove_dir_all;
60///
61/// # {
62/// let store = Store::open("example.db")?;
63///
64/// // insertion
65/// let ex = NamedNode::new("http://example.com")?;
66/// let quad = Quad::new(ex.clone(), ex.clone(), ex.clone(), GraphName::DefaultGraph);
67/// store.insert(&quad)?;
68///
69/// // quad filter
70/// let results: Result<Vec<Quad>, _> = store.quads_for_pattern(None, None, None, None).collect();
71/// assert_eq!(vec![quad], results?);
72///
73/// // SPARQL query
74/// if let QueryResults::Solutions(mut solutions) = store.query("SELECT ?s WHERE { ?s ?p ?o }")? {
75/// assert_eq!(solutions.next().unwrap()?.get("s"), Some(&ex.into()));
76/// };
77/// #
78/// # };
79/// # remove_dir_all("example.db")?;
80/// # Result::<_, Box<dyn std::error::Error>>::Ok(())
81/// ```
82#[derive(Clone)]
83pub struct Store {
84 storage: Storage,
85}
86
87impl Store {
88 /// New in-memory [`Store`] without RocksDB.
89 pub fn new() -> Result<Self, StorageError> {
90 Ok(Self {
91 storage: Storage::new()?,
92 })
93 }
94
95 /// Opens a read-write [`Store`] and creates it if it does not exist yet.
96 ///
97 /// Only one read-write [`Store`] can exist at the same time.
98 /// If you want to have extra [`Store`] instance opened on the same data
99 /// use [`Store::open_read_only`].
100 #[cfg(all(not(target_family = "wasm"), feature = "rocksdb"))]
101 pub fn open(path: impl AsRef<Path>) -> Result<Self, StorageError> {
102 Ok(Self {
103 storage: Storage::open(path.as_ref())?,
104 })
105 }
106
107 /// Opens a read-only [`Store`] from disk.
108 ///
109 /// Opening as read-only while having an other process writing the database is undefined behavior.
110 #[cfg(all(not(target_family = "wasm"), feature = "rocksdb"))]
111 pub fn open_read_only(path: impl AsRef<Path>) -> Result<Self, StorageError> {
112 Ok(Self {
113 storage: Storage::open_read_only(path.as_ref())?,
114 })
115 }
116
117 /// Executes a [SPARQL 1.1 query](https://www.w3.org/TR/sparql11-query/).
118 ///
119 /// Usage example:
120 /// ```
121 /// use oxigraph::model::*;
122 /// use oxigraph::sparql::QueryResults;
123 /// use oxigraph::store::Store;
124 ///
125 /// let store = Store::new()?;
126 ///
127 /// // insertions
128 /// let ex = NamedNodeRef::new("http://example.com")?;
129 /// store.insert(QuadRef::new(ex, ex, ex, GraphNameRef::DefaultGraph))?;
130 ///
131 /// // SPARQL query
132 /// if let QueryResults::Solutions(mut solutions) = store.query("SELECT ?s WHERE { ?s ?p ?o }")? {
133 /// assert_eq!(
134 /// solutions.next().unwrap()?.get("s"),
135 /// Some(&ex.into_owned().into())
136 /// );
137 /// }
138 /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
139 /// ```
140 pub fn query(
141 &self,
142 query: impl TryInto<Query, Error = impl Into<EvaluationError>>,
143 ) -> Result<QueryResults, EvaluationError> {
144 self.query_opt(query, QueryOptions::default())
145 }
146
147 /// Executes a [SPARQL 1.1 query](https://www.w3.org/TR/sparql11-query/) with some options.
148 ///
149 /// Usage example with a custom function serializing terms to N-Triples:
150 /// ```
151 /// use oxigraph::model::*;
152 /// use oxigraph::sparql::{QueryOptions, QueryResults};
153 /// use oxigraph::store::Store;
154 ///
155 /// let store = Store::new()?;
156 /// if let QueryResults::Solutions(mut solutions) = store.query_opt(
157 /// "SELECT (<http://www.w3.org/ns/formats/N-Triples>(1) AS ?nt) WHERE {}",
158 /// QueryOptions::default().with_custom_function(
159 /// NamedNode::new("http://www.w3.org/ns/formats/N-Triples")?,
160 /// |args| args.get(0).map(|t| Literal::from(t.to_string()).into()),
161 /// ),
162 /// )? {
163 /// assert_eq!(
164 /// solutions.next().unwrap()?.get("nt"),
165 /// Some(&Literal::from("\"1\"^^<http://www.w3.org/2001/XMLSchema#integer>").into())
166 /// );
167 /// }
168 /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
169 /// ```
170 pub fn query_opt(
171 &self,
172 query: impl TryInto<Query, Error = impl Into<EvaluationError>>,
173 options: QueryOptions,
174 ) -> Result<QueryResults, EvaluationError> {
175 let (results, _) = self.explain_query_opt(query, options, false)?;
176 results
177 }
178
179 /// Executes a [SPARQL 1.1 query](https://www.w3.org/TR/sparql11-query/) with some options while substituting some variables with the given values.
180 ///
181 /// Substitution follows [RDF-dev SEP-0007](https://github.com/w3c/sparql-dev/blob/main/SEP/SEP-0007/sep-0007.md).
182 ///
183 /// Usage example with a custom function serializing terms to N-Triples:
184 /// ```
185 /// use oxigraph::model::{Literal, Variable};
186 /// use oxigraph::sparql::{QueryOptions, QueryResults};
187 /// use oxigraph::store::Store;
188 ///
189 /// let store = Store::new()?;
190 /// if let QueryResults::Solutions(mut solutions) = store.query_opt_with_substituted_variables(
191 /// "SELECT ?v WHERE {}",
192 /// QueryOptions::default(),
193 /// [(Variable::new("v")?, Literal::from(1).into())],
194 /// )? {
195 /// assert_eq!(
196 /// solutions.next().unwrap()?.get("v"),
197 /// Some(&Literal::from(1).into())
198 /// );
199 /// }
200 /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
201 /// ```
202 pub fn query_opt_with_substituted_variables(
203 &self,
204 query: impl TryInto<Query, Error = impl Into<EvaluationError>>,
205 options: QueryOptions,
206 substitutions: impl IntoIterator<Item = (Variable, Term)>,
207 ) -> Result<QueryResults, EvaluationError> {
208 let (results, _) = self.explain_query_opt_with_substituted_variables(
209 query,
210 options,
211 false,
212 substitutions,
213 )?;
214 results
215 }
216
217 /// Executes a [SPARQL 1.1 query](https://www.w3.org/TR/sparql11-query/) with some options and
218 /// returns a query explanation with some statistics (if enabled with the `with_stats` parameter).
219 ///
220 /// <div class="warning">If you want to compute statistics you need to exhaust the results iterator before having a look at them.</div>
221 ///
222 /// Usage example serialising the explanation with statistics in JSON:
223 /// ```
224 /// use oxigraph::sparql::{QueryOptions, QueryResults};
225 /// use oxigraph::store::Store;
226 ///
227 /// let store = Store::new()?;
228 /// if let (Ok(QueryResults::Solutions(solutions)), explanation) = store.explain_query_opt(
229 /// "SELECT ?s WHERE { VALUES ?s { 1 2 3 } }",
230 /// QueryOptions::default(),
231 /// true,
232 /// )? {
233 /// // We make sure to have read all the solutions
234 /// for _ in solutions {}
235 /// let mut buf = Vec::new();
236 /// explanation.write_in_json(&mut buf)?;
237 /// }
238 /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
239 /// ```
240 pub fn explain_query_opt(
241 &self,
242 query: impl TryInto<Query, Error = impl Into<EvaluationError>>,
243 options: QueryOptions,
244 with_stats: bool,
245 ) -> Result<(Result<QueryResults, EvaluationError>, QueryExplanation), EvaluationError> {
246 self.explain_query_opt_with_substituted_variables(query, options, with_stats, [])
247 }
248
249 /// Executes a [SPARQL 1.1 query](https://www.w3.org/TR/sparql11-query/) with some options and
250 /// returns a query explanation with some statistics (if enabled with the `with_stats` parameter).
251 ///
252 /// <div class="warning">If you want to compute statistics you need to exhaust the results iterator before having a look at them.</div>
253 ///
254 /// Usage example serialising the explanation with statistics in JSON:
255 /// ```
256 /// use oxigraph::sparql::{QueryOptions, QueryResults};
257 /// use oxigraph::store::Store;
258 /// use oxrdf::{Literal, Variable};
259 ///
260 /// let store = Store::new()?;
261 /// if let (Ok(QueryResults::Solutions(solutions)), explanation) = store
262 /// .explain_query_opt_with_substituted_variables(
263 /// "SELECT ?s WHERE {}",
264 /// QueryOptions::default(),
265 /// true,
266 /// [(Variable::new("s")?, Literal::from(1).into())],
267 /// )?
268 /// {
269 /// // We make sure to have read all the solutions
270 /// for _ in solutions {}
271 /// let mut buf = Vec::new();
272 /// explanation.write_in_json(&mut buf)?;
273 /// }
274 /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
275 /// ```
276 pub fn explain_query_opt_with_substituted_variables(
277 &self,
278 query: impl TryInto<Query, Error = impl Into<EvaluationError>>,
279 options: QueryOptions,
280 with_stats: bool,
281 substitutions: impl IntoIterator<Item = (Variable, Term)>,
282 ) -> Result<(Result<QueryResults, EvaluationError>, QueryExplanation), EvaluationError> {
283 evaluate_query(
284 self.storage.snapshot(),
285 query,
286 options,
287 with_stats,
288 substitutions,
289 )
290 }
291
292 /// Retrieves quads with a filter on each quad component
293 ///
294 /// Usage example:
295 /// ```
296 /// use oxigraph::model::*;
297 /// use oxigraph::store::Store;
298 ///
299 /// let store = Store::new()?;
300 ///
301 /// // insertion
302 /// let ex = NamedNode::new("http://example.com")?;
303 /// let quad = Quad::new(ex.clone(), ex.clone(), ex.clone(), GraphName::DefaultGraph);
304 /// store.insert(&quad)?;
305 ///
306 /// // quad filter by object
307 /// let results = store
308 /// .quads_for_pattern(None, None, Some((&ex).into()), None)
309 /// .collect::<Result<Vec<_>, _>>()?;
310 /// assert_eq!(vec![quad], results);
311 /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
312 /// ```
313 pub fn quads_for_pattern(
314 &self,
315 subject: Option<SubjectRef<'_>>,
316 predicate: Option<NamedNodeRef<'_>>,
317 object: Option<TermRef<'_>>,
318 graph_name: Option<GraphNameRef<'_>>,
319 ) -> QuadIter {
320 let reader = self.storage.snapshot();
321 QuadIter {
322 iter: reader.quads_for_pattern(
323 subject.map(EncodedTerm::from).as_ref(),
324 predicate.map(EncodedTerm::from).as_ref(),
325 object.map(EncodedTerm::from).as_ref(),
326 graph_name.map(EncodedTerm::from).as_ref(),
327 ),
328 reader,
329 }
330 }
331
332 /// Returns all the quads contained in the store.
333 ///
334 /// Usage example:
335 /// ```
336 /// use oxigraph::model::*;
337 /// use oxigraph::store::Store;
338 ///
339 /// let store = Store::new()?;
340 ///
341 /// // insertion
342 /// let ex = NamedNode::new("http://example.com")?;
343 /// let quad = Quad::new(ex.clone(), ex.clone(), ex.clone(), GraphName::DefaultGraph);
344 /// store.insert(&quad)?;
345 ///
346 /// // quad filter by object
347 /// let results = store.iter().collect::<Result<Vec<_>, _>>()?;
348 /// assert_eq!(vec![quad], results);
349 /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
350 /// ```
351 pub fn iter(&self) -> QuadIter {
352 self.quads_for_pattern(None, None, None, None)
353 }
354
355 /// Checks if this store contains a given quad.
356 ///
357 /// Usage example:
358 /// ```
359 /// use oxigraph::model::*;
360 /// use oxigraph::store::Store;
361 ///
362 /// let ex = NamedNodeRef::new("http://example.com")?;
363 /// let quad = QuadRef::new(ex, ex, ex, ex);
364 ///
365 /// let store = Store::new()?;
366 /// assert!(!store.contains(quad)?);
367 ///
368 /// store.insert(quad)?;
369 /// assert!(store.contains(quad)?);
370 /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
371 /// ```
372 pub fn contains<'a>(&self, quad: impl Into<QuadRef<'a>>) -> Result<bool, StorageError> {
373 let quad = EncodedQuad::from(quad.into());
374 self.storage.snapshot().contains(&quad)
375 }
376
377 /// Returns the number of quads in the store.
378 ///
379 /// <div class="warning">This function executes a full scan.</div>
380 ///
381 /// Usage example:
382 /// ```
383 /// use oxigraph::model::*;
384 /// use oxigraph::store::Store;
385 ///
386 /// let ex = NamedNodeRef::new("http://example.com")?;
387 /// let store = Store::new()?;
388 /// store.insert(QuadRef::new(ex, ex, ex, ex))?;
389 /// store.insert(QuadRef::new(ex, ex, ex, GraphNameRef::DefaultGraph))?;
390 /// assert_eq!(2, store.len()?);
391 /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
392 /// ```
393 pub fn len(&self) -> Result<usize, StorageError> {
394 self.storage.snapshot().len()
395 }
396
397 /// Returns if the store is empty.
398 ///
399 /// Usage example:
400 /// ```
401 /// use oxigraph::model::*;
402 /// use oxigraph::store::Store;
403 ///
404 /// let store = Store::new()?;
405 /// assert!(store.is_empty()?);
406 ///
407 /// let ex = NamedNodeRef::new("http://example.com")?;
408 /// store.insert(QuadRef::new(ex, ex, ex, ex))?;
409 /// assert!(!store.is_empty()?);
410 /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
411 /// ```
412 pub fn is_empty(&self) -> Result<bool, StorageError> {
413 self.storage.snapshot().is_empty()
414 }
415
416 /// Executes a transaction.
417 ///
418 /// Transactions ensure the "repeatable read" isolation level: the store only exposes changes that have
419 /// been "committed" (i.e. no partial writes) and the exposed state does not change for the complete duration
420 /// of a read operation (e.g. a SPARQL query) or a read/write operation (e.g. a SPARQL update).
421 ///
422 /// Usage example:
423 /// ```
424 /// use oxigraph::model::*;
425 /// use oxigraph::store::{StorageError, Store};
426 ///
427 /// let store = Store::new()?;
428 /// let a = NamedNodeRef::new("http://example.com/a")?;
429 /// let b = NamedNodeRef::new("http://example.com/b")?;
430 ///
431 /// // Copy all triples about ex:a to triples about ex:b
432 /// store.transaction(|mut transaction| {
433 /// for q in transaction.quads_for_pattern(Some(a.into()), None, None, None) {
434 /// let q = q?;
435 /// transaction.insert(QuadRef::new(b, &q.predicate, &q.object, &q.graph_name))?;
436 /// }
437 /// Result::<_, StorageError>::Ok(())
438 /// })?;
439 /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
440 /// ```
441 pub fn transaction<T, E: Error + 'static + From<StorageError>>(
442 &self,
443 f: impl for<'a> Fn(Transaction<'a>) -> Result<T, E>,
444 ) -> Result<T, E> {
445 self.storage.transaction(|writer| f(Transaction { writer }))
446 }
447
448 /// Executes a [SPARQL 1.1 update](https://www.w3.org/TR/sparql11-update/).
449 ///
450 /// Usage example:
451 /// ```
452 /// use oxigraph::model::*;
453 /// use oxigraph::store::Store;
454 ///
455 /// let store = Store::new()?;
456 ///
457 /// // insertion
458 /// store
459 /// .update("INSERT DATA { <http://example.com> <http://example.com> <http://example.com> }")?;
460 ///
461 /// // we inspect the store contents
462 /// let ex = NamedNodeRef::new("http://example.com")?;
463 /// assert!(store.contains(QuadRef::new(ex, ex, ex, GraphNameRef::DefaultGraph))?);
464 /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
465 /// ```
466 pub fn update(
467 &self,
468 update: impl TryInto<Update, Error = impl Into<EvaluationError>>,
469 ) -> Result<(), EvaluationError> {
470 self.update_opt(update, UpdateOptions::default())
471 }
472
473 /// Executes a [SPARQL 1.1 update](https://www.w3.org/TR/sparql11-update/) with some options.
474 ///
475 /// ```
476 /// use oxigraph::store::Store;
477 /// use oxigraph::model::*;
478 /// use oxigraph::sparql::QueryOptions;
479 ///
480 /// let store = Store::new()?;
481 /// store.update_opt(
482 /// "INSERT { ?s <http://example.com/n-triples-representation> ?n } WHERE { ?s ?p ?o BIND(<http://www.w3.org/ns/formats/N-Triples>(?s) AS ?nt) }",
483 /// QueryOptions::default().with_custom_function(
484 /// NamedNode::new("http://www.w3.org/ns/formats/N-Triples")?,
485 /// |args| args.get(0).map(|t| Literal::from(t.to_string()).into())
486 /// )
487 /// )?;
488 /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
489 /// ```
490 pub fn update_opt(
491 &self,
492 update: impl TryInto<Update, Error = impl Into<EvaluationError>>,
493 options: impl Into<UpdateOptions>,
494 ) -> Result<(), EvaluationError> {
495 let update = update.try_into().map_err(Into::into)?;
496 let options = options.into();
497 self.storage
498 .transaction(|mut t| evaluate_update(&mut t, &update, &options))
499 }
500
501 /// Loads a RDF file under into the store.
502 ///
503 /// This function is atomic, quite slow and memory hungry. To get much better performances you might want to use the [`bulk_loader`](Store::bulk_loader).
504 ///
505 /// Usage example:
506 /// ```
507 /// use oxigraph::store::Store;
508 /// use oxigraph::io::RdfFormat;
509 /// use oxigraph::model::*;
510 /// use oxrdfio::RdfParser;
511 ///
512 /// let store = Store::new()?;
513 ///
514 /// // insert a dataset file (former load_dataset method)
515 /// let file = b"<http://example.com> <http://example.com> <http://example.com> <http://example.com/g> .";
516 /// store.load_from_reader(RdfFormat::NQuads, file.as_ref())?;
517 ///
518 /// // insert a graph file (former load_graph method)
519 /// let file = b"<> <> <> .";
520 /// store.load_from_reader(
521 /// RdfParser::from_format(RdfFormat::Turtle)
522 /// .with_base_iri("http://example.com")?
523 /// .without_named_graphs() // No named graphs allowed in the input
524 /// .with_default_graph(NamedNodeRef::new("http://example.com/g2")?), // we put the file default graph inside of a named graph
525 /// file.as_ref()
526 /// )?;
527 ///
528 /// // we inspect the store contents
529 /// let ex = NamedNodeRef::new("http://example.com")?;
530 /// assert!(store.contains(QuadRef::new(ex, ex, ex, NamedNodeRef::new("http://example.com/g")?))?);
531 /// assert!(store.contains(QuadRef::new(ex, ex, ex, NamedNodeRef::new("http://example.com/g2")?))?);
532 /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
533 /// ```
534 pub fn load_from_reader(
535 &self,
536 parser: impl Into<RdfParser>,
537 reader: impl Read,
538 ) -> Result<(), LoaderError> {
539 let quads = parser
540 .into()
541 .rename_blank_nodes()
542 .for_reader(reader)
543 .collect::<Result<Vec<_>, _>>()?;
544 self.storage.transaction(move |mut t| {
545 for quad in &quads {
546 t.insert(quad.as_ref())?;
547 }
548 Ok(())
549 })
550 }
551
552 /// Loads a graph file (i.e. triples) into the store.
553 ///
554 /// This function is atomic, quite slow and memory hungry. To get much better performances you might want to use the [`bulk_loader`](Store::bulk_loader).
555 ///
556 /// Usage example:
557 /// ```
558 /// use oxigraph::io::RdfFormat;
559 /// use oxigraph::model::*;
560 /// use oxigraph::store::Store;
561 ///
562 /// let store = Store::new()?;
563 ///
564 /// // insertion
565 /// let file = b"<http://example.com> <http://example.com> <http://example.com> .";
566 /// store.load_graph(
567 /// file.as_ref(),
568 /// RdfFormat::NTriples,
569 /// GraphName::DefaultGraph,
570 /// None,
571 /// )?;
572 ///
573 /// // we inspect the store contents
574 /// let ex = NamedNodeRef::new("http://example.com")?;
575 /// assert!(store.contains(QuadRef::new(ex, ex, ex, GraphNameRef::DefaultGraph))?);
576 /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
577 /// ```
578 #[deprecated(note = "use Store.load_from_reader instead", since = "0.4.0")]
579 pub fn load_graph(
580 &self,
581 reader: impl Read,
582 format: impl Into<RdfFormat>,
583 to_graph_name: impl Into<GraphName>,
584 base_iri: Option<&str>,
585 ) -> Result<(), LoaderError> {
586 let mut parser = RdfParser::from_format(format.into())
587 .without_named_graphs()
588 .with_default_graph(to_graph_name);
589 if let Some(base_iri) = base_iri {
590 parser = parser
591 .with_base_iri(base_iri)
592 .map_err(|e| LoaderError::InvalidBaseIri {
593 iri: base_iri.into(),
594 error: e,
595 })?;
596 }
597 self.load_from_reader(parser, reader)
598 }
599
600 /// Loads a dataset file (i.e. quads) into the store.
601 ///
602 /// This function is atomic, quite slow and memory hungry. To get much better performances you might want to use the [`bulk_loader`](Store::bulk_loader).
603 ///
604 /// Usage example:
605 /// ```
606 /// use oxigraph::io::RdfFormat;
607 /// use oxigraph::model::*;
608 /// use oxigraph::store::Store;
609 ///
610 /// let store = Store::new()?;
611 ///
612 /// // insertion
613 /// let file =
614 /// b"<http://example.com> <http://example.com> <http://example.com> <http://example.com> .";
615 /// store.load_dataset(file.as_ref(), RdfFormat::NQuads, None)?;
616 ///
617 /// // we inspect the store contents
618 /// let ex = NamedNodeRef::new("http://example.com")?;
619 /// assert!(store.contains(QuadRef::new(ex, ex, ex, ex))?);
620 /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
621 /// ```
622 #[deprecated(note = "use Store.load_from_reader instead", since = "0.4.0")]
623 pub fn load_dataset(
624 &self,
625 reader: impl Read,
626 format: impl Into<RdfFormat>,
627 base_iri: Option<&str>,
628 ) -> Result<(), LoaderError> {
629 let mut parser = RdfParser::from_format(format.into());
630 if let Some(base_iri) = base_iri {
631 parser = parser
632 .with_base_iri(base_iri)
633 .map_err(|e| LoaderError::InvalidBaseIri {
634 iri: base_iri.into(),
635 error: e,
636 })?;
637 }
638 self.load_from_reader(parser, reader)
639 }
640
641 /// Adds a quad to this store.
642 ///
643 /// Returns `true` if the quad was not already in the store.
644 ///
645 /// Usage example:
646 /// ```
647 /// use oxigraph::model::*;
648 /// use oxigraph::store::Store;
649 ///
650 /// let ex = NamedNodeRef::new("http://example.com")?;
651 /// let quad = QuadRef::new(ex, ex, ex, GraphNameRef::DefaultGraph);
652 ///
653 /// let store = Store::new()?;
654 /// assert!(store.insert(quad)?);
655 /// assert!(!store.insert(quad)?);
656 ///
657 /// assert!(store.contains(quad)?);
658 /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
659 /// ```
660 pub fn insert<'a>(&self, quad: impl Into<QuadRef<'a>>) -> Result<bool, StorageError> {
661 let quad = quad.into();
662 self.transaction(|mut t| t.insert(quad))
663 }
664
665 /// Adds atomically a set of quads to this store.
666 ///
667 /// <div class="warning">
668 ///
669 /// This operation uses a memory heavy transaction internally, use the [`bulk_loader`](Store::bulk_loader) if you plan to add ten of millions of triples.</div>
670 pub fn extend(
671 &self,
672 quads: impl IntoIterator<Item = impl Into<Quad>>,
673 ) -> Result<(), StorageError> {
674 let quads = quads.into_iter().map(Into::into).collect::<Vec<_>>();
675 self.transaction(move |mut t| t.extend(&quads))
676 }
677
678 /// Removes a quad from this store.
679 ///
680 /// Returns `true` if the quad was in the store and has been removed.
681 ///
682 /// Usage example:
683 /// ```
684 /// use oxigraph::model::*;
685 /// use oxigraph::store::Store;
686 ///
687 /// let ex = NamedNodeRef::new("http://example.com")?;
688 /// let quad = QuadRef::new(ex, ex, ex, GraphNameRef::DefaultGraph);
689 ///
690 /// let store = Store::new()?;
691 /// store.insert(quad)?;
692 /// assert!(store.remove(quad)?);
693 /// assert!(!store.remove(quad)?);
694 ///
695 /// assert!(!store.contains(quad)?);
696 /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
697 /// ```
698 pub fn remove<'a>(&self, quad: impl Into<QuadRef<'a>>) -> Result<bool, StorageError> {
699 let quad = quad.into();
700 self.transaction(move |mut t| t.remove(quad))
701 }
702
703 /// Dumps the store into a file.
704 ///
705 /// ```
706 /// use oxigraph::io::RdfFormat;
707 /// use oxigraph::store::Store;
708 ///
709 /// let file =
710 /// "<http://example.com> <http://example.com> <http://example.com> <http://example.com> .\n"
711 /// .as_bytes();
712 ///
713 /// let store = Store::new()?;
714 /// store.load_from_reader(RdfFormat::NQuads, file)?;
715 ///
716 /// let buffer = store.dump_to_writer(RdfFormat::NQuads, Vec::new())?;
717 /// assert_eq!(file, buffer.as_slice());
718 /// # std::io::Result::Ok(())
719 /// ```
720 pub fn dump_to_writer<W: Write>(
721 &self,
722 serializer: impl Into<RdfSerializer>,
723 writer: W,
724 ) -> Result<W, SerializerError> {
725 let serializer = serializer.into();
726 if !serializer.format().supports_datasets() {
727 return Err(SerializerError::DatasetFormatExpected(serializer.format()));
728 }
729 let mut serializer = serializer.for_writer(writer);
730 for quad in self {
731 serializer.serialize_quad(&quad?)?;
732 }
733 Ok(serializer.finish()?)
734 }
735
736 /// Dumps a store graph into a file.
737 ///
738 /// Usage example:
739 /// ```
740 /// use oxigraph::io::RdfFormat;
741 /// use oxigraph::model::*;
742 /// use oxigraph::store::Store;
743 ///
744 /// let file = "<http://example.com> <http://example.com> <http://example.com> .\n".as_bytes();
745 ///
746 /// let store = Store::new()?;
747 /// store.load_graph(file, RdfFormat::NTriples, GraphName::DefaultGraph, None)?;
748 ///
749 /// let mut buffer = Vec::new();
750 /// store.dump_graph_to_writer(GraphNameRef::DefaultGraph, RdfFormat::NTriples, &mut buffer)?;
751 /// assert_eq!(file, buffer.as_slice());
752 /// # std::io::Result::Ok(())
753 /// ```
754 pub fn dump_graph_to_writer<'a, W: Write>(
755 &self,
756 from_graph_name: impl Into<GraphNameRef<'a>>,
757 serializer: impl Into<RdfSerializer>,
758 writer: W,
759 ) -> Result<W, SerializerError> {
760 let mut serializer = serializer.into().for_writer(writer);
761 for quad in self.quads_for_pattern(None, None, None, Some(from_graph_name.into())) {
762 serializer.serialize_triple(quad?.as_ref())?;
763 }
764 Ok(serializer.finish()?)
765 }
766
767 /// Dumps a store graph into a file.
768 ///
769 /// Usage example:
770 /// ```
771 /// use oxigraph::io::RdfFormat;
772 /// use oxigraph::model::*;
773 /// use oxigraph::store::Store;
774 ///
775 /// let file = "<http://example.com> <http://example.com> <http://example.com> .\n".as_bytes();
776 ///
777 /// let store = Store::new()?;
778 /// store.load_graph(file, RdfFormat::NTriples, GraphName::DefaultGraph, None)?;
779 ///
780 /// let mut buffer = Vec::new();
781 /// store.dump_graph(&mut buffer, RdfFormat::NTriples, GraphNameRef::DefaultGraph)?;
782 /// assert_eq!(file, buffer.as_slice());
783 /// # std::io::Result::Ok(())
784 /// ```
785 #[deprecated(note = "use Store.dump_graph_to_writer instead", since = "0.4.0")]
786 pub fn dump_graph<'a, W: Write>(
787 &self,
788 writer: W,
789 format: impl Into<RdfFormat>,
790 from_graph_name: impl Into<GraphNameRef<'a>>,
791 ) -> Result<W, SerializerError> {
792 self.dump_graph_to_writer(from_graph_name, format.into(), writer)
793 }
794
795 /// Dumps the store into a file.
796 ///
797 /// ```
798 /// use oxigraph::io::RdfFormat;
799 /// use oxigraph::store::Store;
800 ///
801 /// let file =
802 /// "<http://example.com> <http://example.com> <http://example.com> <http://example.com> .\n"
803 /// .as_bytes();
804 ///
805 /// let store = Store::new()?;
806 /// store.load_from_reader(RdfFormat::NQuads, file)?;
807 ///
808 /// let buffer = store.dump_dataset(Vec::new(), RdfFormat::NQuads)?;
809 /// assert_eq!(file, buffer.as_slice());
810 /// # std::io::Result::Ok(())
811 /// ```
812 #[deprecated(note = "use Store.dump_to_writer instead", since = "0.4.0")]
813 pub fn dump_dataset<W: Write>(
814 &self,
815 writer: W,
816 format: impl Into<RdfFormat>,
817 ) -> Result<W, SerializerError> {
818 self.dump_to_writer(format.into(), writer)
819 }
820
821 /// Returns all the store named graphs.
822 ///
823 /// Usage example:
824 /// ```
825 /// use oxigraph::model::*;
826 /// use oxigraph::store::Store;
827 ///
828 /// let ex = NamedNode::new("http://example.com")?;
829 /// let store = Store::new()?;
830 /// store.insert(QuadRef::new(&ex, &ex, &ex, &ex))?;
831 /// store.insert(QuadRef::new(&ex, &ex, &ex, GraphNameRef::DefaultGraph))?;
832 /// assert_eq!(
833 /// vec![NamedOrBlankNode::from(ex)],
834 /// store.named_graphs().collect::<Result<Vec<_>, _>>()?
835 /// );
836 /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
837 /// ```
838 pub fn named_graphs(&self) -> GraphNameIter {
839 let reader = self.storage.snapshot();
840 GraphNameIter {
841 iter: reader.named_graphs(),
842 reader,
843 }
844 }
845
846 /// Checks if the store contains a given graph
847 ///
848 /// Usage example:
849 /// ```
850 /// use oxigraph::model::{NamedNode, QuadRef};
851 /// use oxigraph::store::Store;
852 ///
853 /// let ex = NamedNode::new("http://example.com")?;
854 /// let store = Store::new()?;
855 /// store.insert(QuadRef::new(&ex, &ex, &ex, &ex))?;
856 /// assert!(store.contains_named_graph(&ex)?);
857 /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
858 /// ```
859 pub fn contains_named_graph<'a>(
860 &self,
861 graph_name: impl Into<NamedOrBlankNodeRef<'a>>,
862 ) -> Result<bool, StorageError> {
863 let graph_name = EncodedTerm::from(graph_name.into());
864 self.storage.snapshot().contains_named_graph(&graph_name)
865 }
866
867 /// Inserts a graph into this store.
868 ///
869 /// Returns `true` if the graph was not already in the store.
870 ///
871 /// Usage example:
872 /// ```
873 /// use oxigraph::model::NamedNodeRef;
874 /// use oxigraph::store::Store;
875 ///
876 /// let ex = NamedNodeRef::new("http://example.com")?;
877 /// let store = Store::new()?;
878 /// store.insert_named_graph(ex)?;
879 ///
880 /// assert_eq!(
881 /// store.named_graphs().collect::<Result<Vec<_>, _>>()?,
882 /// vec![ex.into_owned().into()]
883 /// );
884 /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
885 /// ```
886 pub fn insert_named_graph<'a>(
887 &self,
888 graph_name: impl Into<NamedOrBlankNodeRef<'a>>,
889 ) -> Result<bool, StorageError> {
890 let graph_name = graph_name.into();
891 self.transaction(|mut t| t.insert_named_graph(graph_name))
892 }
893
894 /// Clears a graph from this store.
895 ///
896 /// Usage example:
897 /// ```
898 /// use oxigraph::model::{NamedNodeRef, QuadRef};
899 /// use oxigraph::store::Store;
900 ///
901 /// let ex = NamedNodeRef::new("http://example.com")?;
902 /// let quad = QuadRef::new(ex, ex, ex, ex);
903 /// let store = Store::new()?;
904 /// store.insert(quad)?;
905 /// assert_eq!(1, store.len()?);
906 ///
907 /// store.clear_graph(ex)?;
908 /// assert!(store.is_empty()?);
909 /// assert_eq!(1, store.named_graphs().count());
910 /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
911 /// ```
912 pub fn clear_graph<'a>(
913 &self,
914 graph_name: impl Into<GraphNameRef<'a>>,
915 ) -> Result<(), StorageError> {
916 let graph_name = graph_name.into();
917 self.transaction(|mut t| t.clear_graph(graph_name))
918 }
919
920 /// Removes a graph from this store.
921 ///
922 /// Returns `true` if the graph was in the store and has been removed.
923 ///
924 /// Usage example:
925 /// ```
926 /// use oxigraph::model::{NamedNodeRef, QuadRef};
927 /// use oxigraph::store::Store;
928 ///
929 /// let ex = NamedNodeRef::new("http://example.com")?;
930 /// let quad = QuadRef::new(ex, ex, ex, ex);
931 /// let store = Store::new()?;
932 /// store.insert(quad)?;
933 /// assert_eq!(1, store.len()?);
934 ///
935 /// assert!(store.remove_named_graph(ex)?);
936 /// assert!(store.is_empty()?);
937 /// assert_eq!(0, store.named_graphs().count());
938 /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
939 /// ```
940 pub fn remove_named_graph<'a>(
941 &self,
942 graph_name: impl Into<NamedOrBlankNodeRef<'a>>,
943 ) -> Result<bool, StorageError> {
944 let graph_name = graph_name.into();
945 self.transaction(|mut t| t.remove_named_graph(graph_name))
946 }
947
948 /// Clears the store.
949 ///
950 /// Usage example:
951 /// ```
952 /// use oxigraph::model::*;
953 /// use oxigraph::store::Store;
954 ///
955 /// let ex = NamedNodeRef::new("http://example.com")?;
956 /// let store = Store::new()?;
957 /// store.insert(QuadRef::new(ex, ex, ex, ex))?;
958 /// store.insert(QuadRef::new(ex, ex, ex, GraphNameRef::DefaultGraph))?;
959 /// assert_eq!(2, store.len()?);
960 ///
961 /// store.clear()?;
962 /// assert!(store.is_empty()?);
963 /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
964 /// ```
965 pub fn clear(&self) -> Result<(), StorageError> {
966 self.transaction(|mut t| t.clear())
967 }
968
969 /// Flushes all buffers and ensures that all writes are saved on disk.
970 ///
971 /// Flushes are automatically done using background threads but might lag a little bit.
972 #[cfg(all(not(target_family = "wasm"), feature = "rocksdb"))]
973 pub fn flush(&self) -> Result<(), StorageError> {
974 self.storage.flush()
975 }
976
977 /// Optimizes the database for future workload.
978 ///
979 /// Useful to call after a batch upload or another similar operation.
980 ///
981 /// <div class="warning">Can take hours on huge databases.</div>
982 #[cfg(all(not(target_family = "wasm"), feature = "rocksdb"))]
983 pub fn optimize(&self) -> Result<(), StorageError> {
984 self.storage.compact()
985 }
986
987 /// Creates database backup into the `target_directory`.
988 ///
989 /// After its creation, the backup is usable using [`Store::open`]
990 /// like a regular Oxigraph database and operates independently from the original database.
991 ///
992 /// <div class="warning">
993 ///
994 /// Backups are only possible for on-disk databases created using [`Store::open`].</div>
995 /// Temporary in-memory databases created using [`Store::new`] are not compatible with RocksDB backup system.
996 ///
997 /// <div class="warning">An error is raised if the `target_directory` already exists.</div>
998 ///
999 /// If the target directory is in the same file system as the current database,
1000 /// the database content will not be fully copied
1001 /// but hard links will be used to point to the original database immutable snapshots.
1002 /// This allows cheap regular backups.
1003 ///
1004 /// If you want to move your data to another RDF storage system, you should have a look at the [`Store::dump_to_writer`] function instead.
1005 #[cfg(all(not(target_family = "wasm"), feature = "rocksdb"))]
1006 pub fn backup(&self, target_directory: impl AsRef<Path>) -> Result<(), StorageError> {
1007 self.storage.backup(target_directory.as_ref())
1008 }
1009
1010 /// Creates a bulk loader allowing to load at lot of data quickly into the store.
1011 ///
1012 /// Usage example:
1013 /// ```
1014 /// use oxigraph::io::RdfFormat;
1015 /// use oxigraph::model::*;
1016 /// use oxigraph::store::Store;
1017 ///
1018 /// let store = Store::new()?;
1019 ///
1020 /// // quads file insertion
1021 /// let file =
1022 /// b"<http://example.com> <http://example.com> <http://example.com> <http://example.com> .";
1023 /// store
1024 /// .bulk_loader()
1025 /// .load_from_reader(RdfFormat::NQuads, file.as_ref())?;
1026 ///
1027 /// // we inspect the store contents
1028 /// let ex = NamedNodeRef::new("http://example.com")?;
1029 /// assert!(store.contains(QuadRef::new(ex, ex, ex, ex))?);
1030 /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
1031 /// ```
1032 pub fn bulk_loader(&self) -> BulkLoader {
1033 BulkLoader {
1034 storage: self.storage.bulk_loader(),
1035 on_parse_error: None,
1036 }
1037 }
1038
1039 /// Validates that all the store invariants held in the data
1040 #[doc(hidden)]
1041 pub fn validate(&self) -> Result<(), StorageError> {
1042 self.storage.snapshot().validate()
1043 }
1044}
1045
1046impl fmt::Display for Store {
1047 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1048 for t in self {
1049 writeln!(f, "{} .", t.map_err(|_| fmt::Error)?)?;
1050 }
1051 Ok(())
1052 }
1053}
1054
1055impl IntoIterator for &Store {
1056 type IntoIter = QuadIter;
1057 type Item = Result<Quad, StorageError>;
1058
1059 #[inline]
1060 fn into_iter(self) -> Self::IntoIter {
1061 self.iter()
1062 }
1063}
1064
1065/// An object to do operations during a transaction.
1066///
1067/// See [`Store::transaction`] for a more detailed description.
1068pub struct Transaction<'a> {
1069 writer: StorageWriter<'a>,
1070}
1071
1072impl Transaction<'_> {
1073 /// Executes a [SPARQL 1.1 query](https://www.w3.org/TR/sparql11-query/).
1074 ///
1075 /// Usage example:
1076 /// ```
1077 /// use oxigraph::model::*;
1078 /// use oxigraph::sparql::{EvaluationError, QueryResults};
1079 /// use oxigraph::store::Store;
1080 ///
1081 /// let store = Store::new()?;
1082 /// store.transaction(|mut transaction| {
1083 /// if let QueryResults::Solutions(solutions) =
1084 /// transaction.query("SELECT ?s WHERE { ?s ?p ?o }")?
1085 /// {
1086 /// for solution in solutions {
1087 /// if let Some(Term::NamedNode(s)) = solution?.get("s") {
1088 /// transaction.insert(QuadRef::new(
1089 /// s,
1090 /// vocab::rdf::TYPE,
1091 /// NamedNodeRef::new_unchecked("http://example.com"),
1092 /// GraphNameRef::DefaultGraph,
1093 /// ))?;
1094 /// }
1095 /// }
1096 /// }
1097 /// Result::<_, EvaluationError>::Ok(())
1098 /// })?;
1099 /// # Result::<_, EvaluationError>::Ok(())
1100 /// ```
1101 pub fn query(
1102 &self,
1103 query: impl TryInto<Query, Error = impl Into<EvaluationError>>,
1104 ) -> Result<QueryResults, EvaluationError> {
1105 self.query_opt(query, QueryOptions::default())
1106 }
1107
1108 /// Executes a [SPARQL 1.1 query](https://www.w3.org/TR/sparql11-query/) with some options.
1109 ///
1110 /// Usage example with a custom function serializing terms to N-Triples:
1111 /// ```
1112 /// use oxigraph::model::*;
1113 /// use oxigraph::sparql::{EvaluationError, QueryOptions, QueryResults};
1114 /// use oxigraph::store::Store;
1115 ///
1116 /// let store = Store::new()?;
1117 /// store.transaction(|mut transaction| {
1118 /// if let QueryResults::Solutions(solutions) = transaction.query_opt(
1119 /// "SELECT ?s (<http://www.w3.org/ns/formats/N-Triples>(?s) AS ?nt) WHERE { ?s ?p ?o }",
1120 /// QueryOptions::default().with_custom_function(
1121 /// NamedNode::new_unchecked("http://www.w3.org/ns/formats/N-Triples"),
1122 /// |args| args.get(0).map(|t| Literal::from(t.to_string()).into()),
1123 /// ),
1124 /// )? {
1125 /// for solution in solutions {
1126 /// let solution = solution?;
1127 /// if let (Some(Term::NamedNode(s)), Some(nt)) =
1128 /// (solution.get("s"), solution.get("nt"))
1129 /// {
1130 /// transaction.insert(QuadRef::new(
1131 /// s,
1132 /// NamedNodeRef::new_unchecked("http://example.com/n-triples-representation"),
1133 /// nt,
1134 /// GraphNameRef::DefaultGraph,
1135 /// ))?;
1136 /// }
1137 /// }
1138 /// }
1139 /// Result::<_, EvaluationError>::Ok(())
1140 /// })?;
1141 /// # Result::<_, EvaluationError>::Ok(())
1142 /// ```
1143 pub fn query_opt(
1144 &self,
1145 query: impl TryInto<Query, Error = impl Into<EvaluationError>>,
1146 options: QueryOptions,
1147 ) -> Result<QueryResults, EvaluationError> {
1148 let (results, _) = evaluate_query(self.writer.reader(), query, options, false, [])?;
1149 results
1150 }
1151
1152 /// Retrieves quads with a filter on each quad component.
1153 ///
1154 /// Usage example:
1155 /// ```
1156 /// use oxigraph::model::*;
1157 /// use oxigraph::store::{StorageError, Store};
1158 ///
1159 /// let store = Store::new()?;
1160 /// let a = NamedNodeRef::new("http://example.com/a")?;
1161 /// let b = NamedNodeRef::new("http://example.com/b")?;
1162 ///
1163 /// // Copy all triples about ex:a to triples about ex:b
1164 /// store.transaction(|mut transaction| {
1165 /// for q in transaction.quads_for_pattern(Some(a.into()), None, None, None) {
1166 /// let q = q?;
1167 /// transaction.insert(QuadRef::new(b, &q.predicate, &q.object, &q.graph_name))?;
1168 /// }
1169 /// Result::<_, StorageError>::Ok(())
1170 /// })?;
1171 /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
1172 /// ```
1173 pub fn quads_for_pattern(
1174 &self,
1175 subject: Option<SubjectRef<'_>>,
1176 predicate: Option<NamedNodeRef<'_>>,
1177 object: Option<TermRef<'_>>,
1178 graph_name: Option<GraphNameRef<'_>>,
1179 ) -> QuadIter {
1180 let reader = self.writer.reader();
1181 QuadIter {
1182 iter: reader.quads_for_pattern(
1183 subject.map(EncodedTerm::from).as_ref(),
1184 predicate.map(EncodedTerm::from).as_ref(),
1185 object.map(EncodedTerm::from).as_ref(),
1186 graph_name.map(EncodedTerm::from).as_ref(),
1187 ),
1188 reader,
1189 }
1190 }
1191
1192 /// Returns all the quads contained in the store.
1193 pub fn iter(&self) -> QuadIter {
1194 self.quads_for_pattern(None, None, None, None)
1195 }
1196
1197 /// Checks if this store contains a given quad.
1198 pub fn contains<'b>(&self, quad: impl Into<QuadRef<'b>>) -> Result<bool, StorageError> {
1199 let quad = EncodedQuad::from(quad.into());
1200 self.writer.reader().contains(&quad)
1201 }
1202
1203 /// Returns the number of quads in the store.
1204 ///
1205 /// <div class="warning">this function executes a full scan.</div>
1206 pub fn len(&self) -> Result<usize, StorageError> {
1207 self.writer.reader().len()
1208 }
1209
1210 /// Returns if the store is empty.
1211 pub fn is_empty(&self) -> Result<bool, StorageError> {
1212 self.writer.reader().is_empty()
1213 }
1214
1215 /// Executes a [SPARQL 1.1 update](https://www.w3.org/TR/sparql11-update/).
1216 ///
1217 /// Usage example:
1218 /// ```
1219 /// use oxigraph::model::*;
1220 /// use oxigraph::sparql::EvaluationError;
1221 /// use oxigraph::store::Store;
1222 ///
1223 /// let store = Store::new()?;
1224 /// store.transaction(|mut transaction| {
1225 /// // insertion
1226 /// transaction.update(
1227 /// "INSERT DATA { <http://example.com> <http://example.com> <http://example.com> }",
1228 /// )?;
1229 ///
1230 /// // we inspect the store contents
1231 /// let ex = NamedNodeRef::new_unchecked("http://example.com");
1232 /// assert!(transaction.contains(QuadRef::new(ex, ex, ex, GraphNameRef::DefaultGraph))?);
1233 /// Result::<_, EvaluationError>::Ok(())
1234 /// })?;
1235 /// # Result::<_, EvaluationError>::Ok(())
1236 /// ```
1237 pub fn update(
1238 &mut self,
1239 update: impl TryInto<Update, Error = impl Into<EvaluationError>>,
1240 ) -> Result<(), EvaluationError> {
1241 self.update_opt(update, UpdateOptions::default())
1242 }
1243
1244 /// Executes a [SPARQL 1.1 update](https://www.w3.org/TR/sparql11-update/) with some options.
1245 pub fn update_opt(
1246 &mut self,
1247 update: impl TryInto<Update, Error = impl Into<EvaluationError>>,
1248 options: impl Into<UpdateOptions>,
1249 ) -> Result<(), EvaluationError> {
1250 evaluate_update(
1251 &mut self.writer,
1252 &update.try_into().map_err(Into::into)?,
1253 &options.into(),
1254 )
1255 }
1256
1257 /// Loads a RDF file into the store.
1258 ///
1259 /// This function is atomic, quite slow and memory hungry. To get much better performances you might want to use the [`bulk_loader`](Store::bulk_loader).
1260 ///
1261 /// Usage example:
1262 /// ```
1263 /// use oxigraph::store::Store;
1264 /// use oxigraph::io::RdfFormat;
1265 /// use oxigraph::model::*;
1266 /// use oxrdfio::RdfParser;
1267 ///
1268 /// let store = Store::new()?;
1269 ///
1270 /// // insert a dataset file (former load_dataset method)
1271 /// let file = b"<http://example.com> <http://example.com> <http://example.com> <http://example.com/g> .";
1272 /// store.transaction(|mut t| t.load_from_reader(RdfFormat::NQuads, file.as_ref()))?;
1273 ///
1274 /// // insert a graph file (former load_graph method)
1275 /// let file = b"<> <> <> .";
1276 /// store.transaction(|mut t|
1277 /// t.load_from_reader(
1278 /// RdfParser::from_format(RdfFormat::Turtle)
1279 /// .with_base_iri("http://example.com")
1280 /// .unwrap()
1281 /// .without_named_graphs() // No named graphs allowed in the input
1282 /// .with_default_graph(NamedNodeRef::new("http://example.com/g2").unwrap()), // we put the file default graph inside of a named graph
1283 /// file.as_ref()
1284 /// )
1285 /// )?;
1286 ///
1287 /// // we inspect the store contents
1288 /// let ex = NamedNodeRef::new("http://example.com")?;
1289 /// assert!(store.contains(QuadRef::new(ex, ex, ex, NamedNodeRef::new("http://example.com/g")?))?);
1290 /// assert!(store.contains(QuadRef::new(ex, ex, ex, NamedNodeRef::new("http://example.com/g2")?))?);
1291 /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
1292 /// ```
1293 pub fn load_from_reader(
1294 &mut self,
1295 parser: impl Into<RdfParser>,
1296 reader: impl Read,
1297 ) -> Result<(), LoaderError> {
1298 for quad in parser.into().rename_blank_nodes().for_reader(reader) {
1299 self.insert(quad?.as_ref())?;
1300 }
1301 Ok(())
1302 }
1303
1304 /// Loads a graph file (i.e. triples) into the store.
1305 ///
1306 /// Usage example:
1307 /// ```
1308 /// use oxigraph::io::RdfFormat;
1309 /// use oxigraph::model::*;
1310 /// use oxigraph::store::Store;
1311 ///
1312 /// let store = Store::new()?;
1313 ///
1314 /// // insertion
1315 /// let file = b"<http://example.com> <http://example.com> <http://example.com> .";
1316 /// store.transaction(|mut transaction| {
1317 /// transaction.load_graph(
1318 /// file.as_ref(),
1319 /// RdfFormat::NTriples,
1320 /// GraphName::DefaultGraph,
1321 /// None,
1322 /// )
1323 /// })?;
1324 ///
1325 /// // we inspect the store contents
1326 /// let ex = NamedNodeRef::new("http://example.com")?;
1327 /// assert!(store.contains(QuadRef::new(ex, ex, ex, GraphNameRef::DefaultGraph))?);
1328 /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
1329 /// ```
1330 #[deprecated(note = "use Transaction.load_from_reader instead", since = "0.4.0")]
1331 pub fn load_graph(
1332 &mut self,
1333 reader: impl Read,
1334 format: impl Into<RdfFormat>,
1335 to_graph_name: impl Into<GraphName>,
1336 base_iri: Option<&str>,
1337 ) -> Result<(), LoaderError> {
1338 let mut parser = RdfParser::from_format(format.into())
1339 .without_named_graphs()
1340 .with_default_graph(to_graph_name);
1341 if let Some(base_iri) = base_iri {
1342 parser = parser
1343 .with_base_iri(base_iri)
1344 .map_err(|e| LoaderError::InvalidBaseIri {
1345 iri: base_iri.into(),
1346 error: e,
1347 })?;
1348 }
1349 self.load_from_reader(parser, reader)
1350 }
1351
1352 /// Loads a dataset file (i.e. quads) into the store.
1353 ///
1354 /// Usage example:
1355 /// ```
1356 /// use oxigraph::io::RdfFormat;
1357 /// use oxigraph::model::*;
1358 /// use oxigraph::store::Store;
1359 ///
1360 /// let store = Store::new()?;
1361 ///
1362 /// // insertion
1363 /// let file =
1364 /// b"<http://example.com> <http://example.com> <http://example.com> <http://example.com> .";
1365 /// store.transaction(|mut transaction| {
1366 /// transaction.load_dataset(file.as_ref(), RdfFormat::NQuads, None)
1367 /// })?;
1368 ///
1369 /// // we inspect the store contents
1370 /// let ex = NamedNodeRef::new_unchecked("http://example.com");
1371 /// assert!(store.contains(QuadRef::new(ex, ex, ex, ex))?);
1372 /// # Result::<_,oxigraph::store::LoaderError>::Ok(())
1373 /// ```
1374 #[deprecated(note = "use Transaction.load_from_reader instead", since = "0.4.0")]
1375 pub fn load_dataset(
1376 &mut self,
1377 reader: impl Read,
1378 format: impl Into<RdfFormat>,
1379 base_iri: Option<&str>,
1380 ) -> Result<(), LoaderError> {
1381 let mut parser = RdfParser::from_format(format.into());
1382 if let Some(base_iri) = base_iri {
1383 parser = parser
1384 .with_base_iri(base_iri)
1385 .map_err(|e| LoaderError::InvalidBaseIri {
1386 iri: base_iri.into(),
1387 error: e,
1388 })?;
1389 }
1390 self.load_from_reader(parser, reader)
1391 }
1392
1393 /// Adds a quad to this store.
1394 ///
1395 /// Returns `true` if the quad was not already in the store.
1396 ///
1397 /// Usage example:
1398 /// ```
1399 /// use oxigraph::model::*;
1400 /// use oxigraph::store::Store;
1401 ///
1402 /// let ex = NamedNodeRef::new_unchecked("http://example.com");
1403 /// let quad = QuadRef::new(ex, ex, ex, GraphNameRef::DefaultGraph);
1404 ///
1405 /// let store = Store::new()?;
1406 /// store.transaction(|mut transaction| transaction.insert(quad))?;
1407 /// assert!(store.contains(quad)?);
1408 /// # Result::<_,oxigraph::store::StorageError>::Ok(())
1409 /// ```
1410 pub fn insert<'b>(&mut self, quad: impl Into<QuadRef<'b>>) -> Result<bool, StorageError> {
1411 self.writer.insert(quad.into())
1412 }
1413
1414 /// Adds a set of quads to this store.
1415 pub fn extend<'b>(
1416 &mut self,
1417 quads: impl IntoIterator<Item = impl Into<QuadRef<'b>>>,
1418 ) -> Result<(), StorageError> {
1419 for quad in quads {
1420 self.writer.insert(quad.into())?;
1421 }
1422 Ok(())
1423 }
1424
1425 /// Removes a quad from this store.
1426 ///
1427 /// Returns `true` if the quad was in the store and has been removed.
1428 ///
1429 /// Usage example:
1430 /// ```
1431 /// use oxigraph::model::*;
1432 /// use oxigraph::store::Store;
1433 ///
1434 /// let ex = NamedNodeRef::new_unchecked("http://example.com");
1435 /// let quad = QuadRef::new(ex, ex, ex, GraphNameRef::DefaultGraph);
1436 /// let store = Store::new()?;
1437 /// store.transaction(|mut transaction| {
1438 /// transaction.insert(quad)?;
1439 /// transaction.remove(quad)
1440 /// })?;
1441 /// assert!(!store.contains(quad)?);
1442 /// # Result::<_,oxigraph::store::StorageError>::Ok(())
1443 /// ```
1444 pub fn remove<'b>(&mut self, quad: impl Into<QuadRef<'b>>) -> Result<bool, StorageError> {
1445 self.writer.remove(quad.into())
1446 }
1447
1448 /// Returns all the store named graphs.
1449 pub fn named_graphs(&self) -> GraphNameIter {
1450 let reader = self.writer.reader();
1451 GraphNameIter {
1452 iter: reader.named_graphs(),
1453 reader,
1454 }
1455 }
1456
1457 /// Checks if the store contains a given graph.
1458 pub fn contains_named_graph<'b>(
1459 &self,
1460 graph_name: impl Into<NamedOrBlankNodeRef<'b>>,
1461 ) -> Result<bool, StorageError> {
1462 self.writer
1463 .reader()
1464 .contains_named_graph(&EncodedTerm::from(graph_name.into()))
1465 }
1466
1467 /// Inserts a graph into this store.
1468 ///
1469 /// Returns `true` if the graph was not already in the store.
1470 ///
1471 /// Usage example:
1472 /// ```
1473 /// use oxigraph::model::NamedNodeRef;
1474 /// use oxigraph::store::Store;
1475 ///
1476 /// let ex = NamedNodeRef::new_unchecked("http://example.com");
1477 /// let store = Store::new()?;
1478 /// store.transaction(|mut transaction| transaction.insert_named_graph(ex))?;
1479 /// assert_eq!(
1480 /// store.named_graphs().collect::<Result<Vec<_>, _>>()?,
1481 /// vec![ex.into_owned().into()]
1482 /// );
1483 /// # Result::<_,oxigraph::store::StorageError>::Ok(())
1484 /// ```
1485 pub fn insert_named_graph<'b>(
1486 &mut self,
1487 graph_name: impl Into<NamedOrBlankNodeRef<'b>>,
1488 ) -> Result<bool, StorageError> {
1489 self.writer.insert_named_graph(graph_name.into())
1490 }
1491
1492 /// Clears a graph from this store.
1493 ///
1494 /// Usage example:
1495 /// ```
1496 /// use oxigraph::model::{NamedNodeRef, QuadRef};
1497 /// use oxigraph::store::Store;
1498 ///
1499 /// let ex = NamedNodeRef::new_unchecked("http://example.com");
1500 /// let quad = QuadRef::new(ex, ex, ex, ex);
1501 /// let store = Store::new()?;
1502 /// store.transaction(|mut transaction| {
1503 /// transaction.insert(quad)?;
1504 /// transaction.clear_graph(ex)
1505 /// })?;
1506 /// assert!(store.is_empty()?);
1507 /// assert_eq!(1, store.named_graphs().count());
1508 /// # Result::<_,oxigraph::store::StorageError>::Ok(())
1509 /// ```
1510 pub fn clear_graph<'b>(
1511 &mut self,
1512 graph_name: impl Into<GraphNameRef<'b>>,
1513 ) -> Result<(), StorageError> {
1514 self.writer.clear_graph(graph_name.into())
1515 }
1516
1517 /// Removes a graph from this store.
1518 ///
1519 /// Returns `true` if the graph was in the store and has been removed.
1520 ///
1521 /// Usage example:
1522 /// ```
1523 /// use oxigraph::model::{NamedNodeRef, QuadRef};
1524 /// use oxigraph::store::Store;
1525 ///
1526 /// let ex = NamedNodeRef::new_unchecked("http://example.com");
1527 /// let quad = QuadRef::new(ex, ex, ex, ex);
1528 /// let store = Store::new()?;
1529 /// store.transaction(|mut transaction| {
1530 /// transaction.insert(quad)?;
1531 /// transaction.remove_named_graph(ex)
1532 /// })?;
1533 /// assert!(store.is_empty()?);
1534 /// assert_eq!(0, store.named_graphs().count());
1535 /// # Result::<_,oxigraph::store::StorageError>::Ok(())
1536 /// ```
1537 pub fn remove_named_graph<'b>(
1538 &mut self,
1539 graph_name: impl Into<NamedOrBlankNodeRef<'b>>,
1540 ) -> Result<bool, StorageError> {
1541 self.writer.remove_named_graph(graph_name.into())
1542 }
1543
1544 /// Clears the store.
1545 ///
1546 /// Usage example:
1547 /// ```
1548 /// use oxigraph::model::*;
1549 /// use oxigraph::store::Store;
1550 ///
1551 /// let ex = NamedNodeRef::new_unchecked("http://example.com");
1552 /// let store = Store::new()?;
1553 /// store.transaction(|mut transaction| {
1554 /// transaction.insert(QuadRef::new(ex, ex, ex, ex))?;
1555 /// transaction.clear()
1556 /// })?;
1557 /// assert!(store.is_empty()?);
1558 /// # Result::<_,oxigraph::store::StorageError>::Ok(())
1559 /// ```
1560 pub fn clear(&mut self) -> Result<(), StorageError> {
1561 self.writer.clear()
1562 }
1563}
1564
1565impl IntoIterator for &Transaction<'_> {
1566 type IntoIter = QuadIter;
1567 type Item = Result<Quad, StorageError>;
1568
1569 #[inline]
1570 fn into_iter(self) -> Self::IntoIter {
1571 self.iter()
1572 }
1573}
1574
1575/// An iterator returning the quads contained in a [`Store`].
1576pub struct QuadIter {
1577 iter: DecodingQuadIterator,
1578 reader: StorageReader,
1579}
1580
1581impl Iterator for QuadIter {
1582 type Item = Result<Quad, StorageError>;
1583
1584 fn next(&mut self) -> Option<Self::Item> {
1585 Some(match self.iter.next()? {
1586 Ok(quad) => self.reader.decode_quad(&quad),
1587 Err(error) => Err(error),
1588 })
1589 }
1590}
1591
1592/// An iterator returning the graph names contained in a [`Store`].
1593pub struct GraphNameIter {
1594 iter: DecodingGraphIterator,
1595 reader: StorageReader,
1596}
1597
1598impl Iterator for GraphNameIter {
1599 type Item = Result<NamedOrBlankNode, StorageError>;
1600
1601 fn next(&mut self) -> Option<Self::Item> {
1602 Some(
1603 self.iter
1604 .next()?
1605 .and_then(|graph_name| self.reader.decode_named_or_blank_node(&graph_name)),
1606 )
1607 }
1608
1609 fn size_hint(&self) -> (usize, Option<usize>) {
1610 self.iter.size_hint()
1611 }
1612}
1613
1614/// A bulk loader allowing to load at lot of data quickly into the store.
1615///
1616/// <div class="warning">The operations provided here are not atomic.
1617/// If the operation fails in the middle, only a part of the data may be written to the store.
1618/// Results might get weird if you delete data during the loading process.</div>
1619///
1620/// Memory usage is configurable using [`with_max_memory_size_in_megabytes`](Self::with_max_memory_size_in_megabytes)
1621/// and the number of used threads with [`with_num_threads`](Self::with_num_threads).
1622/// By default the memory consumption target (excluding the system and RocksDB internal consumption)
1623/// is around 2GB per thread and 2 threads.
1624/// These targets are considered per loaded file.
1625///
1626/// Usage example with loading a dataset:
1627/// ```
1628/// use oxigraph::io::RdfFormat;
1629/// use oxigraph::model::*;
1630/// use oxigraph::store::Store;
1631///
1632/// let store = Store::new()?;
1633///
1634/// // quads file insertion
1635/// let file =
1636/// b"<http://example.com> <http://example.com> <http://example.com> <http://example.com> .";
1637/// store
1638/// .bulk_loader()
1639/// .load_from_reader(RdfFormat::NQuads, file.as_ref())?;
1640///
1641/// // we inspect the store contents
1642/// let ex = NamedNodeRef::new("http://example.com")?;
1643/// assert!(store.contains(QuadRef::new(ex, ex, ex, ex))?);
1644/// # Result::<_, Box<dyn std::error::Error>>::Ok(())
1645/// ```
1646#[must_use]
1647pub struct BulkLoader {
1648 storage: StorageBulkLoader,
1649 on_parse_error: Option<Box<dyn Fn(RdfParseError) -> Result<(), RdfParseError>>>,
1650}
1651
1652impl BulkLoader {
1653 /// Sets the maximal number of threads to be used by the bulk loader per operation.
1654 ///
1655 /// This number must be at last 2 (one for parsing and one for loading).
1656 ///
1657 /// The default value is 2.
1658 pub fn with_num_threads(mut self, num_threads: usize) -> Self {
1659 self.storage = self.storage.with_num_threads(num_threads);
1660 self
1661 }
1662
1663 #[doc(hidden)]
1664 #[deprecated(note = "Use with_num_threads", since = "0.4.0")]
1665 pub fn set_num_threads(self, num_threads: usize) -> Self {
1666 self.with_num_threads(num_threads)
1667 }
1668
1669 /// Sets a rough idea of the maximal amount of memory to be used by this operation.
1670 ///
1671 /// This number must be at last a few megabytes per thread.
1672 ///
1673 /// Memory used by RocksDB and the system is not taken into account in this limit.
1674 /// Note that depending on the system behavior this amount might never be reached or be blown up
1675 /// (for example if the data contains very long IRIs or literals).
1676 ///
1677 /// By default, a target 2GB per used thread is used.
1678 pub fn with_max_memory_size_in_megabytes(mut self, max_memory_size: usize) -> Self {
1679 self.storage = self
1680 .storage
1681 .with_max_memory_size_in_megabytes(max_memory_size);
1682 self
1683 }
1684
1685 #[doc(hidden)]
1686 #[deprecated(note = "Use with_max_memory_size_in_megabytes", since = "0.4.0")]
1687 pub fn set_max_memory_size_in_megabytes(self, max_memory_size: usize) -> Self {
1688 self.with_max_memory_size_in_megabytes(max_memory_size)
1689 }
1690
1691 /// Adds a `callback` evaluated from time to time with the number of loaded triples.
1692 pub fn on_progress(mut self, callback: impl Fn(u64) + 'static) -> Self {
1693 self.storage = self.storage.on_progress(callback);
1694 self
1695 }
1696
1697 /// Adds a `callback` catching all parse errors and choosing if the parsing should continue
1698 /// by returning `Ok` or fail by returning `Err`.
1699 ///
1700 /// By default the parsing fails.
1701 pub fn on_parse_error(
1702 mut self,
1703 callback: impl Fn(RdfParseError) -> Result<(), RdfParseError> + 'static,
1704 ) -> Self {
1705 self.on_parse_error = Some(Box::new(callback));
1706 self
1707 }
1708
1709 /// Loads a file using the bulk loader.
1710 ///
1711 /// This function is optimized for large dataset loading speed. For small files, [`Store::load_from_reader`] might be more convenient.
1712 ///
1713 /// <div class="warning">This method is not atomic.
1714 /// If the parsing fails in the middle of the file, only a part of it may be written to the store.
1715 /// Results might get weird if you delete data during the loading process.</div>
1716 ///
1717 /// This method is optimized for speed. See [the struct](Self) documentation for more details.
1718 ///
1719 /// To get better speed on valid datasets, consider enabling [`RdfParser::unchecked`] option to skip some validations.
1720 ///
1721 /// Usage example:
1722 /// ```
1723 /// use oxigraph::store::Store;
1724 /// use oxigraph::io::{RdfParser, RdfFormat};
1725 /// use oxigraph::model::*;
1726 ///
1727 /// let store = Store::new()?;
1728 ///
1729 /// // insert a dataset file (former load_dataset method)
1730 /// let file = b"<http://example.com> <http://example.com> <http://example.com> <http://example.com/g> .";
1731 /// store.bulk_loader().load_from_reader(
1732 /// RdfParser::from_format(RdfFormat::NQuads).unchecked(), // we inject a custom parser with options
1733 /// file.as_ref()
1734 /// )?;
1735 ///
1736 /// // insert a graph file (former load_graph method)
1737 /// let file = b"<> <> <> .";
1738 /// store.bulk_loader().load_from_reader(
1739 /// RdfParser::from_format(RdfFormat::Turtle)
1740 /// .with_base_iri("http://example.com")?
1741 /// .without_named_graphs() // No named graphs allowed in the input
1742 /// .with_default_graph(NamedNodeRef::new("http://example.com/g2")?), // we put the file default graph inside of a named graph
1743 /// file.as_ref()
1744 /// )?;
1745 ///
1746 /// // we inspect the store contents
1747 /// let ex = NamedNodeRef::new("http://example.com")?;
1748 /// assert!(store.contains(QuadRef::new(ex, ex, ex, NamedNodeRef::new("http://example.com/g")?))?);
1749 /// assert!(store.contains(QuadRef::new(ex, ex, ex, NamedNodeRef::new("http://example.com/g2")?))?);
1750 /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
1751 /// ```
1752 pub fn load_from_reader(
1753 &self,
1754 parser: impl Into<RdfParser>,
1755 reader: impl Read,
1756 ) -> Result<(), LoaderError> {
1757 self.load_ok_quads(
1758 parser
1759 .into()
1760 .rename_blank_nodes()
1761 .for_reader(reader)
1762 .filter_map(|r| match r {
1763 Ok(q) => Some(Ok(q)),
1764 Err(e) => {
1765 if let Some(callback) = &self.on_parse_error {
1766 if let Err(e) = callback(e) {
1767 Some(Err(e))
1768 } else {
1769 None
1770 }
1771 } else {
1772 Some(Err(e))
1773 }
1774 }
1775 }),
1776 )
1777 }
1778
1779 /// Loads a dataset file using the bulk loader.
1780 ///
1781 /// This function is optimized for large dataset loading speed. For small files, [`Store::load_dataset`] might be more convenient.
1782 ///
1783 /// <div class="warning">This method is not atomic.
1784 /// If the parsing fails in the middle of the file, only a part of it may be written to the store.
1785 /// Results might get weird if you delete data during the loading process.</div>
1786 ///
1787 /// This method is optimized for speed. See [the struct](Self) documentation for more details.
1788 ///
1789 /// Usage example:
1790 /// ```
1791 /// use oxigraph::io::RdfFormat;
1792 /// use oxigraph::model::*;
1793 /// use oxigraph::store::Store;
1794 ///
1795 /// let store = Store::new()?;
1796 ///
1797 /// // insertion
1798 /// let file =
1799 /// b"<http://example.com> <http://example.com> <http://example.com> <http://example.com> .";
1800 /// store
1801 /// .bulk_loader()
1802 /// .load_dataset(file.as_ref(), RdfFormat::NQuads, None)?;
1803 ///
1804 /// // we inspect the store contents
1805 /// let ex = NamedNodeRef::new("http://example.com")?;
1806 /// assert!(store.contains(QuadRef::new(ex, ex, ex, ex))?);
1807 /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
1808 /// ```
1809 #[deprecated(note = "use BulkLoader.load_from_reader instead", since = "0.4.0")]
1810 pub fn load_dataset(
1811 &self,
1812 reader: impl Read,
1813 format: impl Into<RdfFormat>,
1814 base_iri: Option<&str>,
1815 ) -> Result<(), LoaderError> {
1816 let mut parser = RdfParser::from_format(format.into()).rename_blank_nodes();
1817 if let Some(base_iri) = base_iri {
1818 parser = parser
1819 .with_base_iri(base_iri)
1820 .map_err(|e| LoaderError::InvalidBaseIri {
1821 iri: base_iri.into(),
1822 error: e,
1823 })?;
1824 }
1825 self.load_ok_quads(parser.for_reader(reader).filter_map(|r| match r {
1826 Ok(q) => Some(Ok(q)),
1827 Err(e) => {
1828 if let Some(callback) = &self.on_parse_error {
1829 if let Err(e) = callback(e) {
1830 Some(Err(e))
1831 } else {
1832 None
1833 }
1834 } else {
1835 Some(Err(e))
1836 }
1837 }
1838 }))
1839 }
1840
1841 /// Loads a graph file using the bulk loader.
1842 ///
1843 /// This function is optimized for large graph loading speed. For small files, [`Store::load_graph`] might be more convenient.
1844 ///
1845 /// <div class="warning">This method is not atomic.
1846 /// If the parsing fails in the middle of the file, only a part of it may be written to the store.
1847 /// Results might get weird if you delete data during the loading process.</div>
1848 ///
1849 /// This method is optimized for speed. See [the struct](Self) documentation for more details.
1850 ///
1851 /// Usage example:
1852 /// ```
1853 /// use oxigraph::io::RdfFormat;
1854 /// use oxigraph::model::*;
1855 /// use oxigraph::store::Store;
1856 ///
1857 /// let store = Store::new()?;
1858 ///
1859 /// // insertion
1860 /// let file = b"<http://example.com> <http://example.com> <http://example.com> .";
1861 /// store.bulk_loader().load_graph(
1862 /// file.as_ref(),
1863 /// RdfFormat::NTriples,
1864 /// GraphName::DefaultGraph,
1865 /// None,
1866 /// )?;
1867 ///
1868 /// // we inspect the store contents
1869 /// let ex = NamedNodeRef::new("http://example.com")?;
1870 /// assert!(store.contains(QuadRef::new(ex, ex, ex, GraphNameRef::DefaultGraph))?);
1871 /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
1872 /// ```
1873 #[deprecated(note = "use BulkLoader.load_from_reader instead", since = "0.4.0")]
1874 pub fn load_graph(
1875 &self,
1876 reader: impl Read,
1877 format: impl Into<RdfFormat>,
1878 to_graph_name: impl Into<GraphName>,
1879 base_iri: Option<&str>,
1880 ) -> Result<(), LoaderError> {
1881 let mut parser = RdfParser::from_format(format.into())
1882 .without_named_graphs()
1883 .with_default_graph(to_graph_name)
1884 .rename_blank_nodes();
1885 if let Some(base_iri) = base_iri {
1886 parser = parser
1887 .with_base_iri(base_iri)
1888 .map_err(|e| LoaderError::InvalidBaseIri {
1889 iri: base_iri.into(),
1890 error: e,
1891 })?;
1892 }
1893 self.load_ok_quads(parser.for_reader(reader).filter_map(|r| match r {
1894 Ok(q) => Some(Ok(q)),
1895 Err(e) => {
1896 if let Some(callback) = &self.on_parse_error {
1897 if let Err(e) = callback(e) {
1898 Some(Err(e))
1899 } else {
1900 None
1901 }
1902 } else {
1903 Some(Err(e))
1904 }
1905 }
1906 }))
1907 }
1908
1909 /// Adds a set of quads using the bulk loader.
1910 ///
1911 /// <div class="warning">This method is not atomic.
1912 /// If the process fails in the middle of the file, only a part of the data may be written to the store.
1913 /// Results might get weird if you delete data during the loading process.</div>
1914 ///
1915 /// This method is optimized for speed. See [the struct](Self) documentation for more details.
1916 pub fn load_quads(
1917 &self,
1918 quads: impl IntoIterator<Item = impl Into<Quad>>,
1919 ) -> Result<(), StorageError> {
1920 self.load_ok_quads(quads.into_iter().map(Ok::<_, StorageError>))
1921 }
1922
1923 /// Adds a set of quads using the bulk loader while breaking in the middle of the process in case of error.
1924 ///
1925 /// <div class="warning">This method is not atomic.
1926 /// If the process fails in the middle of the file, only a part of the data may be written to the store.
1927 /// Results might get weird if you delete data during the loading process.</div>
1928 ///
1929 /// This method is optimized for speed. See [the struct](Self) documentation for more details.
1930 pub fn load_ok_quads<EI, EO: From<StorageError> + From<EI>>(
1931 &self,
1932 quads: impl IntoIterator<Item = Result<impl Into<Quad>, EI>>,
1933 ) -> Result<(), EO> {
1934 self.storage
1935 .load(quads.into_iter().map(|q| q.map(Into::into)))
1936 }
1937}
1938
1939#[cfg(test)]
1940#[allow(clippy::panic_in_result_fn)]
1941mod tests {
1942 use super::*;
1943
1944 #[test]
1945 fn test_send_sync() {
1946 fn is_send_sync<T: Send + Sync>() {}
1947 is_send_sync::<Store>();
1948 }
1949
1950 #[test]
1951 fn store() -> Result<(), StorageError> {
1952 use crate::model::*;
1953
1954 let main_s = Subject::from(BlankNode::default());
1955 let main_p = NamedNode::new("http://example.com").unwrap();
1956 let main_o = Term::from(Literal::from(1));
1957 let main_g = GraphName::from(BlankNode::default());
1958
1959 let default_quad = Quad::new(
1960 main_s.clone(),
1961 main_p.clone(),
1962 main_o.clone(),
1963 GraphName::DefaultGraph,
1964 );
1965 let named_quad = Quad::new(
1966 main_s.clone(),
1967 main_p.clone(),
1968 main_o.clone(),
1969 main_g.clone(),
1970 );
1971 let mut default_quads = vec![
1972 Quad::new(
1973 main_s.clone(),
1974 main_p.clone(),
1975 Literal::from(0),
1976 GraphName::DefaultGraph,
1977 ),
1978 default_quad.clone(),
1979 Quad::new(
1980 main_s.clone(),
1981 main_p.clone(),
1982 Literal::from(200_000_000),
1983 GraphName::DefaultGraph,
1984 ),
1985 ];
1986 let all_quads = vec![
1987 named_quad.clone(),
1988 Quad::new(
1989 main_s.clone(),
1990 main_p.clone(),
1991 Literal::from(200_000_000),
1992 GraphName::DefaultGraph,
1993 ),
1994 default_quad.clone(),
1995 Quad::new(
1996 main_s.clone(),
1997 main_p.clone(),
1998 Literal::from(0),
1999 GraphName::DefaultGraph,
2000 ),
2001 ];
2002
2003 let store = Store::new()?;
2004 for t in &default_quads {
2005 assert!(store.insert(t)?);
2006 }
2007 assert!(!store.insert(&default_quad)?);
2008
2009 assert!(store.remove(&default_quad)?);
2010 assert!(!store.remove(&default_quad)?);
2011 assert!(store.insert(&named_quad)?);
2012 assert!(!store.insert(&named_quad)?);
2013 assert!(store.insert(&default_quad)?);
2014 assert!(!store.insert(&default_quad)?);
2015 store.validate()?;
2016
2017 assert_eq!(store.len()?, 4);
2018 assert_eq!(store.iter().collect::<Result<Vec<_>, _>>()?, all_quads);
2019 assert_eq!(
2020 store
2021 .quads_for_pattern(Some(main_s.as_ref()), None, None, None)
2022 .collect::<Result<Vec<_>, _>>()?,
2023 all_quads
2024 );
2025 assert_eq!(
2026 store
2027 .quads_for_pattern(Some(main_s.as_ref()), Some(main_p.as_ref()), None, None)
2028 .collect::<Result<Vec<_>, _>>()?,
2029 all_quads
2030 );
2031 assert_eq!(
2032 store
2033 .quads_for_pattern(
2034 Some(main_s.as_ref()),
2035 Some(main_p.as_ref()),
2036 Some(main_o.as_ref()),
2037 None
2038 )
2039 .collect::<Result<Vec<_>, _>>()?,
2040 vec![named_quad.clone(), default_quad.clone()]
2041 );
2042 assert_eq!(
2043 store
2044 .quads_for_pattern(
2045 Some(main_s.as_ref()),
2046 Some(main_p.as_ref()),
2047 Some(main_o.as_ref()),
2048 Some(GraphNameRef::DefaultGraph)
2049 )
2050 .collect::<Result<Vec<_>, _>>()?,
2051 vec![default_quad.clone()]
2052 );
2053 assert_eq!(
2054 store
2055 .quads_for_pattern(
2056 Some(main_s.as_ref()),
2057 Some(main_p.as_ref()),
2058 Some(main_o.as_ref()),
2059 Some(main_g.as_ref())
2060 )
2061 .collect::<Result<Vec<_>, _>>()?,
2062 vec![named_quad.clone()]
2063 );
2064 default_quads.reverse();
2065 assert_eq!(
2066 store
2067 .quads_for_pattern(
2068 Some(main_s.as_ref()),
2069 Some(main_p.as_ref()),
2070 None,
2071 Some(GraphNameRef::DefaultGraph)
2072 )
2073 .collect::<Result<Vec<_>, _>>()?,
2074 default_quads
2075 );
2076 assert_eq!(
2077 store
2078 .quads_for_pattern(Some(main_s.as_ref()), None, Some(main_o.as_ref()), None)
2079 .collect::<Result<Vec<_>, _>>()?,
2080 vec![named_quad.clone(), default_quad.clone()]
2081 );
2082 assert_eq!(
2083 store
2084 .quads_for_pattern(
2085 Some(main_s.as_ref()),
2086 None,
2087 Some(main_o.as_ref()),
2088 Some(GraphNameRef::DefaultGraph)
2089 )
2090 .collect::<Result<Vec<_>, _>>()?,
2091 vec![default_quad.clone()]
2092 );
2093 assert_eq!(
2094 store
2095 .quads_for_pattern(
2096 Some(main_s.as_ref()),
2097 None,
2098 Some(main_o.as_ref()),
2099 Some(main_g.as_ref())
2100 )
2101 .collect::<Result<Vec<_>, _>>()?,
2102 vec![named_quad.clone()]
2103 );
2104 assert_eq!(
2105 store
2106 .quads_for_pattern(
2107 Some(main_s.as_ref()),
2108 None,
2109 None,
2110 Some(GraphNameRef::DefaultGraph)
2111 )
2112 .collect::<Result<Vec<_>, _>>()?,
2113 default_quads
2114 );
2115 assert_eq!(
2116 store
2117 .quads_for_pattern(None, Some(main_p.as_ref()), None, None)
2118 .collect::<Result<Vec<_>, _>>()?,
2119 all_quads
2120 );
2121 assert_eq!(
2122 store
2123 .quads_for_pattern(None, Some(main_p.as_ref()), Some(main_o.as_ref()), None)
2124 .collect::<Result<Vec<_>, _>>()?,
2125 vec![named_quad.clone(), default_quad.clone()]
2126 );
2127 assert_eq!(
2128 store
2129 .quads_for_pattern(None, None, Some(main_o.as_ref()), None)
2130 .collect::<Result<Vec<_>, _>>()?,
2131 vec![named_quad.clone(), default_quad.clone()]
2132 );
2133 assert_eq!(
2134 store
2135 .quads_for_pattern(None, None, None, Some(GraphNameRef::DefaultGraph))
2136 .collect::<Result<Vec<_>, _>>()?,
2137 default_quads
2138 );
2139 assert_eq!(
2140 store
2141 .quads_for_pattern(
2142 None,
2143 Some(main_p.as_ref()),
2144 Some(main_o.as_ref()),
2145 Some(GraphNameRef::DefaultGraph)
2146 )
2147 .collect::<Result<Vec<_>, _>>()?,
2148 vec![default_quad]
2149 );
2150 assert_eq!(
2151 store
2152 .quads_for_pattern(
2153 None,
2154 Some(main_p.as_ref()),
2155 Some(main_o.as_ref()),
2156 Some(main_g.as_ref())
2157 )
2158 .collect::<Result<Vec<_>, _>>()?,
2159 vec![named_quad]
2160 );
2161
2162 Ok(())
2163 }
2164}