BREAKING CHANGE(core): replace the TypeScript database engine with a Rust-backed embedded server and bridge
This commit is contained in:
@@ -0,0 +1,168 @@
|
||||
use bson::{Bson, Document};
|
||||
|
||||
use crate::field_path::get_nested_value;
|
||||
|
||||
/// Apply a projection to a document.
|
||||
/// Inclusion mode: only specified fields + _id.
|
||||
/// Exclusion mode: all fields except specified ones.
|
||||
/// _id can be explicitly excluded in either mode.
|
||||
pub fn apply_projection(doc: &Document, projection: &Document) -> Document {
|
||||
if projection.is_empty() {
|
||||
return doc.clone();
|
||||
}
|
||||
|
||||
// Determine mode: inclusion or exclusion
|
||||
let mut has_inclusion = false;
|
||||
let mut id_explicitly_set = false;
|
||||
|
||||
for (key, value) in projection {
|
||||
if key == "_id" {
|
||||
id_explicitly_set = true;
|
||||
continue;
|
||||
}
|
||||
match value {
|
||||
Bson::Int32(0) | Bson::Int64(0) | Bson::Boolean(false) => {}
|
||||
_ => has_inclusion = true,
|
||||
}
|
||||
}
|
||||
|
||||
if has_inclusion {
|
||||
apply_inclusion(doc, projection, id_explicitly_set)
|
||||
} else {
|
||||
apply_exclusion(doc, projection)
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_inclusion(doc: &Document, projection: &Document, id_explicitly_set: bool) -> Document {
|
||||
let mut result = Document::new();
|
||||
|
||||
// Include _id by default unless explicitly excluded
|
||||
let include_id = if id_explicitly_set {
|
||||
is_truthy(projection.get("_id"))
|
||||
} else {
|
||||
true
|
||||
};
|
||||
|
||||
if include_id {
|
||||
if let Some(id) = doc.get("_id") {
|
||||
result.insert("_id", id.clone());
|
||||
}
|
||||
}
|
||||
|
||||
for (key, value) in projection {
|
||||
if key == "_id" {
|
||||
continue;
|
||||
}
|
||||
if !is_truthy(Some(value)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if key.contains('.') {
|
||||
if let Some(val) = get_nested_value(doc, key) {
|
||||
// Rebuild nested structure
|
||||
set_nested_in_result(&mut result, key, val);
|
||||
}
|
||||
} else if let Some(val) = doc.get(key) {
|
||||
result.insert(key.clone(), val.clone());
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn apply_exclusion(doc: &Document, projection: &Document) -> Document {
|
||||
let mut result = doc.clone();
|
||||
|
||||
for (key, value) in projection {
|
||||
if !is_truthy(Some(value)) {
|
||||
if key.contains('.') {
|
||||
// Remove nested field
|
||||
remove_nested_from_result(&mut result, key);
|
||||
} else {
|
||||
result.remove(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn is_truthy(value: Option<&Bson>) -> bool {
|
||||
match value {
|
||||
None => false,
|
||||
Some(Bson::Int32(0)) | Some(Bson::Int64(0)) | Some(Bson::Boolean(false)) => false,
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
|
||||
fn set_nested_in_result(doc: &mut Document, path: &str, value: Bson) {
|
||||
let parts: Vec<&str> = path.split('.').collect();
|
||||
set_nested_recursive(doc, &parts, value);
|
||||
}
|
||||
|
||||
fn set_nested_recursive(doc: &mut Document, parts: &[&str], value: Bson) {
|
||||
if parts.len() == 1 {
|
||||
doc.insert(parts[0].to_string(), value);
|
||||
return;
|
||||
}
|
||||
|
||||
let key = parts[0];
|
||||
if !doc.contains_key(key) {
|
||||
doc.insert(key.to_string(), Bson::Document(Document::new()));
|
||||
}
|
||||
if let Some(Bson::Document(ref mut nested)) = doc.get_mut(key) {
|
||||
set_nested_recursive(nested, &parts[1..], value);
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_nested_from_result(doc: &mut Document, path: &str) {
|
||||
let parts: Vec<&str> = path.split('.').collect();
|
||||
remove_nested_recursive(doc, &parts);
|
||||
}
|
||||
|
||||
fn remove_nested_recursive(doc: &mut Document, parts: &[&str]) {
|
||||
if parts.len() == 1 {
|
||||
doc.remove(parts[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
let key = parts[0];
|
||||
if let Some(Bson::Document(ref mut nested)) = doc.get_mut(key) {
|
||||
remove_nested_recursive(nested, &parts[1..]);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_inclusion_projection() {
|
||||
let doc = bson::doc! { "_id": 1, "name": "Alice", "age": 30, "email": "a@b.c" };
|
||||
let proj = bson::doc! { "name": 1, "age": 1 };
|
||||
let result = apply_projection(&doc, &proj);
|
||||
assert!(result.contains_key("_id"));
|
||||
assert!(result.contains_key("name"));
|
||||
assert!(result.contains_key("age"));
|
||||
assert!(!result.contains_key("email"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_exclusion_projection() {
|
||||
let doc = bson::doc! { "_id": 1, "name": "Alice", "age": 30 };
|
||||
let proj = bson::doc! { "age": 0 };
|
||||
let result = apply_projection(&doc, &proj);
|
||||
assert!(result.contains_key("_id"));
|
||||
assert!(result.contains_key("name"));
|
||||
assert!(!result.contains_key("age"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_exclude_id() {
|
||||
let doc = bson::doc! { "_id": 1, "name": "Alice" };
|
||||
let proj = bson::doc! { "name": 1, "_id": 0 };
|
||||
let result = apply_projection(&doc, &proj);
|
||||
assert!(!result.contains_key("_id"));
|
||||
assert!(result.contains_key("name"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user