(cmd: &RespCommand, session: &RespSession, state: &SharedState)
| 253 | // --------------------------------------------------------------------------- |
| 254 | |
| 255 | async fn handle_scan(cmd: &RespCommand, session: &RespSession, state: &SharedState) -> RespValue { |
| 256 | let cursor_str = cmd.arg_str(0).unwrap_or("0"); |
| 257 | let cursor = if cursor_str == "0" { |
| 258 | Vec::new() |
| 259 | } else { |
| 260 | base64::Engine::decode(&base64::engine::general_purpose::STANDARD, cursor_str) |
| 261 | .unwrap_or_default() |
| 262 | }; |
| 263 | |
| 264 | // Parse MATCH, COUNT, and FILTER options. |
| 265 | let mut match_pattern: Option<String> = None; |
| 266 | let mut count: usize = 10; |
| 267 | let mut filter_bytes: Vec<u8> = Vec::new(); |
| 268 | let mut i = 1; |
| 269 | while i < cmd.argc() { |
| 270 | match cmd.arg_str(i).map(|s| s.to_uppercase()) { |
| 271 | Some(ref flag) if flag == "MATCH" => { |
| 272 | match_pattern = cmd.arg_str(i + 1).map(|s| s.to_string()); |
| 273 | i += 2; |
| 274 | } |
| 275 | Some(ref flag) if flag == "COUNT" => { |
| 276 | count = cmd.arg_i64(i + 1).unwrap_or(10) as usize; |
| 277 | i += 2; |
| 278 | } |
| 279 | // NodeDB extension: SCAN 0 FILTER <field> = <value> |
| 280 | Some(ref flag) if flag == "FILTER" && i + 4 <= cmd.argc() => { |
| 281 | // Parse simple "field = value" predicate (needs 4 args: FILTER field = value). |
| 282 | let field = cmd.arg_str(i + 1).unwrap_or(""); |
| 283 | let _op = cmd.arg_str(i + 2).unwrap_or(""); // "=" expected |
| 284 | let value = cmd.arg_str(i + 3).unwrap_or(""); |
| 285 | let scan_filter = serde_json::json!([{ |
| 286 | "field": field, |
| 287 | "op": "eq", |
| 288 | "value": value, |
| 289 | }]); |
| 290 | match nodedb_types::json_to_msgpack(&scan_filter) { |
| 291 | Ok(bytes) => filter_bytes = bytes, |
| 292 | Err(_) => { |
| 293 | return RespValue::err("ERR filter serialization failed"); |
| 294 | } |
| 295 | } |
| 296 | i += 4; |
| 297 | } |
| 298 | _ => { |
| 299 | i += 1; |
| 300 | } |
| 301 | } |
| 302 | } |
| 303 | |
| 304 | let plan = PhysicalPlan::Kv(KvOp::Scan { |
| 305 | collection: session.collection.clone(), |
| 306 | cursor, |
| 307 | count, |
| 308 | filters: filter_bytes, |
| 309 | match_pattern, |
| 310 | sort_keys: Vec::new(), |
| 311 | surrogate_ceiling: None, |
| 312 | }); |
no test coverage detected