1use core::ops::Bound;
69
70use yo_common::{Code, Error, Result};
71use yo_kv::{Cursor, Elements, Full};
72
73use crate::head::{DEPTH_MAX, Kind};
74use crate::index::{self, IndexKind, Key, PathIndex};
75use crate::path::{Step, Steps};
76use crate::{Builder, Keys, Value};
77
78#[derive(Debug)]
80pub struct Docs {
81 rows: Elements<()>,
83 keys: Keys,
85 build: Builder,
87 indexes: Vec<PathIndex>,
89 taken: Vec<Vec<u8>>,
96}
97
98impl Default for Docs {
99 fn default() -> Docs {
102 Docs::new()
103 }
104}
105
106impl Docs {
107 #[must_use]
109 pub fn new() -> Docs {
110 Docs {
111 rows: Elements::tailed(0, 0),
112 keys: Keys::new(),
113 build: Builder::new(),
114 indexes: Vec::new(),
115 taken: Vec::new(),
116 }
117 }
118
119 #[must_use]
124 pub fn with_capacity(n: usize, each: usize) -> Docs {
125 Docs {
126 rows: Elements::tailed(n, n.saturating_mul(each)),
127 keys: Keys::new(),
128 build: Builder::with_capacity(each),
129 indexes: Vec::new(),
130 taken: Vec::new(),
131 }
132 }
133
134 pub fn put(&mut self, id: &[u8], value: Value<'_>) -> Result<bool> {
142 self.write(id, value, None)
143 }
144
145 pub fn put_bytes(&mut self, id: &[u8], doc: &[u8]) -> Result<bool> {
152 let value = Value::new(doc)
153 .ok_or_else(|| Error::new(Code::Corrupt, "the document is not a readable value"))?;
154 self.write(id, value, Some(doc))
155 }
156
157 fn write(&mut self, id: &[u8], value: Value<'_>, raw: Option<&[u8]>) -> Result<bool> {
168 let Docs {
169 rows,
170 keys,
171 build,
172 indexes,
173 taken,
174 } = self;
175
176 taken.resize(indexes.len(), Vec::new());
177 for (slot, index) in taken.iter_mut().zip(indexes.iter()) {
178 slot.clear();
179 let Some(at) = value.path_bytes(index.path())? else {
182 continue;
183 };
184 if index.keys_at(at, slot).is_err() {
185 return Err(Error::fmt(
186 Code::Full,
187 format_args!(
188 "a value at {} is longer than {} bytes and cannot be indexed",
189 String::from_utf8_lossy(index.path()),
190 index::KEY_MAX
191 ),
192 ));
193 }
194 }
195
196 unindex(rows, keys, indexes, id);
197
198 build.clear();
199 let fresh = if intern_into(keys, build, value, 0)? {
200 store(rows, id, build.finish()?)?
201 } else if let Some(raw) = raw {
202 store(rows, id, raw)?
205 } else {
206 build.clear();
207 build.embed(&value)?;
208 store(rows, id, build.finish()?)?
209 };
210
211 for (slot, index) in taken.iter().zip(indexes.iter_mut()) {
212 let mut filed = Ok(());
213 index::each_key(slot, |key| {
214 if filed.is_ok() {
215 filed = index.add(key, id);
216 }
217 });
218 filed?;
219 }
220 Ok(fresh)
221 }
222
223 pub fn create_index(&mut self, path: &str) -> Result<()> {
236 self.create_index_bytes(path.as_bytes(), IndexKind::Equality)
237 }
238
239 pub fn create_ordered_index(&mut self, path: &str) -> Result<()> {
251 self.create_index_bytes(path.as_bytes(), IndexKind::Ordered)
252 }
253
254 pub fn create_array_index(&mut self, path: &str) -> Result<()> {
265 self.create_index_bytes(path.as_bytes(), IndexKind::Array)
266 }
267
268 pub fn create_text_index(&mut self, path: &str) -> Result<()> {
281 self.create_index_bytes(path.as_bytes(), IndexKind::Text)
282 }
283
284 pub fn create_index_bytes(&mut self, path: &[u8], kind: IndexKind) -> Result<()> {
287 for step in Steps::new(path) {
288 step?;
289 }
290 match self.indexes.iter().position(|i| i.path() == path) {
291 Some(at) if self.indexes[at].kind() == kind => return Ok(()),
295 Some(at)
296 if kind == IndexKind::Equality && self.indexes[at].kind() == IndexKind::Ordered =>
297 {
298 return Ok(());
299 }
300 Some(at) => {
301 self.indexes.remove(at);
302 self.taken.truncate(self.indexes.len());
303 }
304 None => {}
305 }
306 let mut index = PathIndex::new(path, kind);
307 let mut list = Vec::new();
308 for (id, bytes) in self.rows.pairs() {
309 let Some(value) = Value::new(bytes) else {
310 continue;
311 };
312 let doc = Doc {
313 value,
314 keys: &self.keys,
315 };
316 let Some(at) = doc.path_bytes(path)? else {
317 continue;
318 };
319 list.clear();
320 if index.keys_at(at.value(), &mut list).is_err() {
321 return Err(Error::fmt(
322 Code::Full,
323 format_args!(
324 "a value at {} in {} is longer than {} bytes and cannot be indexed",
325 String::from_utf8_lossy(path),
326 String::from_utf8_lossy(id),
327 index::KEY_MAX
328 ),
329 ));
330 }
331 let mut filed = Ok(());
332 index::each_key(&list, |key| {
333 if filed.is_ok() {
334 filed = index.add(key, id);
335 }
336 });
337 filed?;
338 }
339 self.indexes.push(index);
340 self.taken.push(Vec::new());
341 Ok(())
342 }
343
344 pub fn drop_index(&mut self, path: &str) -> bool {
346 self.drop_index_bytes(path.as_bytes())
347 }
348
349 pub fn drop_index_bytes(&mut self, path: &[u8]) -> bool {
351 let Some(at) = self.indexes.iter().position(|i| i.path() == path) else {
352 return false;
353 };
354 self.indexes.remove(at);
355 self.taken.truncate(self.indexes.len());
356 true
357 }
358
359 #[must_use]
361 pub fn indexes(&self) -> &[PathIndex] {
362 &self.indexes
363 }
364
365 #[must_use]
367 pub fn index(&self, path: &str) -> Option<&PathIndex> {
368 self.indexes.iter().find(|i| i.path() == path.as_bytes())
369 }
370
371 pub fn find(&self, path: &str, key: &Key, mut f: impl FnMut(&[u8], Doc<'_>)) -> Result<usize> {
380 let index = self.index(path).ok_or_else(|| {
381 Error::fmt(
382 Code::Invalid,
383 format_args!("there is no index on {path}, so this would be a scan"),
384 )
385 })?;
386 let Some(set) = index.get(key) else {
387 return Ok(0);
388 };
389 let mut n = 0usize;
390 index::each_id(set, |id| {
391 if let Some(doc) = self.get(id) {
392 f(id, doc);
393 n += 1;
394 }
395 });
396 Ok(n)
397 }
398
399 pub fn count(&self, path: &str, key: &Key) -> Result<usize> {
404 let index = self.index(path).ok_or_else(|| {
405 Error::fmt(
406 Code::Invalid,
407 format_args!("there is no index on {path}, so this would be a scan"),
408 )
409 })?;
410 Ok(index.count(key))
411 }
412
413 pub fn range(
425 &self,
426 path: &str,
427 lo: Bound<&Key>,
428 hi: Bound<&Key>,
429 mut f: impl FnMut(&[u8], Doc<'_>),
430 ) -> Result<usize> {
431 let index = self.ordered(path)?;
432 let mut n = 0usize;
433 for (_, set) in index.range(lo, hi) {
434 index::each_id(set, |id| {
435 if let Some(doc) = self.get(id) {
436 f(id, doc);
437 n += 1;
438 }
439 });
440 }
441 Ok(n)
442 }
443
444 pub fn range_rev(
446 &self,
447 path: &str,
448 lo: Bound<&Key>,
449 hi: Bound<&Key>,
450 mut f: impl FnMut(&[u8], Doc<'_>),
451 ) -> Result<usize> {
452 let index = self.ordered(path)?;
453 let mut n = 0usize;
454 for (_, set) in index.range_rev(lo, hi) {
455 index::each_id(set, |id| {
456 if let Some(doc) = self.get(id) {
457 f(id, doc);
458 n += 1;
459 }
460 });
461 }
462 Ok(n)
463 }
464
465 pub fn count_range(&self, path: &str, lo: Bound<&Key>, hi: Bound<&Key>) -> Result<usize> {
472 Ok(self.ordered(path)?.count_in(lo, hi))
473 }
474
475 fn ordered(&self, path: &str) -> Result<&PathIndex> {
477 match self.index(path) {
478 Some(index) if index.kind() == IndexKind::Ordered => Ok(index),
479 Some(_) => Err(Error::fmt(
480 Code::Invalid,
481 format_args!("the index on {path} answers equality and not ranges"),
482 )),
483 None => Err(Error::fmt(
484 Code::Invalid,
485 format_args!("there is no index on {path}, so this would be a scan"),
486 )),
487 }
488 }
489
490 #[must_use]
492 pub fn get(&self, id: &[u8]) -> Option<Doc<'_>> {
493 let value = Value::new(self.rows.tail(id)?)?;
494 Some(Doc {
495 value,
496 keys: &self.keys,
497 })
498 }
499
500 #[must_use]
505 pub fn bytes(&self, id: &[u8]) -> Option<&[u8]> {
506 self.rows.tail(id)
507 }
508
509 #[must_use]
511 pub fn contains(&self, id: &[u8]) -> bool {
512 self.rows.contains(id)
513 }
514
515 pub fn remove(&mut self, id: &[u8]) -> bool {
524 let Docs {
525 rows,
526 keys,
527 indexes,
528 ..
529 } = self;
530 unindex(rows, keys, indexes, id);
531 rows.remove(id).is_some()
532 }
533
534 #[must_use]
536 pub fn len(&self) -> usize {
537 self.rows.len()
538 }
539
540 #[must_use]
542 pub fn is_empty(&self) -> bool {
543 self.rows.is_empty()
544 }
545
546 #[must_use]
548 pub fn keys(&self) -> &Keys {
549 &self.keys
550 }
551
552 pub fn iter(&self) -> impl Iterator<Item = (&[u8], Doc<'_>)> {
554 let keys = &self.keys;
555 self.rows.pairs().filter_map(move |(id, bytes)| {
556 let value = Value::new(bytes)?;
557 Some((id, Doc { value, keys }))
558 })
559 }
560
561 pub fn scan<F>(&self, cursor: Cursor, count: usize, mut f: F) -> Cursor
564 where
565 F: FnMut(&[u8], Doc<'_>),
566 {
567 let keys = &self.keys;
568 self.rows.scan_pairs(cursor, count, |id, bytes| {
569 if let Some(value) = Value::new(bytes) {
570 f(id, Doc { value, keys });
571 }
572 })
573 }
574
575 pub fn clear(&mut self) {
586 self.rows.clear();
587 self.build.clear();
588 for index in &mut self.indexes {
589 index.clear();
590 }
591 }
592
593 #[must_use]
595 pub fn memory_bytes(&self) -> usize {
596 self.rows.memory_bytes()
597 + self.keys.memory_bytes()
598 + self
599 .indexes
600 .iter()
601 .map(PathIndex::memory_bytes)
602 .sum::<usize>()
603 }
604}
605
606fn unindex(rows: &Elements<()>, keys: &Keys, indexes: &mut [PathIndex], id: &[u8]) {
616 if indexes.is_empty() {
617 return;
618 }
619 let Some(bytes) = rows.tail(id) else {
620 return;
621 };
622 let Some(value) = Value::new(bytes) else {
623 return;
624 };
625 let doc = Doc { value, keys };
626 let mut list = Vec::new();
627 for index in indexes {
628 let Ok(Some(at)) = doc.path_bytes(index.path()) else {
629 continue;
630 };
631 list.clear();
632 let _ = index.keys_at(at.value(), &mut list);
635 index::each_key(&list, |key| index.take(key, id));
636 }
637}
638
639fn store(rows: &mut Elements<()>, id: &[u8], bytes: &[u8]) -> Result<bool> {
642 match rows.set_tailed(id, bytes, ()) {
643 Ok((_, fresh)) => Ok(fresh),
644 Err(Full::Name) => Err(Error::fmt(
645 Code::Full,
646 format_args!("a document id is at most {} bytes", yo_kv::NAME_MAX),
647 )),
648 Err(Full::Rows) => Err(Error::fmt(
649 Code::Full,
650 format_args!("a collection holds at most {} documents", yo_kv::MAX_ROWS),
651 )),
652 }
653}
654
655fn intern_into(keys: &mut Keys, b: &mut Builder, value: Value<'_>, depth: usize) -> Result<bool> {
665 let corrupt = || Error::new(Code::Corrupt, "the document is not readable at that point");
666 match value.kind() {
667 Kind::Null => b.null()?,
668 Kind::Bool => b.bool(value.as_bool().ok_or_else(corrupt)?)?,
669 Kind::Int => b.int(value.as_int().ok_or_else(corrupt)?)?,
670 Kind::Float => b.float(value.as_float().ok_or_else(corrupt)?)?,
671 Kind::Text => b.text_bytes(value.text_bytes().ok_or_else(corrupt)?)?,
672 Kind::Array => {
673 b.begin_array()?;
674 for i in 0..value.len() {
675 let child = value.at(i).ok_or_else(corrupt)?;
676 if !intern_into(keys, b, child, depth + 1)? {
677 return Ok(false);
678 }
679 }
680 b.end_array()?;
681 }
682 Kind::Object => {
683 if value.is_interned() {
684 return Err(Error::new(
685 Code::Invalid,
686 "this document's keys are ids from another collection's key table",
687 ));
688 }
689 b.begin_object_interned()?;
690 for i in 0..value.len() {
691 let name = value.key_at(i).ok_or_else(corrupt)?;
692 let Some(id) = keys.intern(name) else {
693 return Ok(false);
694 };
695 b.key_id(id)?;
696 let child = value.at(i).ok_or_else(corrupt)?;
697 if !intern_into(keys, b, child, depth + 1)? {
698 return Ok(false);
699 }
700 }
701 b.end_object()?;
702 }
703 }
704 debug_assert!(depth <= DEPTH_MAX, "the builder caps the depth");
705 Ok(true)
706}
707
708#[derive(Clone, Copy)]
716pub struct Doc<'a> {
717 value: Value<'a>,
718 keys: &'a Keys,
719}
720
721impl<'a> Doc<'a> {
722 #[must_use]
724 pub fn new(value: Value<'a>, keys: &'a Keys) -> Doc<'a> {
725 Doc { value, keys }
726 }
727
728 #[must_use]
730 pub fn value(&self) -> Value<'a> {
731 self.value
732 }
733
734 #[must_use]
736 pub fn keys(&self) -> &'a Keys {
737 self.keys
738 }
739
740 #[must_use]
742 pub fn kind(&self) -> Kind {
743 self.value.kind()
744 }
745
746 #[must_use]
748 pub fn is_null(&self) -> bool {
749 self.value.is_null()
750 }
751
752 #[must_use]
754 pub fn as_bool(&self) -> Option<bool> {
755 self.value.as_bool()
756 }
757
758 #[must_use]
760 pub fn as_int(&self) -> Option<i64> {
761 self.value.as_int()
762 }
763
764 #[must_use]
766 pub fn as_float(&self) -> Option<f64> {
767 self.value.as_float()
768 }
769
770 #[must_use]
772 pub fn as_text(&self) -> Option<&'a str> {
773 self.value.as_text()
774 }
775
776 #[must_use]
778 pub fn text_bytes(&self) -> Option<&'a [u8]> {
779 self.value.text_bytes()
780 }
781
782 #[must_use]
784 pub fn len(&self) -> usize {
785 self.value.len()
786 }
787
788 #[must_use]
790 pub fn is_empty(&self) -> bool {
791 self.value.is_empty()
792 }
793
794 #[must_use]
801 pub fn get(&self, key: &[u8]) -> Option<Doc<'a>> {
802 let value = if self.value.is_interned() {
803 self.value.get_id(self.keys.id(key)?)?
804 } else {
805 self.value.get(key)?
806 };
807 Some(Doc {
808 value,
809 keys: self.keys,
810 })
811 }
812
813 #[must_use]
815 pub fn at(&self, i: usize) -> Option<Doc<'a>> {
816 Some(Doc {
817 value: self.value.at(i)?,
818 keys: self.keys,
819 })
820 }
821
822 #[must_use]
824 pub fn key_at(&self, i: usize) -> Option<&'a [u8]> {
825 if self.value.is_interned() {
826 self.keys.name(self.value.key_id_at(i)?)
827 } else {
828 self.value.key_at(i)
829 }
830 }
831
832 #[must_use]
839 pub fn members(&self) -> DocMembers<'a> {
840 DocMembers { d: *self, i: 0 }
841 }
842
843 #[must_use]
845 pub fn iter(&self) -> DocElems<'a> {
846 DocElems { d: *self, i: 0 }
847 }
848
849 pub fn path(&self, path: &str) -> Result<Option<Doc<'a>>> {
854 self.path_bytes(path.as_bytes())
855 }
856
857 pub fn path_bytes(&self, path: &[u8]) -> Result<Option<Doc<'a>>> {
859 let mut at = *self;
860 for step in Steps::new(path) {
861 let next = match step? {
862 Step::Key(k) => at.get(k),
863 Step::Index(_) if at.kind() != Kind::Array => None,
864 Step::Index(i) => {
865 let n = at.len();
866 let i = if i < 0 {
867 match n.checked_sub(i.unsigned_abs() as usize) {
868 Some(i) => i,
869 None => return Ok(None),
870 }
871 } else {
872 i as usize
873 };
874 at.at(i)
875 }
876 };
877 let Some(next) = next else {
878 return Ok(None);
879 };
880 at = next;
881 }
882 Ok(Some(at))
883 }
884}
885
886impl core::fmt::Debug for Doc<'_> {
887 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
888 match self.kind() {
889 Kind::Object => {
890 let mut m = f.debug_map();
891 for (k, v) in self.members() {
892 m.entry(&String::from_utf8_lossy(k), &v);
893 }
894 m.finish()
895 }
896 Kind::Array => f.debug_list().entries(self.iter()).finish(),
897 _ => self.value.fmt(f),
898 }
899 }
900}
901
902#[derive(Clone)]
904pub struct DocMembers<'a> {
905 d: Doc<'a>,
906 i: usize,
907}
908
909impl<'a> Iterator for DocMembers<'a> {
910 type Item = (&'a [u8], Doc<'a>);
911
912 fn next(&mut self) -> Option<(&'a [u8], Doc<'a>)> {
913 let key = self.d.key_at(self.i)?;
914 let val = self.d.at(self.i)?;
915 self.i += 1;
916 Some((key, val))
917 }
918
919 fn size_hint(&self) -> (usize, Option<usize>) {
920 let left = self.d.len().saturating_sub(self.i);
921 (left, Some(left))
922 }
923}
924
925#[derive(Clone)]
927pub struct DocElems<'a> {
928 d: Doc<'a>,
929 i: usize,
930}
931
932impl<'a> Iterator for DocElems<'a> {
933 type Item = Doc<'a>;
934
935 fn next(&mut self) -> Option<Doc<'a>> {
936 let out = self.d.at(self.i)?;
937 self.i += 1;
938 Some(out)
939 }
940
941 fn size_hint(&self) -> (usize, Option<usize>) {
942 let left = self.d.len().saturating_sub(self.i);
943 (left, Some(left))
944 }
945}
946
947#[cfg(test)]
948mod tests {
949 use super::*;
950
951 fn order(id: i64, status: &str, lines: usize) -> Vec<u8> {
953 let mut b = Builder::new();
954 b.begin_object().expect("open");
955 b.key(b"id").expect("key");
956 b.int(id).expect("value");
957 b.key(b"customer").expect("key");
958 b.int(id * 7).expect("value");
959 b.key(b"status").expect("key");
960 b.text(status).expect("value");
961 b.key(b"lines").expect("key");
962 b.begin_array().expect("open");
963 for i in 0..lines {
964 b.begin_object().expect("open");
965 b.key(b"sku").expect("key");
966 b.text(&format!("sku-{i}")).expect("value");
967 b.key(b"qty").expect("key");
968 b.int(i as i64 + 1).expect("value");
969 b.end_object().expect("close");
970 }
971 b.end_array().expect("close");
972 b.end_object().expect("close");
973 b.finish().expect("finished").to_vec()
974 }
975
976 #[test]
977 fn a_document_reads_back_the_way_it_went_in() {
978 let mut docs = Docs::new();
979 assert!(
980 docs.put_bytes(b"order:1", &order(1, "open", 3))
981 .expect("put")
982 );
983 assert!(
984 !docs
985 .put_bytes(b"order:1", &order(1, "shut", 3))
986 .expect("put")
987 );
988 assert_eq!(docs.len(), 1);
989
990 let d = docs.get(b"order:1").expect("stored");
991 assert_eq!(d.get(b"id").and_then(|v| v.as_int()), Some(1));
992 assert_eq!(d.get(b"status").and_then(|v| v.as_text()), Some("shut"));
993 assert_eq!(d.get(b"lines").map(|v| v.len()), Some(3));
994 assert_eq!(
995 d.path("$.lines[1].sku")
996 .expect("a path")
997 .and_then(|v| v.as_text()),
998 Some("sku-1")
999 );
1000 assert_eq!(
1001 d.path("$.lines[-1].qty")
1002 .expect("a path")
1003 .and_then(|v| v.as_int()),
1004 Some(3)
1005 );
1006 assert!(d.get(b"missing").is_none());
1007 }
1008
1009 #[test]
1010 fn the_keys_are_interned_and_the_names_come_back() {
1011 let mut docs = Docs::new();
1012 docs.put_bytes(b"order:1", &order(1, "open", 2))
1013 .expect("put");
1014 let names: Vec<String> = docs
1015 .keys()
1016 .iter()
1017 .map(|(n, _)| String::from_utf8_lossy(n).into_owned())
1018 .collect();
1019 names.iter().for_each(|n| assert!(!n.is_empty()));
1020 assert_eq!(
1021 docs.keys().len(),
1022 6,
1023 "id customer status lines sku qty: {names:?}"
1024 );
1025
1026 let d = docs.get(b"order:1").expect("stored");
1027 assert!(d.value().is_interned());
1028 let mut got: Vec<&[u8]> = d.members().map(|(k, _)| k).collect();
1029 got.sort_unstable();
1030 assert_eq!(got, [&b"customer"[..], b"id", b"lines", b"status"]);
1031 let line = d.path("$.lines[0]").expect("a path").expect("there");
1032 assert!(line.value().is_interned());
1033 let mut inner: Vec<&[u8]> = line.members().map(|(k, _)| k).collect();
1034 inner.sort_unstable();
1035 assert_eq!(inner, [&b"qty"[..], b"sku"]);
1036 }
1037
1038 fn shrinkage(shape: impl Fn(i64) -> Vec<u8>) -> f64 {
1040 let mut docs = Docs::new();
1041 let mut plain = 0usize;
1042 for i in 0..256i64 {
1043 let bytes = shape(i);
1044 plain += bytes.len();
1045 docs.put_bytes(format!("d:{i}").as_bytes(), &bytes)
1046 .expect("put");
1047 }
1048 let stored: usize = (0..256i64)
1049 .map(|i| {
1050 docs.bytes(format!("d:{i}").as_bytes())
1051 .expect("stored")
1052 .len()
1053 })
1054 .sum();
1055 stored as f64 / plain as f64
1056 }
1057
1058 #[test]
1059 fn interning_makes_a_collection_of_the_same_shape_smaller() {
1060 let names = shrinkage(|i| {
1069 let mut b = Builder::new();
1070 b.begin_object().expect("open");
1071 for f in 0..20 {
1072 b.key(format!("some_field_name_{f:02}").as_bytes())
1073 .expect("key");
1074 b.int(i + f).expect("value");
1075 }
1076 b.end_object().expect("close");
1077 b.finish().expect("finished").to_vec()
1078 });
1079 assert!(names < 0.60, "a document of names kept {names}");
1080
1081 let orders = shrinkage(|i| order(i, "open", 2));
1086 assert!(orders < 0.80, "an order collection kept {orders}");
1087 }
1088
1089 #[test]
1090 fn a_document_whose_keys_are_already_ids_is_refused() {
1091 let mut b = Builder::new();
1092 b.begin_object_interned().expect("open");
1093 b.key_id(0).expect("key");
1094 b.int(1).expect("value");
1095 b.end_object().expect("close");
1096 let bytes = b.finish().expect("finished").to_vec();
1097
1098 let mut docs = Docs::new();
1099 let err = docs.put_bytes(b"x", &bytes).expect_err("refused");
1100 assert_eq!(err.code(), Code::Invalid);
1101 }
1102
1103 #[test]
1104 fn a_document_that_is_not_readable_is_refused() {
1105 let mut docs = Docs::new();
1106 let err = docs.put_bytes(b"x", &[2, 0, 0, 0]).expect_err("refused");
1107 assert_eq!(err.code(), Code::Corrupt);
1108 assert!(docs.is_empty());
1109 }
1110
1111 #[test]
1112 fn a_removal_leaves_every_other_document_where_it_was() {
1113 let mut docs = Docs::new();
1114 for i in 0..64i64 {
1115 docs.put_bytes(format!("order:{i}").as_bytes(), &order(i, "open", 1))
1116 .expect("put");
1117 }
1118 for i in (0..64i64).step_by(3) {
1119 assert!(docs.remove(format!("order:{i}").as_bytes()));
1120 }
1121 assert_eq!(docs.len(), 64 - 22);
1122 for i in 0..64i64 {
1123 let id = format!("order:{i}");
1124 match docs.get(id.as_bytes()) {
1125 Some(d) => {
1126 assert!(i % 3 != 0, "{id} was removed");
1127 assert_eq!(d.get(b"id").and_then(|v| v.as_int()), Some(i));
1128 }
1129 None => assert!(i % 3 == 0, "{id} was not removed"),
1130 }
1131 }
1132 assert_eq!(docs.keys().len(), 6, "a removal does not un-intern a name");
1133 }
1134
1135 #[test]
1136 fn a_walk_sees_every_document_once() {
1137 let mut docs = Docs::new();
1138 for i in 0..200i64 {
1139 docs.put_bytes(format!("order:{i}").as_bytes(), &order(i, "open", 1))
1140 .expect("put");
1141 }
1142
1143 let mut seen: Vec<i64> = docs
1144 .iter()
1145 .map(|(_, d)| d.get(b"id").and_then(|v| v.as_int()).expect("an id"))
1146 .collect();
1147 seen.sort_unstable();
1148 assert_eq!(seen, (0..200).collect::<Vec<i64>>());
1149
1150 let mut scanned = Vec::new();
1151 let mut cursor = Cursor::START;
1152 loop {
1153 cursor = docs.scan(cursor, 16, |id, _| scanned.push(id.to_vec()));
1154 if cursor.is_end() {
1155 break;
1156 }
1157 }
1158 scanned.sort_unstable();
1159 scanned.dedup();
1160 assert_eq!(scanned.len(), 200);
1161 }
1162
1163 #[test]
1164 fn an_empty_collection_answers_nothing_rather_than_failing() {
1165 let docs = Docs::new();
1166 assert!(docs.is_empty());
1167 assert!(docs.get(b"nothing").is_none());
1168 assert!(docs.bytes(b"nothing").is_none());
1169 assert!(!docs.contains(b"nothing"));
1170 assert_eq!(docs.iter().count(), 0);
1171 }
1172
1173 #[test]
1174 fn a_document_prints_with_its_names_back_on() {
1175 let mut docs = Docs::new();
1176 docs.put_bytes(b"order:1", &order(1, "open", 1))
1177 .expect("put");
1178 let text = format!("{:?}", docs.get(b"order:1").expect("stored"));
1179 assert!(text.contains("\"status\": \"open\""), "{text}");
1180 assert!(text.contains("\"sku\": \"sku-0\""), "{text}");
1181 }
1182
1183 fn found(docs: &Docs, path: &str, key: &Key) -> Vec<String> {
1185 let mut out = Vec::new();
1186 let n = docs
1187 .find(path, key, |id, d| {
1188 assert!(!d.is_empty(), "the document came back whole");
1189 out.push(String::from_utf8_lossy(id).into_owned());
1190 })
1191 .expect("indexed");
1192 assert_eq!(n, out.len(), "the count is what the callback saw");
1193 out.sort();
1194 out
1195 }
1196
1197 #[test]
1198 fn an_index_declared_after_the_documents_finds_them() {
1199 let mut docs = Docs::new();
1200 for i in 0..64i64 {
1201 let status = if i % 4 == 0 { "shut" } else { "open" };
1202 docs.put_bytes(format!("order:{i}").as_bytes(), &order(i, status, 1))
1203 .expect("put");
1204 }
1205 docs.create_index("$.status").expect("indexed");
1206 assert_eq!(docs.index("$.status").expect("there").len(), 2);
1207 assert_eq!(docs.count("$.status", &Key::text("shut")).expect("i"), 16);
1208 assert_eq!(docs.count("$.status", &Key::text("open")).expect("i"), 48);
1209 assert_eq!(found(&docs, "$.status", &Key::text("shut")).len(), 16);
1210 assert!(found(&docs, "$.status", &Key::text("gone")).is_empty());
1211
1212 docs.put_bytes(b"order:64", &order(64, "shut", 1))
1214 .expect("put");
1215 assert_eq!(docs.count("$.status", &Key::text("shut")).expect("i"), 17);
1216 }
1217
1218 #[test]
1219 fn an_overwrite_moves_a_document_from_one_key_to_the_other() {
1220 let mut docs = Docs::new();
1221 docs.create_index("$.status").expect("indexed");
1222 docs.put_bytes(b"order:1", &order(1, "open", 1))
1223 .expect("put");
1224 assert_eq!(found(&docs, "$.status", &Key::text("open")), ["order:1"]);
1225
1226 docs.put_bytes(b"order:1", &order(1, "shut", 1))
1227 .expect("put");
1228 assert!(
1229 found(&docs, "$.status", &Key::text("open")).is_empty(),
1230 "the old key kept it"
1231 );
1232 assert_eq!(found(&docs, "$.status", &Key::text("shut")), ["order:1"]);
1233 assert_eq!(docs.index("$.status").expect("there").postings(), 1);
1234 }
1235
1236 #[test]
1237 fn a_removal_takes_a_document_out_of_every_index() {
1238 let mut docs = Docs::new();
1239 docs.create_index("$.status").expect("indexed");
1240 docs.create_index("$.customer").expect("indexed");
1241 for i in 0..8i64 {
1242 docs.put_bytes(format!("order:{i}").as_bytes(), &order(i, "open", 1))
1243 .expect("put");
1244 }
1245 assert!(docs.remove(b"order:3"));
1246 assert_eq!(found(&docs, "$.status", &Key::text("open")).len(), 7);
1247 assert_eq!(docs.count("$.customer", &Key::int(21)).expect("i"), 0);
1248 assert_eq!(docs.count("$.customer", &Key::int(28)).expect("i"), 1);
1249 for index in docs.indexes() {
1250 assert_eq!(index.postings(), 7);
1251 }
1252
1253 assert!(!docs.remove(b"order:3"), "it is already gone");
1254 assert_eq!(docs.index("$.status").expect("there").postings(), 7);
1255 }
1256
1257 #[test]
1258 fn a_path_that_names_a_container_or_nothing_is_simply_not_filed() {
1259 let mut docs = Docs::new();
1260 docs.create_index("$.lines").expect("indexed");
1261 docs.create_index("$.shipped").expect("indexed");
1262 docs.create_index("$.lines[0].qty").expect("indexed");
1263 for i in 0..4i64 {
1264 docs.put_bytes(format!("order:{i}").as_bytes(), &order(i, "open", 2))
1265 .expect("put");
1266 }
1267 assert_eq!(docs.len(), 4);
1268 assert!(
1269 docs.index("$.lines").expect("there").is_empty(),
1270 "an array has no equality key"
1271 );
1272 assert!(
1273 docs.index("$.shipped").expect("there").is_empty(),
1274 "no document has that path"
1275 );
1276 assert_eq!(
1277 docs.count("$.lines[0].qty", &Key::int(1)).expect("i"),
1278 4,
1279 "a path through an array reaches a scalar"
1280 );
1281 }
1282
1283 #[test]
1284 fn a_value_too_long_to_index_fails_the_write_and_stores_nothing() {
1285 let mut b = Builder::new();
1286 b.begin_object().expect("open");
1287 b.key(b"status").expect("key");
1288 b.text(&"x".repeat(crate::KEY_MAX)).expect("value");
1289 b.end_object().expect("close");
1290 let huge = b.finish().expect("finished").to_vec();
1291
1292 let mut docs = Docs::new();
1293 docs.create_index("$.status").expect("indexed");
1294 let err = docs.put_bytes(b"order:1", &huge).expect_err("refused");
1295 assert_eq!(err.code(), Code::Full);
1296 assert!(
1297 docs.is_empty(),
1298 "a write that cannot be indexed leaves nothing behind"
1299 );
1300
1301 assert!(docs.drop_index("$.status"));
1303 docs.put_bytes(b"order:1", &huge).expect("put");
1304 assert_eq!(docs.len(), 1);
1305 }
1306
1307 #[test]
1308 fn a_query_on_a_path_with_no_index_says_so_rather_than_scanning() {
1309 let mut docs = Docs::new();
1310 docs.put_bytes(b"order:1", &order(1, "open", 1))
1311 .expect("put");
1312 let err = docs
1313 .find("$.status", &Key::text("open"), |_, _| ())
1314 .expect_err("refused");
1315 assert_eq!(err.code(), Code::Invalid);
1316 assert_eq!(
1317 docs.count("$.status", &Key::text("open"))
1318 .expect_err("refused")
1319 .code(),
1320 Code::Invalid
1321 );
1322 assert!(docs.index("$.status").is_none());
1323 assert!(!docs.drop_index("$.status"));
1324 }
1325
1326 #[test]
1327 fn declaring_the_same_index_twice_leaves_the_first_one_alone() {
1328 let mut docs = Docs::new();
1329 docs.create_index("$.status").expect("indexed");
1330 docs.put_bytes(b"order:1", &order(1, "open", 1))
1331 .expect("put");
1332 docs.create_index("$.status").expect("indexed again");
1333 assert_eq!(docs.indexes().len(), 1);
1334 assert_eq!(
1335 docs.index("$.status").expect("there").postings(),
1336 1,
1337 "a redeclaration did not double file anything"
1338 );
1339 assert!(docs.create_index("$.[").is_err(), "the path has to parse");
1340 }
1341
1342 #[test]
1343 fn clearing_a_collection_empties_its_indexes_and_keeps_them() {
1344 let mut docs = Docs::new();
1345 docs.create_index("$.status").expect("indexed");
1346 for i in 0..8i64 {
1347 docs.put_bytes(format!("order:{i}").as_bytes(), &order(i, "open", 1))
1348 .expect("put");
1349 }
1350 docs.clear();
1351 assert!(docs.is_empty());
1352 assert!(docs.index("$.status").expect("still declared").is_empty());
1353 assert_eq!(docs.count("$.status", &Key::text("open")).expect("i"), 0);
1354
1355 docs.put_bytes(b"order:9", &order(9, "open", 1))
1356 .expect("put");
1357 assert_eq!(found(&docs, "$.status", &Key::text("open")), ["order:9"]);
1358 }
1359
1360 #[test]
1361 fn two_indexes_intersect_as_the_sets_they_are() {
1362 let mut docs = Docs::new();
1363 docs.create_index("$.status").expect("indexed");
1364 docs.create_index("$.customer").expect("indexed");
1365 for i in 0..32i64 {
1366 let status = if i % 2 == 0 { "open" } else { "shut" };
1367 docs.put_bytes(format!("order:{i}").as_bytes(), &order(i % 4, status, 1))
1368 .expect("put");
1369 }
1370
1371 let open = Key::text("open");
1375 let customer = Key::int(14);
1376 let small = docs.count("$.customer", &customer).expect("indexed");
1377 let large = docs.count("$.status", &open).expect("indexed");
1378 assert_eq!((small, large), (8, 16));
1379
1380 let small = docs.index("$.customer").expect("there").get(&customer);
1381 let large = docs.index("$.status").expect("there").get(&open);
1382 let (Some(small), Some(large)) = (small, large) else {
1383 panic!("both keys are filed");
1384 };
1385 let mut both = Vec::new();
1386 index::each_id(small, |id| {
1387 if large.contains(id) {
1388 both.push(String::from_utf8_lossy(id).into_owned());
1389 }
1390 });
1391 both.sort();
1392 assert_eq!(
1393 both,
1394 [
1395 "order:10", "order:14", "order:18", "order:2", "order:22", "order:26", "order:30",
1396 "order:6"
1397 ]
1398 );
1399 }
1400
1401 fn ranged(docs: &Docs, lo: Bound<&Key>, hi: Bound<&Key>) -> Vec<i64> {
1403 let mut out = Vec::new();
1404 let n = docs
1405 .range("$.customer", lo, hi, |_, d| {
1406 out.push(d.get(b"customer").and_then(|v| v.as_int()).expect("there"));
1407 })
1408 .expect("ordered");
1409 assert_eq!(n, out.len());
1410
1411 let mut back = Vec::new();
1412 docs.range_rev("$.customer", lo, hi, |_, d| {
1413 back.push(d.get(b"customer").and_then(|v| v.as_int()).expect("there"));
1414 })
1415 .expect("ordered");
1416 back.reverse();
1417 assert_eq!(out, back, "backwards is forwards read the other way");
1418 assert_eq!(
1419 docs.count_range("$.customer", lo, hi).expect("ordered"),
1420 out.len()
1421 );
1422 out
1423 }
1424
1425 #[test]
1426 fn an_ordered_index_answers_a_range_in_order() {
1427 let mut docs = Docs::new();
1428 docs.create_ordered_index("$.customer").expect("ordered");
1429 for i in 0..64i64 {
1431 docs.put_bytes(format!("order:{i}").as_bytes(), &order(i, "open", 1))
1432 .expect("put");
1433 }
1434
1435 assert_eq!(
1436 ranged(&docs, Bound::Unbounded, Bound::Unbounded),
1437 (0..64i64).map(|i| i * 7).collect::<Vec<i64>>()
1438 );
1439 let (lo, hi) = (Key::int(70), Key::int(105));
1440 assert_eq!(
1441 ranged(&docs, Bound::Included(&lo), Bound::Included(&hi)),
1442 [70, 77, 84, 91, 98, 105]
1443 );
1444 assert_eq!(
1445 ranged(&docs, Bound::Excluded(&lo), Bound::Excluded(&hi)),
1446 [77, 84, 91, 98]
1447 );
1448 assert_eq!(
1450 ranged(
1451 &docs,
1452 Bound::Included(&Key::int(71)),
1453 Bound::Excluded(&Key::int(90))
1454 ),
1455 [77, 84]
1456 );
1457 assert!(ranged(&docs, Bound::Included(&Key::int(442)), Bound::Unbounded).is_empty());
1458
1459 assert_eq!(docs.count("$.customer", &Key::int(70)).expect("i"), 1);
1461 assert_eq!(
1462 docs.index("$.customer").expect("there").kind(),
1463 IndexKind::Ordered
1464 );
1465 }
1466
1467 #[test]
1468 fn a_range_stays_right_through_writes_and_removals() {
1469 let mut docs = Docs::new();
1470 for i in 0..128i64 {
1471 docs.put_bytes(format!("order:{i}").as_bytes(), &order(i, "open", 1))
1472 .expect("put");
1473 }
1474 docs.create_ordered_index("$.customer").expect("ordered");
1477 assert_eq!(ranged(&docs, Bound::Unbounded, Bound::Unbounded).len(), 128);
1478
1479 for i in (0..128i64).step_by(2) {
1482 assert!(docs.remove(format!("order:{i}").as_bytes()));
1483 }
1484 assert_eq!(
1485 ranged(&docs, Bound::Unbounded, Bound::Unbounded),
1486 (0..128i64)
1487 .filter(|i| i % 2 == 1)
1488 .map(|i| i * 7)
1489 .collect::<Vec<i64>>()
1490 );
1491
1492 docs.put_bytes(b"order:1", &order(200, "open", 1))
1494 .expect("put");
1495 let after = ranged(&docs, Bound::Unbounded, Bound::Unbounded);
1496 assert_eq!(after.first(), Some(&21), "seven is gone");
1497 assert_eq!(after.last(), Some(&1400), "and it came back at the top");
1498 }
1499
1500 #[test]
1501 fn an_equality_index_refuses_a_range_rather_than_answering_nothing() {
1502 let mut docs = Docs::new();
1503 docs.create_index("$.customer").expect("indexed");
1504 docs.put_bytes(b"order:1", &order(1, "open", 1))
1505 .expect("put");
1506 let err = docs
1507 .range("$.customer", Bound::Unbounded, Bound::Unbounded, |_, _| ())
1508 .expect_err("refused");
1509 assert_eq!(err.code(), Code::Invalid);
1510 assert!(err.to_string().contains("equality"), "{err}");
1511 assert_eq!(
1512 docs.range("$.status", Bound::Unbounded, Bound::Unbounded, |_, _| ())
1513 .expect_err("refused")
1514 .code(),
1515 Code::Invalid
1516 );
1517 }
1518
1519 #[test]
1520 fn asking_for_an_order_on_an_equality_index_upgrades_it() {
1521 let mut docs = Docs::new();
1522 docs.create_index("$.customer").expect("indexed");
1523 for i in 0..8i64 {
1524 docs.put_bytes(format!("order:{i}").as_bytes(), &order(i, "open", 1))
1525 .expect("put");
1526 }
1527 assert_eq!(
1528 docs.index("$.customer").expect("there").kind(),
1529 IndexKind::Equality
1530 );
1531
1532 docs.create_ordered_index("$.customer").expect("upgraded");
1533 assert_eq!(docs.indexes().len(), 1, "it replaced rather than added");
1534 assert_eq!(ranged(&docs, Bound::Unbounded, Bound::Unbounded).len(), 8);
1535
1536 docs.create_index("$.customer").expect("already there");
1539 assert_eq!(
1540 docs.index("$.customer").expect("there").kind(),
1541 IndexKind::Ordered
1542 );
1543 assert_eq!(docs.indexes().len(), 1);
1544 }
1545
1546 fn tagged(title: &str, tags: &[&str]) -> Vec<u8> {
1548 let mut b = Builder::new();
1549 b.begin_object().expect("open");
1550 b.key(b"title").expect("key");
1551 b.text(title).expect("value");
1552 b.key(b"tags").expect("key");
1553 b.begin_array().expect("open");
1554 for tag in tags {
1555 b.text(tag).expect("value");
1556 }
1557 b.end_array().expect("close");
1558 b.end_object().expect("close");
1559 b.finish().expect("finished").to_vec()
1560 }
1561
1562 #[test]
1563 fn an_array_index_files_a_document_under_every_element() {
1564 let mut docs = Docs::new();
1565 docs.create_array_index("$.tags").expect("indexed");
1566 docs.put_bytes(b"a", &tagged("one", &["red", "blue"]))
1567 .expect("put");
1568 docs.put_bytes(b"b", &tagged("two", &["blue", "green"]))
1569 .expect("put");
1570 docs.put_bytes(b"c", &tagged("three", &[])).expect("put");
1571
1572 assert_eq!(found(&docs, "$.tags", &Key::text("red")), ["a"]);
1573 assert_eq!(found(&docs, "$.tags", &Key::text("blue")), ["a", "b"]);
1574 assert_eq!(found(&docs, "$.tags", &Key::text("green")), ["b"]);
1575 assert!(found(&docs, "$.tags", &Key::text("puce")).is_empty());
1576 assert_eq!(
1577 docs.index("$.tags").expect("there").len(),
1578 3,
1579 "three distinct tags over two documents"
1580 );
1581 }
1582
1583 #[test]
1584 fn an_array_index_takes_every_element_back_out_again() {
1585 let mut docs = Docs::new();
1586 docs.create_array_index("$.tags").expect("indexed");
1587 docs.put_bytes(b"a", &tagged("one", &["red", "blue"]))
1588 .expect("put");
1589 docs.put_bytes(b"b", &tagged("two", &["blue"]))
1590 .expect("put");
1591
1592 docs.put_bytes(b"a", &tagged("one", &["blue", "green"]))
1594 .expect("put");
1595 assert!(found(&docs, "$.tags", &Key::text("red")).is_empty());
1596 assert_eq!(found(&docs, "$.tags", &Key::text("blue")), ["a", "b"]);
1597 assert_eq!(found(&docs, "$.tags", &Key::text("green")), ["a"]);
1598
1599 assert!(docs.remove(b"a"));
1600 assert_eq!(found(&docs, "$.tags", &Key::text("blue")), ["b"]);
1601 assert!(found(&docs, "$.tags", &Key::text("green")).is_empty());
1602 assert_eq!(
1603 docs.index("$.tags").expect("there").len(),
1604 1,
1605 "a tag nobody has left is not a key any more"
1606 );
1607 }
1608
1609 #[test]
1610 fn an_array_index_treats_one_value_as_a_list_of_one() {
1611 let mut docs = Docs::new();
1612 docs.create_array_index("$.status").expect("indexed");
1613 docs.put_bytes(b"order:1", &order(1, "open", 1))
1614 .expect("put");
1615 assert_eq!(found(&docs, "$.status", &Key::text("open")), ["order:1"]);
1616 }
1617
1618 #[test]
1619 fn the_same_element_twice_is_one_posting() {
1620 let mut docs = Docs::new();
1621 docs.create_array_index("$.tags").expect("indexed");
1622 docs.put_bytes(b"a", &tagged("one", &["red", "red", "red"]))
1623 .expect("put");
1624 assert_eq!(found(&docs, "$.tags", &Key::text("red")), ["a"]);
1625 assert_eq!(docs.index("$.tags").expect("there").postings(), 1);
1626
1627 assert!(docs.remove(b"a"));
1629 assert_eq!(docs.index("$.tags").expect("there").postings(), 0);
1630 assert!(docs.index("$.tags").expect("there").is_empty());
1631 }
1632
1633 #[test]
1634 fn a_text_index_files_a_document_under_every_word() {
1635 let mut docs = Docs::new();
1636 docs.create_text_index("$.title").expect("indexed");
1637 docs.put_bytes(b"a", &tagged("A red bicycle", &[]))
1638 .expect("put");
1639 docs.put_bytes(b"b", &tagged("The red car, and a bicycle!", &[]))
1640 .expect("put");
1641
1642 assert_eq!(found(&docs, "$.title", &word("bicycle")), ["a", "b"]);
1643 assert_eq!(found(&docs, "$.title", &word("car")), ["b"]);
1644 assert_eq!(
1645 found(&docs, "$.title", &word("RED")),
1646 ["a", "b"],
1647 "a search folds case the same way the write did"
1648 );
1649 assert!(found(&docs, "$.title", &word("lorry")).is_empty());
1650 }
1651
1652 #[test]
1653 fn a_text_index_follows_the_words_through_a_rewrite() {
1654 let mut docs = Docs::new();
1655 docs.create_text_index("$.title").expect("indexed");
1656 docs.put_bytes(b"a", &tagged("a red bicycle", &[]))
1657 .expect("put");
1658 docs.put_bytes(b"a", &tagged("a blue bicycle", &[]))
1659 .expect("put");
1660 assert!(found(&docs, "$.title", &word("red")).is_empty());
1661 assert_eq!(found(&docs, "$.title", &word("blue")), ["a"]);
1662 assert_eq!(found(&docs, "$.title", &word("bicycle")), ["a"]);
1663
1664 assert!(docs.remove(b"a"));
1665 assert!(docs.index("$.title").expect("there").is_empty());
1666 }
1667
1668 #[test]
1669 fn a_text_index_declared_after_the_documents_finds_them() {
1670 let mut docs = Docs::new();
1671 for i in 0..16i64 {
1672 let title = if i % 2 == 0 {
1673 "a red one"
1674 } else {
1675 "a blue one"
1676 };
1677 docs.put_bytes(format!("t:{i}").as_bytes(), &tagged(title, &[]))
1678 .expect("put");
1679 }
1680 docs.create_text_index("$.title").expect("indexed");
1681 assert_eq!(docs.count("$.title", &word("red")).expect("i"), 8);
1682 assert_eq!(docs.count("$.title", &word("one")).expect("i"), 16);
1683 assert_eq!(
1684 docs.index("$.title").expect("there").len(),
1685 4,
1686 "a, red, blue and one"
1687 );
1688 }
1689
1690 #[test]
1691 fn changing_what_an_index_is_asked_rebuilds_it() {
1692 let mut docs = Docs::new();
1693 docs.create_index("$.tags").expect("indexed");
1694 docs.put_bytes(b"a", &tagged("one", &["red", "blue"]))
1695 .expect("put");
1696 assert!(
1697 found(&docs, "$.tags", &Key::text("red")).is_empty(),
1698 "an equality index over an array files nothing"
1699 );
1700
1701 docs.create_array_index("$.tags").expect("rebuilt");
1702 assert_eq!(docs.indexes().len(), 1, "it replaced rather than added");
1703 assert_eq!(found(&docs, "$.tags", &Key::text("red")), ["a"]);
1704
1705 docs.create_array_index("$.tags").expect("already there");
1706 assert_eq!(docs.indexes().len(), 1);
1707 }
1708
1709 fn word(w: &str) -> Key {
1711 Key::word(w).expect("one word")
1712 }
1713
1714 #[test]
1715 fn a_collection_whose_key_table_is_full_stores_the_rest_with_names() {
1716 let mut docs = Docs::new();
1719 for i in 0..crate::KEYS_MAX {
1720 let name = format!("filler{i}");
1721 assert!(docs.keys.intern(name.as_bytes()).is_some());
1722 }
1723 assert!(docs.keys().is_full());
1724
1725 docs.put_bytes(b"order:1", &order(1, "open", 1))
1726 .expect("put");
1727 let d = docs.get(b"order:1").expect("stored");
1728 assert!(!d.value().is_interned(), "there were no ids left to use");
1729 assert_eq!(d.get(b"status").and_then(|v| v.as_text()), Some("open"));
1730 assert_eq!(
1731 d.path("$.lines[0].sku")
1732 .expect("a path")
1733 .and_then(|v| v.as_text()),
1734 Some("sku-0")
1735 );
1736 }
1737}