Collection

Struct Collection 

Source
pub struct Collection { /* private fields */ }
Expand description

A collection represents a namespace for documents in the Sentinel database.

Collections are backed by filesystem directories, where each document is stored as a JSON file. The collection provides CRUD operations (Create, Read, Update, Delete) for managing documents asynchronously using tokio.

§Structure

Each collection is stored in a directory with the following structure:

  • {collection_name}/ - Root directory for the collection
  • {collection_name}/{id}.json - Individual document files

§Example

use sentinel_dbms::{Store, Collection};
use serde_json::json;

// Create a store and get a collection
let store = Store::new("/path/to/data", None).await?;
let collection = store.collection("users").await?;

// Insert a document
let user_data = json!({
    "name": "Alice",
    "email": "alice@example.com"
});
collection.insert("user-123", user_data).await?;

// Retrieve the document
let doc = collection.get("user-123").await?;
assert!(doc.is_some());

Implementations§

Source§

impl Collection

Source

pub fn name(&self) -> &str

Returns the name of the collection.

Source

pub async fn insert(&self, id: &str, data: Value) -> Result<()>

Inserts a new document into the collection or overwrites an existing one.

The document is serialized to pretty-printed JSON and written to a file named {id}.json within the collection’s directory. If a document with the same ID already exists, it will be overwritten.

§Arguments
  • id - A unique identifier for the document. This will be used as the filename (with .json extension). Must be filesystem-safe.
  • data - The JSON data to store. Can be any valid serde_json::Value.
§Returns

Returns Ok(()) on success, or a SentinelError if the operation fails (e.g., filesystem errors, serialization errors).

§Example
use sentinel_dbms::{Store, Collection};
use serde_json::json;

let store = Store::new("/path/to/data", None).await?;
let collection = store.collection("users").await?;

let user = json!({
    "name": "Alice",
    "email": "alice@example.com",
    "age": 30
});

collection.insert("user-123", user).await?;
Source

pub async fn get(&self, id: &str) -> Result<Option<Document>>

Retrieves a document from the collection by its ID.

Reads the JSON file corresponding to the given ID and deserializes it into a Document struct. If the document doesn’t exist, returns None.

§Arguments
  • id - The unique identifier of the document to retrieve.
§Returns

Returns:

  • Ok(Some(Document)) if the document exists and was successfully read
  • Ok(None) if the document doesn’t exist (file not found)
  • Err(SentinelError) if there was an error reading or parsing the document
§Example
use sentinel_dbms::{Store, Collection};
use serde_json::json;

let store = Store::new("/path/to/data", None).await?;
let collection = store.collection("users").await?;

// Insert a document first
collection.insert("user-123", json!({"name": "Alice"})).await?;

// Retrieve the document
let doc = collection.get("user-123").await?;
assert!(doc.is_some());
assert_eq!(doc.unwrap().id(), "user-123");

// Try to get a non-existent document
let missing = collection.get("user-999").await?;
assert!(missing.is_none());
Source

pub async fn update(&self, id: &str, data: Value) -> Result<()>

Updates an existing document or creates a new one if it doesn’t exist.

This method is semantically equivalent to insert in the current implementation, as it overwrites the entire document. Future versions may implement partial updates or version tracking.

§Arguments
  • id - The unique identifier of the document to update.
  • data - The new JSON data that will replace the existing document.
§Returns

Returns Ok(()) on success, or a SentinelError if the operation fails.

§Example
use sentinel_dbms::{Store, Collection};
use serde_json::json;

let store = Store::new("/path/to/data", None).await?;
let collection = store.collection("users").await?;

// Insert initial document
collection.insert("user-123", json!({"name": "Alice", "age": 30})).await?;

// Update the document with new data
collection.update("user-123", json!({"name": "Alice", "age": 31})).await?;

// Verify the update
let doc = collection.get("user-123").await?.unwrap();
assert_eq!(doc.data()["age"], 31);
Source

pub async fn delete(&self, id: &str) -> Result<()>

Deletes a document from the collection (soft delete).

Moves the JSON file corresponding to the given ID to a .deleted/ subdirectory within the collection. This implements soft deletes, allowing for recovery of accidentally deleted documents. The .deleted/ directory is created automatically if it doesn’t exist.

If the document doesn’t exist, the operation succeeds silently (idempotent).

§Arguments
  • id - The unique identifier of the document to delete.
§Returns

Returns Ok(()) on success (including when the document doesn’t exist), or a SentinelError if the operation fails due to filesystem errors.

§Example
use sentinel_dbms::{Store, Collection};
use serde_json::json;

let store = Store::new("/path/to/data", None).await?;
let collection = store.collection("users").await?;

// Insert a document
collection.insert("user-123", json!({"name": "Alice"})).await?;

// Soft delete the document
collection.delete("user-123").await?;

// Document is no longer accessible via get()
let doc = collection.get("user-123").await?;
assert!(doc.is_none());

// But the file still exists in .deleted/
// (can be recovered manually if needed)
Source

pub async fn list(&self) -> Result<Vec<String>>

Lists all document IDs in the collection.

Scans the collection directory for JSON files and returns their IDs (filenames without the .json extension). This operation reads the directory contents and filters for valid document files, skipping hidden directories and metadata directories for optimization.

§Returns

Returns Ok(Vec<String>) containing all document IDs in the collection, or a SentinelError if the operation fails due to filesystem errors.

§Example
use sentinel_dbms::{Store, Collection};
use serde_json::json;

let store = Store::new("/path/to/data", None).await?;
let collection = store.collection("users").await?;

// Insert some documents
collection.insert("user-123", json!({"name": "Alice"})).await?;
collection.insert("user-456", json!({"name": "Bob"})).await?;

// List all documents
let ids = collection.list().await?;
assert_eq!(ids.len(), 2);
assert!(ids.contains(&"user-123".to_string()));
assert!(ids.contains(&"user-456".to_string()));
Source

pub async fn bulk_insert(&self, documents: Vec<(&str, Value)>) -> Result<()>

Performs bulk insert operations on multiple documents.

Inserts multiple documents into the collection in a single operation. If any document fails to insert, the operation stops and returns an error. Documents are inserted in the order provided.

§Arguments
  • documents - A vector of (id, data) tuples to insert.
§Returns

Returns Ok(()) on success, or a SentinelError if any operation fails. In case of failure, some documents may have been inserted before the error.

§Example
use sentinel_dbms::{Store, Collection};
use serde_json::json;

let store = Store::new("/path/to/data", None).await?;
let collection = store.collection("users").await?;

// Prepare bulk documents
let documents = vec![
    ("user-123", json!({"name": "Alice", "role": "admin"})),
    ("user-456", json!({"name": "Bob", "role": "user"})),
    ("user-789", json!({"name": "Charlie", "role": "user"})),
];

// Bulk insert
collection.bulk_insert(documents).await?;

// Verify all documents were inserted
assert!(collection.get("user-123").await?.is_some());
assert!(collection.get("user-456").await?.is_some());
assert!(collection.get("user-789").await?.is_some());

Trait Implementations§

Source§

impl Clone for Collection

Source§

fn clone(&self) -> Collection

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Collection

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl PartialEq for Collection

Source§

fn eq(&self, other: &Collection) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl Eq for Collection

Source§

impl StructuralPartialEq for Collection

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more