Skip to main content

DocumentStore

Struct DocumentStore 

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

The top-level embedded document store.

Holds a collection of parsed Markdown documents and provides access to the query interface. Documents are stored in memory with their flattened block lists and interval indexes.

§Load modes

Secondary indexes (DocumentIndex) are built once via load_all_indexes and cached, so subsequent crate::SqlEngine construction is O(1).

§Example

use mq_db::DocumentStore;

let mut store = DocumentStore::new();
store.add_str("# Hello\n\nWorld").unwrap();

let results = store.query().heading_depth(1).blocks();
assert_eq!(results.len(), 1);
assert_eq!(results[0].content, "Hello");

Implementations§

Source§

impl DocumentStore

Source

pub fn execute_sql_mut(&mut self, sql: &str) -> Result<QueryOutput, MqdbError>

Execute a SQL statement that may mutate the store.

UPDATE/DELETE against the blocks table are handled directly — see the module-level write-back notes above — and are written back to the affected document’s source Markdown file (re-parsed in place, same DocumentId). Everything else (SELECT, CREATE TABLE, INSERT, DROP TABLE, DESC, SHOW TABLES) delegates to the regular read-only SqlEngine::execute.

Callers that expose this over an interface an end user might not expect to mutate files (a CLI, an HTTP/MCP endpoint) should gate it behind an explicit opt-in before calling this — write-back mutates the user’s Markdown source on disk.

Source§

impl DocumentStore

Source

pub fn new() -> Self

Creates an empty document store.

Source

pub fn set_store_spans(&mut self, val: bool)

When set to false, source line/column spans are stripped from every block added after this call. Reduces memory by ~21 bytes per block.

Source

pub fn register_table( &mut self, name: impl Into<String>, columns: Vec<String>, rows: Vec<Vec<String>>, )

Register a custom virtual table that can be queried via SQL.

The table is queryable with SELECT … FROM <name>. All column values are treated as strings; cast them in SQL as needed.

Calling this a second time with the same name replaces the previous table.

Source

pub fn unregister_table(&mut self, name: &str) -> bool

Remove a previously registered custom table. Returns true if it existed.

Source

pub fn attach(&self, alias: DatabaseAlias, path: &Path) -> Result<(), MqdbError>

Attach another .mq-db store under alias, queryable as <alias>.<table>. Session-scoped; not persisted.

Source

pub fn detach(&self, alias: &str) -> bool

Detach a previously attached store. Returns true if alias was attached, false otherwise.

Source

pub fn add_file( &mut self, path: impl AsRef<Path>, ) -> Result<DocumentId, MqdbError>

Parses and adds a Markdown file from disk.

Returns the assigned DocumentId on success.

Source

pub fn add_str(&mut self, content: &str) -> Result<DocumentId, MqdbError>

Parses and adds Markdown content from a string.

Returns the assigned DocumentId on success.

Source

pub fn add_str_with_path( &mut self, content: &str, path: Option<PathBuf>, ) -> Result<DocumentId, MqdbError>

Parses and adds already-read Markdown content, attributing it to path. For callers that read files concurrently and want to skip add_file’s own read.

Source

pub fn append_str(&mut self, content: &str) -> Result<DocumentId, MqdbError>

Append a Markdown string to the existing .mq-db file (in-place).

Works only when the store was opened via DocumentStore::open (i.e. self.storage is Some). New block pages and an index page chain are appended to the file and the catalog is rewritten to include the new entry.

When called on an in-memory store (no backing file) this behaves identically to add_str.

Source

pub fn append_file( &mut self, path: impl AsRef<Path>, ) -> Result<DocumentId, MqdbError>

Append a Markdown file to the existing .mq-db file (in-place).

See append_str for full semantics.

Source

pub fn replace_document( &mut self, doc_id: DocumentId, content: &str, path: Option<PathBuf>, ) -> Result<(), MqdbError>

Replace the content of an existing document in place, keeping its DocumentId stable (so any external references to it — e.g. mq() join columns, application code holding the id — stay valid).

Re-parses content, writes fresh block/index page chains to the backing storage file (if one is open) and overwrites that document’s catalog entry; the old page chains become orphaned dead space (no compaction yet — a future vacuum command could reclaim them).

For in-memory-only stores (no backing file open), this only updates in-memory state — call save afterward to persist.

Returns an error if no document with doc_id exists.

Source

pub fn reindex_paths( &mut self, files: &[PathBuf], prune: bool, ) -> Result<ReindexReport, MqdbError>

Index files, skipping any whose content hash matches what’s already catalogued (see content_hashes), replacing changed ones in place via replace_document (same DocumentId), and adding new ones exactly like append_file/add_file depending on whether a backing file is open.

When prune is true, any catalogued document whose path is not present in files is dropped from the store.

Documents with no path (added via add_str) are left untouched and are never counted as “removed” by prune.

Source

pub fn documents(&self) -> &[Document]

Returns a slice of all documents in the store.

Source

pub fn get_document(&self, id: DocumentId) -> Option<&Document>

Looks up a document by its DocumentId.

Source

pub fn len(&self) -> usize

Returns the number of documents in the store.

Source

pub fn is_empty(&self) -> bool

Returns true if the store contains no documents.

Source

pub fn query(&self) -> Query<'_>

Creates a new query builder backed by this store.

Source

pub fn stats(&self) -> StoreStats

Aggregate block-type / code-language statistics across every document currently loaded in memory.

Source

pub fn load_all_blocks(&mut self) -> Result<(), MqdbError>

Load blocks for every document that has not yet been loaded.

No-op when the store was built in memory or fully loaded via load().

Source

pub fn load_all_indexes(&mut self) -> Result<(), MqdbError>

Build or load persisted secondary indexes for every document and cache them.

Must be called after load_all_blocks. Subsequent crate::SqlEngine construction reuses the cache and pays no per-block index rebuild cost.

Source

pub fn save(&self, path: impl AsRef<Path>) -> Result<(), MqdbError>

Persist all in-memory documents to a .mq-db file, including secondary indexes. Writes atomically: writes to path.tmp then renames to path.

Source

pub fn vacuum( &mut self, path: impl AsRef<Path>, ) -> Result<VacuumReport, MqdbError>

Rewrites the backing .mq-db file at path from scratch (same compaction save already does), reclaiming dead page chains left behind by replace_document, DROP TABLE/DROP VIEW, and re-indexing changed files. Requires a store opened via open (a live backing file).

Source

pub fn open(path: impl AsRef<Path>) -> Result<Self, MqdbError>

Open a .mq-db file in lazy mode: reads only catalog and zone maps.

Block data is not loaded until you call load_all_blocks. Secondary indexes are not built until you call load_all_indexes.

Source

pub fn load(path: impl AsRef<Path>) -> Result<Self, MqdbError>

Load a .mq-db file and reconstruct the in-memory DocumentStore.

All block data is read from disk. Secondary indexes are not built here — crate::SqlEngine builds them lazily on construction.

Source

pub fn load_catalog_only(path: impl AsRef<Path>) -> Result<Self, MqdbError>

Load only the catalog metadata from a .mq-db file — no block data.

Documents have block_count populated from the catalog but blocks is empty. Useful for commands that only need zone-map metadata (e.g. list), avoiding the cost of deserialising all block data.

Source

pub fn file_version(path: impl AsRef<Path>) -> Result<u32, MqdbError>

The on-disk file-format version at path, without loading the catalog. Use this to decide whether DocumentStore::migrate is needed before calling DocumentStore::open.

Source

pub fn migrate(path: impl AsRef<Path>) -> Result<u32, MqdbError>

Rewrites a store written by an older but still-recognised file format (see storage::page) in the current format, rebuilding every document’s secondary index (including any index sections added since that version) from its stored blocks. Returns the version the file was in before migrating; a no-op if it was already current.

This does not touch the original bytes in place — like Self::save, it writes a new file to <path>.tmp and renames it over path, so a failure or interruption midway leaves the original file untouched. Callers that want a backup of the pre-migration file should copy it before calling this.

Trait Implementations§

Source§

impl Default for DocumentStore

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

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<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

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> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
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.