BREAKING CHANGE(core): replace the TypeScript database engine with a Rust-backed embedded server and bridge

This commit is contained in:
2026-03-26 19:48:27 +00:00
parent 8ec2046908
commit e23a951dbe
106 changed files with 11567 additions and 10678 deletions
@@ -0,0 +1,196 @@
use std::collections::HashSet;
use bson::{doc, Bson, Document};
use rustdb_query::QueryMatcher;
use tracing::debug;
use crate::context::CommandContext;
use crate::error::{CommandError, CommandResult};
/// Handle the `delete` command.
pub async fn handle(
cmd: &Document,
db: &str,
ctx: &CommandContext,
) -> CommandResult<Document> {
let coll = cmd
.get_str("delete")
.map_err(|_| CommandError::InvalidArgument("missing 'delete' field".into()))?;
let deletes = cmd
.get_array("deletes")
.map_err(|_| CommandError::InvalidArgument("missing 'deletes' array".into()))?;
// Ordered flag (default true).
let ordered = match cmd.get("ordered") {
Some(Bson::Boolean(b)) => *b,
_ => true,
};
debug!(
db = db,
collection = coll,
count = deletes.len(),
"delete command"
);
let ns_key = format!("{}.{}", db, coll);
let mut total_deleted: i32 = 0;
let mut write_errors: Vec<Document> = Vec::new();
for (idx, del_spec) in deletes.iter().enumerate() {
let del_doc = match del_spec {
Bson::Document(d) => d,
_ => {
write_errors.push(doc! {
"index": idx as i32,
"code": 14_i32,
"codeName": "TypeMismatch",
"errmsg": "delete spec must be a document",
});
if ordered {
break;
}
continue;
}
};
// Extract filter (q) and limit.
let filter = match del_doc.get_document("q") {
Ok(f) => f.clone(),
Err(_) => Document::new(), // empty filter matches everything
};
let limit = match del_doc.get("limit") {
Some(Bson::Int32(n)) => *n,
Some(Bson::Int64(n)) => *n as i32,
Some(Bson::Double(n)) => *n as i32,
_ => 0, // default: delete all matches
};
match delete_matching(db, coll, &ns_key, &filter, limit, ctx).await {
Ok(count) => {
total_deleted += count;
}
Err(e) => {
write_errors.push(doc! {
"index": idx as i32,
"code": 1_i32,
"codeName": "InternalError",
"errmsg": e.to_string(),
});
if ordered {
break;
}
}
}
}
// Build response.
let mut response = doc! {
"n": total_deleted,
"ok": 1.0,
};
if !write_errors.is_empty() {
response.insert(
"writeErrors",
write_errors
.into_iter()
.map(Bson::Document)
.collect::<Vec<_>>(),
);
}
Ok(response)
}
/// Find and delete documents matching a filter, returning the number deleted.
async fn delete_matching(
db: &str,
coll: &str,
ns_key: &str,
filter: &Document,
limit: i32,
ctx: &CommandContext,
) -> Result<i32, CommandError> {
// Check if the collection exists; if not, nothing to delete.
match ctx.storage.collection_exists(db, coll).await {
Ok(false) => return Ok(0),
Err(_) => return Ok(0),
Ok(true) => {}
}
// Try to use index to narrow candidates.
let candidate_ids: Option<HashSet<String>> = {
if let Some(engine) = ctx.indexes.get(ns_key) {
engine.find_candidate_ids(filter)
} else {
None
}
};
// Load candidate documents.
let docs = if let Some(ids) = candidate_ids {
if ids.is_empty() {
return Ok(0);
}
ctx.storage
.find_by_ids(db, coll, ids)
.await
.map_err(|e| CommandError::StorageError(e.to_string()))?
} else {
ctx.storage
.find_all(db, coll)
.await
.map_err(|e| CommandError::StorageError(e.to_string()))?
};
// Apply filter to get matched documents.
let matched = QueryMatcher::filter(&docs, filter);
// Apply limit: 0 means delete all, 1 means delete only the first match.
let to_delete: &[Document] = if limit == 1 && !matched.is_empty() {
&matched[..1]
} else {
&matched
};
if to_delete.is_empty() {
return Ok(0);
}
let mut deleted_count: i32 = 0;
for doc in to_delete {
// Extract the _id as a hex string for storage deletion.
let id_str = extract_id_string(doc)?;
ctx.storage
.delete_by_id(db, coll, &id_str)
.await
.map_err(|e| CommandError::StorageError(e.to_string()))?;
// Update index engine.
if let Some(mut engine) = ctx.indexes.get_mut(ns_key) {
engine.on_delete(doc);
}
deleted_count += 1;
}
Ok(deleted_count)
}
/// Extract the `_id` field from a document as a hex string suitable for the
/// storage adapter.
fn extract_id_string(doc: &Document) -> Result<String, CommandError> {
match doc.get("_id") {
Some(Bson::ObjectId(oid)) => Ok(oid.to_hex()),
Some(Bson::String(s)) => Ok(s.clone()),
Some(other) => Ok(format!("{}", other)),
None => Err(CommandError::InvalidArgument(
"document missing _id field".into(),
)),
}
}