Skip to main content

moltendb_core/handlers/
process_update.rs

1use tracing::debug;
2use serde_json::{Value, json};
3use crate::validation;
4use crate::engine;
5
6/// Handle an UPDATE (partial merge) request.
7///
8/// Merges the provided fields into existing documents without overwriting
9/// fields that are not mentioned in the update.
10///
11/// Format: { "collection": "users", "data": { "u1": { "role": "admin" } } }
12pub fn process_update(db: &engine::Db, payload: &Value, max_body_size: usize, max_keys_per_request: usize) -> (u16, Value) {
13    // Only "collection" and "data" are valid for an update/patch request.
14    const UPDATE_ALLOWED: &[&str] = &["collection", "data"];
15    if let Err(e) = validation::validate_allowed_properties(payload, UPDATE_ALLOWED) {
16        return (400, json!({ "error": e.to_string(), "statusCode": 400 }));
17    }
18    if let Err(e) = validation::validate_request(payload, max_body_size, max_keys_per_request) {
19        return (400, json!({ "error": e.to_string(), "statusCode": 400 }));
20    }
21
22    let col = payload["collection"].as_str().unwrap_or("default");
23
24    if let Some(data_map) = payload.get("data").and_then(|v| v.as_object()) {
25        let mut updated_count = 0;
26        for (k, v) in data_map {
27            match db.update(col, k, v.clone()) {
28                Ok(true)  => updated_count += 1,  // Document found and updated
29                Ok(false) => {},                   // Document not found — skip
30                Err(engine::DbError::Conflict) => return (409, json!({ "error": "Conflict: Document version is outdated", "statusCode": 409 })),
31                #[cfg(feature = "schema")]
32                Err(engine::DbError::SchemaValidationError(msg)) => return (400, json!({ "error": msg, "statusCode": 400 })),
33                Err(e) => return (500, json!({ "error": "Database update failed", "details": e.to_string(), "statusCode": 500 }))
34            }
35        }
36        // Check collection size for auto-eviction (Hybrid Bitcask).
37        if let Ok(count) = db.evict_collection(col, db.hot_threshold) {
38            if count > 0 {
39                debug!("❄️  Auto-evicted {} documents from {} to disk", count, col);
40            }
41        }
42        (200, json!({ "status": "ok", "updated": updated_count }))
43    } else {
44        (400, json!({ "error": "Missing 'data' map", "statusCode": 400 }))
45    }
46}