1use std::collections::HashMap;
8
9use regex::Regex;
10use serde::{Deserialize, Serialize};
11
12use rouchdb_core::adapter::Adapter;
13use rouchdb_core::collation::collate;
14use rouchdb_core::document::AllDocsOptions;
15use rouchdb_core::error::Result;
16
17#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct IndexDefinition {
20 pub name: String,
22 pub fields: Vec<SortField>,
24 #[serde(skip_serializing_if = "Option::is_none")]
26 pub ddoc: Option<String>,
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct IndexInfo {
32 pub name: String,
34 pub ddoc: Option<String>,
36 pub def: IndexFields,
38}
39
40#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct IndexFields {
43 pub fields: Vec<SortField>,
44}
45
46#[derive(Debug, Clone, Serialize, Deserialize)]
48pub struct CreateIndexResponse {
49 pub result: String,
51 pub name: String,
53}
54
55#[derive(Debug, Clone, Serialize)]
57pub struct ExplainResponse {
58 pub dbname: String,
59 pub index: ExplainIndex,
60 pub selector: serde_json::Value,
61 pub fields: Option<Vec<String>>,
62}
63
64#[derive(Debug, Clone, Serialize)]
66pub struct ExplainIndex {
67 pub ddoc: Option<String>,
68 pub name: String,
69 #[serde(rename = "type")]
70 pub index_type: String,
71 pub def: IndexFields,
72}
73
74#[derive(Debug, Clone)]
76pub struct BuiltIndex {
77 pub def: IndexDefinition,
78 pub entries: Vec<(Vec<serde_json::Value>, String)>,
79}
80
81impl BuiltIndex {
82 pub fn find_matching(&self, selector: &serde_json::Value) -> Vec<String> {
84 if self.def.fields.is_empty() {
85 return Vec::new();
86 }
87
88 let (first_field, _) = self.def.fields[0].field_and_direction();
90
91 if let Some(conditions) = selector.get(first_field) {
92 match conditions {
93 serde_json::Value::Object(ops) => {
94 self.entries
96 .iter()
97 .filter(|(key, _)| {
98 if key.is_empty() {
99 return false;
100 }
101 let val = &key[0];
102 for (op, operand) in ops {
103 let matches = match op.as_str() {
104 "$eq" => collate(val, operand) == std::cmp::Ordering::Equal,
105 "$gt" => collate(val, operand) == std::cmp::Ordering::Greater,
106 "$gte" => collate(val, operand) != std::cmp::Ordering::Less,
107 "$lt" => collate(val, operand) == std::cmp::Ordering::Less,
108 "$lte" => collate(val, operand) != std::cmp::Ordering::Greater,
109 _ => true, };
111 if !matches {
112 return false;
113 }
114 }
115 true
116 })
117 .map(|(_, id)| id.clone())
118 .collect()
119 }
120 other => self
122 .entries
123 .iter()
124 .filter(|(key, _)| {
125 !key.is_empty() && collate(&key[0], other) == std::cmp::Ordering::Equal
126 })
127 .map(|(_, id)| id.clone())
128 .collect(),
129 }
130 } else {
131 self.entries.iter().map(|(_, id)| id.clone()).collect()
133 }
134 }
135}
136
137pub async fn build_index(adapter: &dyn Adapter, def: &IndexDefinition) -> Result<BuiltIndex> {
139 let all = adapter
140 .all_docs(AllDocsOptions {
141 include_docs: true,
142 ..AllDocsOptions::new()
143 })
144 .await?;
145
146 let mut entries: Vec<(Vec<serde_json::Value>, String)> = Vec::new();
147
148 for row in &all.rows {
149 if let Some(ref doc_json) = row.doc {
150 let key: Vec<serde_json::Value> = def
151 .fields
152 .iter()
153 .map(|sf| {
154 let (field, _) = sf.field_and_direction();
155 get_nested_field(doc_json, field)
156 .cloned()
157 .unwrap_or(serde_json::Value::Null)
158 })
159 .collect();
160 entries.push((key, row.id.clone()));
161 }
162 }
163
164 entries.sort_by(|(a, _), (b, _)| {
166 for (va, vb) in a.iter().zip(b.iter()) {
167 let cmp = collate(va, vb);
168 if cmp != std::cmp::Ordering::Equal {
169 return cmp;
170 }
171 }
172 std::cmp::Ordering::Equal
173 });
174
175 Ok(BuiltIndex {
176 def: def.clone(),
177 entries,
178 })
179}
180
181#[derive(Debug, Clone, Default, Serialize, Deserialize)]
183pub struct FindOptions {
184 pub selector: serde_json::Value,
186 #[serde(skip_serializing_if = "Option::is_none")]
188 pub fields: Option<Vec<String>>,
189 #[serde(skip_serializing_if = "Option::is_none")]
191 pub sort: Option<Vec<SortField>>,
192 #[serde(skip_serializing_if = "Option::is_none")]
194 pub limit: Option<u64>,
195 #[serde(skip_serializing_if = "Option::is_none")]
197 pub skip: Option<u64>,
198}
199
200#[derive(Debug, Clone, Serialize, Deserialize)]
202#[serde(untagged)]
203pub enum SortField {
204 Simple(String),
206 WithDirection(HashMap<String, String>),
208}
209
210impl SortField {
211 pub fn field_and_direction(&self) -> (&str, SortDirection) {
212 match self {
213 SortField::Simple(f) => (f.as_str(), SortDirection::Asc),
214 SortField::WithDirection(map) => {
215 let (field, dir) = map.iter().next().unwrap();
216 let direction = if dir == "desc" {
217 SortDirection::Desc
218 } else {
219 SortDirection::Asc
220 };
221 (field.as_str(), direction)
222 }
223 }
224 }
225}
226
227#[derive(Debug, Clone, Copy, PartialEq)]
228pub enum SortDirection {
229 Asc,
230 Desc,
231}
232
233#[derive(Debug, Clone, Serialize, Deserialize)]
235pub struct FindResponse {
236 pub docs: Vec<serde_json::Value>,
237}
238
239pub async fn find(adapter: &dyn Adapter, opts: FindOptions) -> Result<FindResponse> {
241 let all = adapter
243 .all_docs(AllDocsOptions {
244 include_docs: true,
245 ..AllDocsOptions::new()
246 })
247 .await?;
248
249 let mut matched: Vec<serde_json::Value> = Vec::new();
250
251 for row in &all.rows {
252 if let Some(ref doc_json) = row.doc
253 && matches_selector(doc_json, &opts.selector)
254 {
255 matched.push(doc_json.clone());
256 }
257 }
258
259 if let Some(ref sort_fields) = opts.sort {
261 matched.sort_by(|a, b| {
262 for sf in sort_fields {
263 let (field, direction) = sf.field_and_direction();
264 let va = get_nested_field(a, field);
265 let vb = get_nested_field(b, field);
266 let va = va.unwrap_or(&serde_json::Value::Null);
267 let vb = vb.unwrap_or(&serde_json::Value::Null);
268 let cmp = collate(va, vb);
269 let cmp = if direction == SortDirection::Desc {
270 cmp.reverse()
271 } else {
272 cmp
273 };
274 if cmp != std::cmp::Ordering::Equal {
275 return cmp;
276 }
277 }
278 std::cmp::Ordering::Equal
279 });
280 }
281
282 if let Some(skip) = opts.skip {
284 matched = matched.into_iter().skip(skip as usize).collect();
285 }
286
287 if let Some(limit) = opts.limit {
289 matched.truncate(limit as usize);
290 }
291
292 if let Some(ref fields) = opts.fields {
294 matched = matched
295 .into_iter()
296 .map(|doc| project(doc, fields))
297 .collect();
298 }
299
300 Ok(FindResponse { docs: matched })
301}
302
303pub fn matches_selector(doc: &serde_json::Value, selector: &serde_json::Value) -> bool {
305 match selector {
306 serde_json::Value::Object(map) => {
307 for (key, condition) in map {
308 if !match_condition(doc, key, condition) {
309 return false;
310 }
311 }
312 true
313 }
314 _ => false,
315 }
316}
317
318fn match_condition(doc: &serde_json::Value, key: &str, condition: &serde_json::Value) -> bool {
319 match key {
321 "$and" => return match_and(doc, condition),
322 "$or" => return match_or(doc, condition),
323 "$not" => return match_not(doc, condition),
324 "$nor" => return match_nor(doc, condition),
325 _ => {}
326 }
327
328 let field_value = get_nested_field(doc, key);
329
330 match condition {
331 serde_json::Value::Object(ops) => {
333 for (op, operand) in ops {
334 if !match_operator(field_value, op, operand) {
335 return false;
336 }
337 }
338 true
339 }
340 other => match_operator(field_value, "$eq", other),
342 }
343}
344
345fn match_operator(
346 field_value: Option<&serde_json::Value>,
347 op: &str,
348 operand: &serde_json::Value,
349) -> bool {
350 match op {
351 "$eq" => field_value.is_some_and(|v| collate(v, operand) == std::cmp::Ordering::Equal),
352 "$ne" => field_value.is_none_or(|v| collate(v, operand) != std::cmp::Ordering::Equal),
353 "$gt" => field_value.is_some_and(|v| collate(v, operand) == std::cmp::Ordering::Greater),
354 "$gte" => field_value.is_some_and(|v| collate(v, operand) != std::cmp::Ordering::Less),
355 "$lt" => field_value.is_some_and(|v| collate(v, operand) == std::cmp::Ordering::Less),
356 "$lte" => field_value.is_some_and(|v| collate(v, operand) != std::cmp::Ordering::Greater),
357 "$in" => {
358 if let Some(arr) = operand.as_array() {
359 field_value.is_some_and(|v| {
360 arr.iter()
361 .any(|item| collate(v, item) == std::cmp::Ordering::Equal)
362 })
363 } else {
364 false
365 }
366 }
367 "$nin" => {
368 if let Some(arr) = operand.as_array() {
369 field_value.is_none_or(|v| {
370 !arr.iter()
371 .any(|item| collate(v, item) == std::cmp::Ordering::Equal)
372 })
373 } else {
374 true
375 }
376 }
377 "$exists" => {
378 let should_exist = operand.as_bool().unwrap_or(true);
379 if should_exist {
380 field_value.is_some()
381 } else {
382 field_value.is_none()
383 }
384 }
385 "$type" => {
386 if let Some(type_name) = operand.as_str() {
387 field_value.is_some_and(|v| json_type_name(v) == type_name)
388 } else {
389 false
390 }
391 }
392 "$regex" => {
393 if let Some(pattern) = operand.as_str() {
394 field_value.is_some_and(|v| {
395 if let Some(s) = v.as_str() {
396 Regex::new(pattern).is_ok_and(|re| re.is_match(s))
397 } else {
398 false
399 }
400 })
401 } else {
402 false
403 }
404 }
405 "$size" => {
406 if let Some(expected_size) = operand.as_u64() {
407 field_value.is_some_and(|v| {
408 v.as_array()
409 .is_some_and(|arr| arr.len() as u64 == expected_size)
410 })
411 } else {
412 false
413 }
414 }
415 "$all" => {
416 if let Some(required) = operand.as_array() {
417 field_value.is_some_and(|v| {
418 if let Some(arr) = v.as_array() {
419 required.iter().all(|req| {
420 arr.iter()
421 .any(|item| collate(item, req) == std::cmp::Ordering::Equal)
422 })
423 } else {
424 false
425 }
426 })
427 } else {
428 false
429 }
430 }
431 "$elemMatch" => field_value.is_some_and(|v| {
432 if let Some(arr) = v.as_array() {
433 arr.iter().any(|elem| matches_selector(elem, operand))
434 } else {
435 false
436 }
437 }),
438 "$not" => {
439 if let Some(ops) = operand.as_object() {
441 for (sub_op, sub_operand) in ops {
442 if match_operator(field_value, sub_op, sub_operand) {
443 return false;
444 }
445 }
446 true
447 } else {
448 !match_operator(field_value, "$eq", operand)
450 }
451 }
452 "$mod" => {
453 if let Some(arr) = operand.as_array() {
454 if arr.len() == 2 {
455 let divisor = arr[0].as_i64();
456 let remainder = arr[1].as_i64();
457 if let (Some(d), Some(r)) = (divisor, remainder) {
458 field_value
459 .is_some_and(|v| v.as_i64().is_some_and(|n| d != 0 && n % d == r))
460 } else {
461 false
462 }
463 } else {
464 false
465 }
466 } else {
467 false
468 }
469 }
470 _ => false,
471 }
472}
473
474fn match_and(doc: &serde_json::Value, condition: &serde_json::Value) -> bool {
475 if let Some(arr) = condition.as_array() {
476 arr.iter().all(|sub| matches_selector(doc, sub))
477 } else {
478 false
479 }
480}
481
482fn match_or(doc: &serde_json::Value, condition: &serde_json::Value) -> bool {
483 if let Some(arr) = condition.as_array() {
484 arr.iter().any(|sub| matches_selector(doc, sub))
485 } else {
486 false
487 }
488}
489
490fn match_not(doc: &serde_json::Value, condition: &serde_json::Value) -> bool {
491 !matches_selector(doc, condition)
492}
493
494fn match_nor(doc: &serde_json::Value, condition: &serde_json::Value) -> bool {
495 if let Some(arr) = condition.as_array() {
496 !arr.iter().any(|sub| matches_selector(doc, sub))
497 } else {
498 false
499 }
500}
501
502fn get_nested_field<'a>(doc: &'a serde_json::Value, path: &str) -> Option<&'a serde_json::Value> {
504 let mut current = doc;
505 for part in path.split('.') {
506 match current.get(part) {
507 Some(v) => current = v,
508 None => return None,
509 }
510 }
511 Some(current)
512}
513
514fn json_type_name(value: &serde_json::Value) -> &'static str {
516 match value {
517 serde_json::Value::Null => "null",
518 serde_json::Value::Bool(_) => "boolean",
519 serde_json::Value::Number(_) => "number",
520 serde_json::Value::String(_) => "string",
521 serde_json::Value::Array(_) => "array",
522 serde_json::Value::Object(_) => "object",
523 }
524}
525
526fn project(doc: serde_json::Value, fields: &[String]) -> serde_json::Value {
528 let mut result = serde_json::Map::new();
529
530 if let serde_json::Value::Object(map) = &doc {
531 for field in fields {
532 if let Some(val) = map.get(field) {
534 result.insert(field.clone(), val.clone());
535 }
536 }
537 if let Some(id) = map.get("_id") {
539 result
540 .entry("_id".to_string())
541 .or_insert_with(|| id.clone());
542 }
543 }
544
545 serde_json::Value::Object(result)
546}
547
548#[cfg(test)]
553mod tests {
554 use super::*;
555
556 fn doc(json: serde_json::Value) -> serde_json::Value {
557 json
558 }
559
560 #[test]
563 fn eq_implicit() {
564 let d = doc(serde_json::json!({"name": "Alice", "age": 30}));
565 assert!(matches_selector(&d, &serde_json::json!({"name": "Alice"})));
566 assert!(!matches_selector(&d, &serde_json::json!({"name": "Bob"})));
567 }
568
569 #[test]
570 fn eq_explicit() {
571 let d = doc(serde_json::json!({"age": 30}));
572 assert!(matches_selector(
573 &d,
574 &serde_json::json!({"age": {"$eq": 30}})
575 ));
576 }
577
578 #[test]
579 fn ne() {
580 let d = doc(serde_json::json!({"age": 30}));
581 assert!(matches_selector(
582 &d,
583 &serde_json::json!({"age": {"$ne": 25}})
584 ));
585 assert!(!matches_selector(
586 &d,
587 &serde_json::json!({"age": {"$ne": 30}})
588 ));
589 }
590
591 #[test]
592 fn gt_gte_lt_lte() {
593 let d = doc(serde_json::json!({"age": 30}));
594
595 assert!(matches_selector(
596 &d,
597 &serde_json::json!({"age": {"$gt": 20}})
598 ));
599 assert!(!matches_selector(
600 &d,
601 &serde_json::json!({"age": {"$gt": 30}})
602 ));
603
604 assert!(matches_selector(
605 &d,
606 &serde_json::json!({"age": {"$gte": 30}})
607 ));
608 assert!(!matches_selector(
609 &d,
610 &serde_json::json!({"age": {"$gte": 31}})
611 ));
612
613 assert!(matches_selector(
614 &d,
615 &serde_json::json!({"age": {"$lt": 40}})
616 ));
617 assert!(!matches_selector(
618 &d,
619 &serde_json::json!({"age": {"$lt": 30}})
620 ));
621
622 assert!(matches_selector(
623 &d,
624 &serde_json::json!({"age": {"$lte": 30}})
625 ));
626 assert!(!matches_selector(
627 &d,
628 &serde_json::json!({"age": {"$lte": 29}})
629 ));
630 }
631
632 #[test]
633 fn in_nin() {
634 let d = doc(serde_json::json!({"color": "red"}));
635
636 assert!(matches_selector(
637 &d,
638 &serde_json::json!({"color": {"$in": ["red", "blue"]}})
639 ));
640 assert!(!matches_selector(
641 &d,
642 &serde_json::json!({"color": {"$in": ["green", "blue"]}})
643 ));
644
645 assert!(matches_selector(
646 &d,
647 &serde_json::json!({"color": {"$nin": ["green", "blue"]}})
648 ));
649 assert!(!matches_selector(
650 &d,
651 &serde_json::json!({"color": {"$nin": ["red", "blue"]}})
652 ));
653 }
654
655 #[test]
656 fn exists() {
657 let d = doc(serde_json::json!({"name": "Alice"}));
658
659 assert!(matches_selector(
660 &d,
661 &serde_json::json!({"name": {"$exists": true}})
662 ));
663 assert!(!matches_selector(
664 &d,
665 &serde_json::json!({"age": {"$exists": true}})
666 ));
667 assert!(matches_selector(
668 &d,
669 &serde_json::json!({"age": {"$exists": false}})
670 ));
671 }
672
673 #[test]
674 fn type_check() {
675 let d = doc(serde_json::json!({"name": "Alice", "age": 30, "active": true}));
676
677 assert!(matches_selector(
678 &d,
679 &serde_json::json!({"name": {"$type": "string"}})
680 ));
681 assert!(matches_selector(
682 &d,
683 &serde_json::json!({"age": {"$type": "number"}})
684 ));
685 assert!(matches_selector(
686 &d,
687 &serde_json::json!({"active": {"$type": "boolean"}})
688 ));
689 }
690
691 #[test]
692 fn regex_match() {
693 let d = doc(serde_json::json!({"name": "Alice"}));
694
695 assert!(matches_selector(
696 &d,
697 &serde_json::json!({"name": {"$regex": "^Ali"}})
698 ));
699 assert!(!matches_selector(
700 &d,
701 &serde_json::json!({"name": {"$regex": "^Bob"}})
702 ));
703 }
704
705 #[test]
706 fn size_operator() {
707 let d = doc(serde_json::json!({"tags": ["a", "b", "c"]}));
708
709 assert!(matches_selector(
710 &d,
711 &serde_json::json!({"tags": {"$size": 3}})
712 ));
713 assert!(!matches_selector(
714 &d,
715 &serde_json::json!({"tags": {"$size": 2}})
716 ));
717 }
718
719 #[test]
720 fn all_operator() {
721 let d = doc(serde_json::json!({"tags": ["a", "b", "c"]}));
722
723 assert!(matches_selector(
724 &d,
725 &serde_json::json!({"tags": {"$all": ["a", "c"]}})
726 ));
727 assert!(!matches_selector(
728 &d,
729 &serde_json::json!({"tags": {"$all": ["a", "d"]}})
730 ));
731 }
732
733 #[test]
734 fn elem_match() {
735 let d = doc(serde_json::json!({
736 "scores": [
737 {"subject": "math", "grade": 90},
738 {"subject": "english", "grade": 75}
739 ]
740 }));
741
742 assert!(matches_selector(
743 &d,
744 &serde_json::json!({"scores": {"$elemMatch": {"subject": "math", "grade": {"$gt": 80}}}})
745 ));
746 assert!(!matches_selector(
747 &d,
748 &serde_json::json!({"scores": {"$elemMatch": {"subject": "math", "grade": {"$gt": 95}}}})
749 ));
750 }
751
752 #[test]
753 fn mod_operator() {
754 let d = doc(serde_json::json!({"n": 10}));
755
756 assert!(matches_selector(
757 &d,
758 &serde_json::json!({"n": {"$mod": [3, 1]}})
759 ));
760 assert!(!matches_selector(
761 &d,
762 &serde_json::json!({"n": {"$mod": [3, 0]}})
763 ));
764 }
765
766 #[test]
769 fn and_operator() {
770 let d = doc(serde_json::json!({"age": 30, "active": true}));
771
772 assert!(matches_selector(
773 &d,
774 &serde_json::json!({"$and": [{"age": {"$gte": 20}}, {"active": true}]})
775 ));
776 assert!(!matches_selector(
777 &d,
778 &serde_json::json!({"$and": [{"age": {"$gte": 20}}, {"active": false}]})
779 ));
780 }
781
782 #[test]
783 fn or_operator() {
784 let d = doc(serde_json::json!({"age": 30}));
785
786 assert!(matches_selector(
787 &d,
788 &serde_json::json!({"$or": [{"age": 30}, {"age": 40}]})
789 ));
790 assert!(!matches_selector(
791 &d,
792 &serde_json::json!({"$or": [{"age": 20}, {"age": 40}]})
793 ));
794 }
795
796 #[test]
797 fn not_operator() {
798 let d = doc(serde_json::json!({"age": 30}));
799
800 assert!(matches_selector(
801 &d,
802 &serde_json::json!({"$not": {"age": 40}})
803 ));
804 assert!(!matches_selector(
805 &d,
806 &serde_json::json!({"$not": {"age": 30}})
807 ));
808 }
809
810 #[test]
811 fn nor_operator() {
812 let d = doc(serde_json::json!({"age": 30}));
813
814 assert!(matches_selector(
815 &d,
816 &serde_json::json!({"$nor": [{"age": 20}, {"age": 40}]})
817 ));
818 assert!(!matches_selector(
819 &d,
820 &serde_json::json!({"$nor": [{"age": 30}, {"age": 40}]})
821 ));
822 }
823
824 #[test]
827 fn nested_field_access() {
828 let d = doc(serde_json::json!({"address": {"city": "NYC", "zip": "10001"}}));
829
830 assert!(matches_selector(
831 &d,
832 &serde_json::json!({"address.city": "NYC"})
833 ));
834 assert!(!matches_selector(
835 &d,
836 &serde_json::json!({"address.city": "LA"})
837 ));
838 }
839
840 #[test]
843 fn multiple_field_conditions() {
844 let d = doc(serde_json::json!({"name": "Alice", "age": 30}));
845
846 assert!(matches_selector(
848 &d,
849 &serde_json::json!({"name": "Alice", "age": {"$gte": 25}})
850 ));
851 assert!(!matches_selector(
852 &d,
853 &serde_json::json!({"name": "Alice", "age": {"$gte": 35}})
854 ));
855 }
856
857 #[test]
858 fn combined_operators_on_field() {
859 let d = doc(serde_json::json!({"age": 30}));
860
861 assert!(matches_selector(
863 &d,
864 &serde_json::json!({"age": {"$gt": 20, "$lt": 40}})
865 ));
866 assert!(!matches_selector(
867 &d,
868 &serde_json::json!({"age": {"$gt": 30, "$lt": 40}})
869 ));
870 }
871
872 #[test]
875 fn project_fields() {
876 let d = serde_json::json!({"_id": "doc1", "_rev": "1-abc", "name": "Alice", "age": 30});
877 let projected = project(d, &["name".to_string()]);
878
879 assert_eq!(projected["_id"], "doc1");
880 assert_eq!(projected["name"], "Alice");
881 assert!(projected.get("age").is_none());
882 }
883
884 #[test]
887 fn missing_field_ne_matches() {
888 let d = doc(serde_json::json!({"name": "Alice"}));
890 assert!(matches_selector(
891 &d,
892 &serde_json::json!({"age": {"$ne": 30}})
893 ));
894 }
895
896 #[test]
897 fn missing_field_eq_fails() {
898 let d = doc(serde_json::json!({"name": "Alice"}));
899 assert!(!matches_selector(
900 &d,
901 &serde_json::json!({"age": {"$eq": 30}})
902 ));
903 }
904}