1use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5use std::num::NonZeroU32;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
9pub struct Field(pub u32);
10
11#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
13pub enum FieldType {
14 #[serde(rename = "text")]
16 Text,
17 #[serde(rename = "u64")]
19 U64,
20 #[serde(rename = "i64")]
22 I64,
23 #[serde(rename = "f64")]
25 F64,
26 #[serde(rename = "bytes")]
28 Bytes,
29 #[serde(rename = "sparse_vector")]
31 SparseVector,
32 #[serde(rename = "dense_vector")]
34 DenseVector,
35 #[serde(rename = "json")]
37 Json,
38 #[serde(rename = "binary_dense_vector")]
40 BinaryDenseVector,
41}
42
43#[derive(Debug, Clone, Serialize, Deserialize)]
45pub struct FieldEntry {
46 pub name: String,
47 pub field_type: FieldType,
48 pub indexed: bool,
49 pub stored: bool,
50 pub tokenizer: Option<String>,
52 #[serde(default)]
54 pub multi: bool,
55 #[serde(default, skip_serializing_if = "Option::is_none")]
57 pub positions: Option<PositionMode>,
58 #[serde(default, skip_serializing_if = "Option::is_none")]
60 pub sparse_vector_config: Option<crate::structures::SparseVectorConfig>,
61 #[serde(default, skip_serializing_if = "Option::is_none")]
63 pub dense_vector_config: Option<DenseVectorConfig>,
64 #[serde(default, skip_serializing_if = "Option::is_none")]
66 pub binary_dense_vector_config: Option<BinaryDenseVectorConfig>,
67 #[serde(default)]
70 pub fast: bool,
71 #[serde(default)]
73 pub primary_key: bool,
74 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
76 pub content_hash: bool,
77 #[serde(default)]
80 pub reorder: bool,
81 #[serde(default)]
84 pub chunked: bool,
85 #[serde(default, skip_serializing_if = "Option::is_none")]
87 pub bm25_k1: Option<f32>,
88 #[serde(default, skip_serializing_if = "Option::is_none")]
90 pub bm25_b: Option<f32>,
91}
92
93impl FieldEntry {
94 pub fn tokenizer_spec(&self) -> Option<crate::tokenizer::TokenizerSpec> {
97 if self.field_type != FieldType::Text {
98 return None;
99 }
100 crate::tokenizer::TokenizerSpec::parse(self.tokenizer.as_deref()?).ok()
101 }
102
103 fn supports_reorder(&self) -> bool {
104 self.indexed
105 && (self.field_type == FieldType::Text
106 || self
107 .sparse_vector_config
108 .as_ref()
109 .is_some_and(|config| config.format == crate::structures::SparseFormat::Bmp))
110 }
111}
112
113#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
115#[serde(rename_all = "snake_case")]
116pub enum PositionMode {
117 Ordinal,
120 TokenPosition,
123 Full,
126}
127
128impl PositionMode {
129 pub fn tracks_ordinal(&self) -> bool {
131 matches!(self, PositionMode::Ordinal | PositionMode::Full)
132 }
133
134 pub fn tracks_token_position(&self) -> bool {
136 matches!(self, PositionMode::TokenPosition | PositionMode::Full)
137 }
138}
139
140#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
142#[serde(rename_all = "snake_case")]
143pub enum VectorIndexType {
144 Flat,
146 IvfPq,
151 Tq,
155 #[default]
159 IvfTq,
160 Scann,
164}
165
166pub(crate) fn reject_removed_vector_index_types(schema: &Schema) -> Result<(), String> {
170 for (_, entry) in schema.fields() {
171 if let Some(config) = &entry.sparse_vector_config
172 && config.format == crate::structures::SparseFormat::Seismic
173 {
174 config
175 .seismic
176 .validate()
177 .map_err(|error| format!("sparse field '{}': {error}", entry.name))?;
178 if let Some(query) = &config.query_config
179 && (query.seismic_cut == 0
180 || query.seismic_cut > crate::query::MAX_QUERY_TERMS
181 || !query.seismic_factor.is_finite()
182 || !(0.0..=1.0).contains(&query.seismic_factor))
183 {
184 return Err(format!(
185 "sparse field '{}': invalid Seismic query cut/factor",
186 entry.name
187 ));
188 }
189 }
190 if let Some(config) = entry.dense_vector_config.as_ref() {
191 validate_target_vectors(
192 &entry.name,
193 config.target_vectors,
194 !matches!(
195 config.index_type,
196 VectorIndexType::Flat | VectorIndexType::Tq
197 ),
198 )?;
199 if config.index_type == VectorIndexType::IvfPq {
200 return Err(format!(
201 "dense field '{}' uses index_type `ivf_pq`, which was removed; \
202 recreate the index with `ivf_tq` (trained router, training-free \
203 TurboQuant leaves) and reindex — see docs/turboquant-quantization.md",
204 entry.name,
205 ));
206 }
207 if config.index_type == VectorIndexType::Scann && config.soar.is_some() {
208 return Err(format!(
209 "dense field '{}' enables SOAR for ScaNN, but ScaNN SOAR secondary assignments are not implemented; set soar to null/off",
210 entry.name,
211 ));
212 }
213 validate_persisted_scann_options(
214 &entry.name,
215 config.index_type == VectorIndexType::Scann,
216 config.num_clusters,
217 config.tree_levels,
218 config.nprobe,
219 config.ivf_routing,
220 )?;
221 }
222 if let Some(config) = entry.binary_dense_vector_config.as_ref() {
223 validate_target_vectors(
224 &entry.name,
225 config.target_vectors,
226 config.index_type != BinaryIndexType::Flat,
227 )?;
228 if config.soar.is_some() && config.index_type != BinaryIndexType::Scann {
229 return Err(format!(
230 "binary dense field '{}' enables binary SOAR spilling, but it requires the ScaNN index",
231 entry.name,
232 ));
233 }
234 if config.index_type == BinaryIndexType::Scann && !config.dim.is_multiple_of(8) {
235 return Err(format!(
236 "binary dense field '{}' uses ScaNN with dimension {}; binary ScaNN dimensions must be a multiple of 8 bits",
237 entry.name, config.dim,
238 ));
239 }
240 validate_persisted_scann_options(
241 &entry.name,
242 config.index_type == BinaryIndexType::Scann,
243 config.num_clusters,
244 config.tree_levels,
245 config.nprobe,
246 config.ivf_routing,
247 )?;
248 }
249 }
250 Ok(())
251}
252
253fn validate_target_vectors(
254 field_name: &str,
255 target_vectors: Option<u64>,
256 topology_is_automatic: bool,
257) -> Result<(), String> {
258 if target_vectors == Some(0) {
259 return Err(format!(
260 "field '{field_name}' has target_vectors 0; expected a positive steady-state vector count"
261 ));
262 }
263 if target_vectors.is_some() && !topology_is_automatic {
264 return Err(format!(
265 "field '{field_name}' sets target_vectors for a flat/training-free index; the hint is only valid for IVF or ScaNN automatic topology"
266 ));
267 }
268 Ok(())
269}
270
271fn validate_persisted_scann_options(
272 field_name: &str,
273 is_scann: bool,
274 num_clusters: Option<usize>,
275 tree_levels: Option<u8>,
276 nprobe: usize,
277 routing: IvfRoutingMode,
278) -> Result<(), String> {
279 if !is_scann {
280 if tree_levels.is_some() {
281 return Err(format!(
282 "field '{field_name}' sets tree_levels but does not use the ScaNN index"
283 ));
284 }
285 return Ok(());
286 }
287 if routing != IvfRoutingMode::Auto {
288 return Err(format!(
289 "field '{field_name}' sets routing {routing:?} for ScaNN, but ScaNN owns its hierarchical routing; remove the routing option"
290 ));
291 }
292
293 if let Some(levels) = tree_levels
294 && !(1..=3).contains(&levels)
295 {
296 return Err(format!(
297 "field '{field_name}' has ScaNN tree_levels {levels}; expected 1..=3"
298 ));
299 }
300 if let Some(leaves) = num_clusters {
301 if !(2..=30_000_000).contains(&leaves) {
302 return Err(format!(
303 "field '{field_name}' has ScaNN num_clusters {leaves}; expected 2..=30000000"
304 ));
305 }
306 if nprobe > leaves {
307 return Err(format!(
308 "field '{field_name}' has ScaNN nprobe {nprobe} greater than num_clusters {leaves}"
309 ));
310 }
311 }
312 if nprobe == 0 {
313 return Err(format!(
314 "field '{field_name}' has ScaNN nprobe 0; expected a positive probe count"
315 ));
316 }
317 Ok(())
318}
319
320#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
326#[serde(rename_all = "snake_case")]
327pub enum IvfRoutingMode {
328 #[default]
331 Auto,
332 Flat,
334 TwoLevel,
336 Hnsw,
338}
339
340#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
346#[serde(rename_all = "snake_case")]
347pub enum DenseVectorQuantization {
348 #[default]
350 F32,
351 F16,
353 UInt8,
355 Binary,
358}
359
360impl DenseVectorQuantization {
361 pub fn element_size(self) -> usize {
364 match self {
365 Self::F32 => 4,
366 Self::F16 => 2,
367 Self::UInt8 => 1,
368 Self::Binary => panic!("element_size() not valid for Binary; use dim.div_ceil(8)"),
369 }
370 }
371
372 pub fn tag(self) -> u8 {
374 match self {
375 Self::F32 => 0,
376 Self::F16 => 1,
377 Self::UInt8 => 2,
378 Self::Binary => 3,
379 }
380 }
381
382 pub fn from_tag(tag: u8) -> Option<Self> {
384 match tag {
385 0 => Some(Self::F32),
386 1 => Some(Self::F16),
387 2 => Some(Self::UInt8),
388 3 => Some(Self::Binary),
389 _ => None,
390 }
391 }
392}
393
394#[derive(Debug, Clone, Serialize)]
404#[serde(deny_unknown_fields)]
405pub struct DenseVectorConfig {
406 pub dim: usize,
408 #[serde(default)]
411 pub index_type: VectorIndexType,
412 #[serde(default)]
414 pub quantization: DenseVectorQuantization,
415 #[serde(default, skip_serializing_if = "Option::is_none")]
419 pub num_clusters: Option<usize>,
420 #[serde(default, skip_serializing_if = "Option::is_none")]
424 pub target_vectors: Option<u64>,
425 #[serde(default, skip_serializing_if = "Option::is_none")]
428 pub tree_levels: Option<u8>,
429 #[serde(default)]
432 pub ivf_routing: IvfRoutingMode,
433 #[serde(default = "default_nprobe")]
435 pub nprobe: usize,
436 #[serde(default = "default_unit_norm")]
444 pub unit_norm: bool,
445 #[serde(default = "default_soar")]
456 pub soar: Option<crate::structures::SoarConfig>,
457}
458
459#[derive(Default)]
460enum PersistedSoar {
461 #[default]
462 Unspecified,
463 Specified(Option<crate::structures::SoarConfig>),
464}
465
466impl<'de> Deserialize<'de> for PersistedSoar {
467 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
468 Option::<crate::structures::SoarConfig>::deserialize(deserializer).map(Self::Specified)
469 }
470}
471
472#[derive(Deserialize)]
473#[serde(deny_unknown_fields)]
474struct DenseVectorConfigWire {
475 dim: usize,
476 #[serde(default)]
477 index_type: VectorIndexType,
478 #[serde(default)]
479 quantization: DenseVectorQuantization,
480 #[serde(default)]
481 num_clusters: Option<usize>,
482 #[serde(default)]
483 target_vectors: Option<u64>,
484 #[serde(default)]
485 tree_levels: Option<u8>,
486 #[serde(default)]
487 ivf_routing: IvfRoutingMode,
488 #[serde(default = "default_nprobe")]
489 nprobe: usize,
490 #[serde(default = "default_unit_norm")]
491 unit_norm: bool,
492 #[serde(default)]
493 soar: PersistedSoar,
494}
495
496impl<'de> Deserialize<'de> for DenseVectorConfig {
497 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
498 let wire = DenseVectorConfigWire::deserialize(deserializer)?;
499 let soar = match wire.soar {
500 PersistedSoar::Specified(soar) => soar,
501 PersistedSoar::Unspecified if wire.index_type == VectorIndexType::IvfTq => {
502 default_soar()
503 }
504 PersistedSoar::Unspecified => None,
505 };
506 Ok(Self {
507 dim: wire.dim,
508 index_type: wire.index_type,
509 quantization: wire.quantization,
510 num_clusters: wire.num_clusters,
511 target_vectors: wire.target_vectors,
512 tree_levels: wire.tree_levels,
513 ivf_routing: wire.ivf_routing,
514 nprobe: wire.nprobe,
515 unit_norm: wire.unit_norm,
516 soar,
517 })
518 }
519}
520
521fn default_nprobe() -> usize {
522 64
523}
524
525fn default_unit_norm() -> bool {
526 true
527}
528
529fn default_soar() -> Option<crate::structures::SoarConfig> {
530 Some(crate::structures::SoarConfig::default())
531}
532
533impl DenseVectorConfig {
534 pub fn new(dim: usize) -> Self {
535 Self {
536 dim,
537 index_type: VectorIndexType::IvfTq,
538 quantization: DenseVectorQuantization::F32,
539 num_clusters: None,
540 target_vectors: None,
541 tree_levels: None,
542 ivf_routing: IvfRoutingMode::Auto,
543 nprobe: 64,
544 unit_norm: true,
545 soar: Some(crate::structures::SoarConfig::default()),
546 }
547 }
548
549 pub fn flat(dim: usize) -> Self {
551 Self {
552 dim,
553 index_type: VectorIndexType::Flat,
554 quantization: DenseVectorQuantization::F32,
555 num_clusters: None,
556 target_vectors: None,
557 tree_levels: None,
558 ivf_routing: IvfRoutingMode::Auto,
559 nprobe: 0,
560 unit_norm: true,
561 soar: None,
562 }
563 }
564
565 pub fn tq(dim: usize) -> Self {
567 Self {
568 dim,
569 index_type: VectorIndexType::Tq,
570 quantization: DenseVectorQuantization::F32,
571 num_clusters: None,
572 target_vectors: None,
573 tree_levels: None,
574 ivf_routing: IvfRoutingMode::Flat,
575 nprobe: 0,
576 unit_norm: true,
577 soar: None,
578 }
579 }
580
581 pub fn ivf_tq(dim: usize, num_clusters: Option<usize>, nprobe: usize) -> Self {
583 Self {
584 dim,
585 index_type: VectorIndexType::IvfTq,
586 quantization: DenseVectorQuantization::F32,
587 num_clusters,
588 target_vectors: None,
589 tree_levels: None,
590 ivf_routing: IvfRoutingMode::Auto,
591 nprobe,
592 unit_norm: true,
593 soar: Some(crate::structures::SoarConfig::default()),
594 }
595 }
596
597 pub fn with_quantization(mut self, quantization: DenseVectorQuantization) -> Self {
599 self.quantization = quantization;
600 self
601 }
602
603 pub fn with_unit_norm(mut self) -> Self {
605 self.unit_norm = true;
606 self
607 }
608
609 pub fn with_num_clusters(mut self, num_clusters: usize) -> Self {
611 self.num_clusters = Some(num_clusters);
612 self
613 }
614
615 pub fn with_target_vectors(mut self, target_vectors: u64) -> Self {
617 self.target_vectors = Some(target_vectors);
618 self
619 }
620
621 pub fn with_ivf_routing(mut self, routing: IvfRoutingMode) -> Self {
623 self.ivf_routing = routing;
624 self
625 }
626 pub fn with_soar(mut self, soar: crate::structures::SoarConfig) -> Self {
628 self.soar = Some(soar);
629 self
630 }
631
632 pub fn without_soar(mut self) -> Self {
634 self.soar = None;
635 self
636 }
637
638 pub fn uses_ivf(&self) -> bool {
640 self.index_type == VectorIndexType::IvfTq
641 }
642
643 pub fn supports_soar(&self) -> bool {
645 self.index_type == VectorIndexType::IvfTq
646 }
647
648 pub fn is_flat(&self) -> bool {
650 self.index_type == VectorIndexType::Flat
651 }
652
653 pub fn optimal_num_clusters(&self, num_vectors: usize) -> usize {
655 self.num_clusters.unwrap_or_else(|| {
656 let num_vectors = self.target_vectors.map_or(num_vectors, |target| {
657 usize::try_from(target)
658 .unwrap_or(usize::MAX)
659 .max(num_vectors)
660 });
661 let optimal = 8.0 * (num_vectors as f64).sqrt();
665 (optimal as usize).clamp(16, 1_048_576)
666 })
667 }
668}
669
670#[derive(Debug, Clone, Serialize, Deserialize)]
676#[serde(deny_unknown_fields)]
677pub struct BinaryDenseVectorConfig {
678 pub dim: usize,
680 #[serde(default)]
684 pub index_type: BinaryIndexType,
685 #[serde(default, skip_serializing_if = "Option::is_none")]
687 pub num_clusters: Option<usize>,
688 #[serde(default, skip_serializing_if = "Option::is_none")]
692 pub target_vectors: Option<u64>,
693 #[serde(default, skip_serializing_if = "Option::is_none")]
696 pub tree_levels: Option<u8>,
697 #[serde(default)]
700 pub ivf_routing: IvfRoutingMode,
701 #[serde(default = "default_nprobe")]
703 pub nprobe: usize,
704 #[serde(default, skip_serializing_if = "Option::is_none")]
708 pub soar: Option<crate::structures::SoarConfig>,
709}
710
711#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
713#[serde(rename_all = "snake_case")]
714pub enum BinaryIndexType {
715 Flat,
717 #[default]
719 Ivf,
720 Scann,
722}
723
724#[derive(Debug, Clone)]
728pub enum VectorIndexAlter {
729 Dense(DenseVectorConfig),
730 Binary(BinaryDenseVectorConfig),
731}
732
733impl BinaryDenseVectorConfig {
734 pub fn new(dim: usize) -> Self {
735 assert!(
736 dim.is_multiple_of(8),
737 "BinaryDenseVector dimension must be a multiple of 8, got {dim}"
738 );
739 Self {
740 dim,
741 index_type: BinaryIndexType::Ivf,
742 num_clusters: None,
743 target_vectors: None,
744 tree_levels: None,
745 ivf_routing: IvfRoutingMode::Auto,
746 nprobe: 64,
747 soar: None,
748 }
749 }
750
751 pub fn with_ivf(mut self, num_clusters: Option<usize>, nprobe: usize) -> Self {
753 self.index_type = BinaryIndexType::Ivf;
754 self.num_clusters = num_clusters;
755 self.nprobe = nprobe;
756 self
757 }
758
759 pub fn with_target_vectors(mut self, target_vectors: u64) -> Self {
761 self.target_vectors = Some(target_vectors);
762 self
763 }
764
765 pub fn with_ivf_routing(mut self, routing: IvfRoutingMode) -> Self {
767 self.ivf_routing = routing;
768 self
769 }
770
771 pub fn with_soar(mut self, soar: crate::structures::SoarConfig) -> Self {
773 self.soar = Some(soar);
774 self
775 }
776
777 pub fn without_soar(mut self) -> Self {
779 self.soar = None;
780 self
781 }
782
783 pub fn optimal_num_clusters(&self, num_vectors: usize) -> usize {
785 self.num_clusters.unwrap_or_else(|| {
786 let num_vectors = self.target_vectors.map_or(num_vectors, |target| {
787 usize::try_from(target)
788 .unwrap_or(usize::MAX)
789 .max(num_vectors)
790 });
791 let balanced = (num_vectors as f64).sqrt().ceil() as usize;
795 balanced.clamp(16, 1_048_576)
796 })
797 }
798
799 pub fn byte_len(&self) -> usize {
801 self.dim.div_ceil(8)
802 }
803}
804
805use super::query_field_router::QueryRouterRule;
806
807#[derive(Debug, Clone, Default, Serialize, Deserialize)]
809pub struct Schema {
810 #[serde(skip)]
811 content_hash_field: std::sync::OnceLock<Option<Field>>,
812 fields: Vec<FieldEntry>,
813 name_to_field: HashMap<String, Field>,
814 #[serde(default)]
816 default_fields: Vec<Field>,
817 #[serde(default)]
819 query_routers: Vec<QueryRouterRule>,
820 #[serde(default)]
825 reorder_on_merge: bool,
826 #[serde(default, skip_serializing_if = "Option::is_none")]
830 max_l1_phrase_terms: Option<NonZeroU32>,
831 #[serde(default)]
835 index_name: String,
836}
837
838impl Schema {
839 pub fn builder() -> SchemaBuilder {
840 SchemaBuilder::default()
841 }
842
843 pub fn get_field(&self, name: &str) -> Option<Field> {
844 self.name_to_field.get(name).copied()
845 }
846
847 pub fn get_field_entry(&self, field: Field) -> Option<&FieldEntry> {
848 self.fields.get(field.0 as usize)
849 }
850
851 pub fn tokenizer_hint_field(&self, field: Field) -> Option<Field> {
854 let spec = self.get_field_entry(field)?.tokenizer_spec()?;
855 self.get_field(spec.hint_field()?)
856 }
857
858 pub fn with_vector_index_alter(
861 &self,
862 field: Field,
863 alter: VectorIndexAlter,
864 ) -> Result<Self, String> {
865 let mut next = self.clone();
866 let entry = next
867 .fields
868 .get_mut(field.0 as usize)
869 .ok_or_else(|| format!("vector ALTER references unknown field {}", field.0))?;
870 match alter {
871 VectorIndexAlter::Dense(config) => {
872 let current = entry
873 .dense_vector_config
874 .as_ref()
875 .ok_or_else(|| format!("field '{}' is not a dense vector field", entry.name))?;
876 if config.dim != current.dim || config.quantization != current.quantization {
877 return Err(format!(
878 "field '{}' ALTER cannot change dimension or storage quantization",
879 entry.name
880 ));
881 }
882 if matches!(
883 config.index_type,
884 VectorIndexType::Flat | VectorIndexType::Tq
885 ) {
886 return Err(format!(
887 "field '{}' ALTER target must be `ivf_tq` or `scann`",
888 entry.name
889 ));
890 }
891 entry.dense_vector_config = Some(config);
892 }
893 VectorIndexAlter::Binary(config) => {
894 let current = entry.binary_dense_vector_config.as_ref().ok_or_else(|| {
895 format!("field '{}' is not a binary dense vector field", entry.name)
896 })?;
897 if config.dim != current.dim {
898 return Err(format!(
899 "field '{}' ALTER cannot change binary dimension",
900 entry.name
901 ));
902 }
903 if config.index_type == BinaryIndexType::Flat {
904 return Err(format!(
905 "field '{}' ALTER target must be `ivf` or `scann`",
906 entry.name
907 ));
908 }
909 entry.binary_dense_vector_config = Some(config);
910 }
911 }
912 reject_removed_vector_index_types(&next)?;
913 Ok(next)
914 }
915
916 pub fn get_field_name(&self, field: Field) -> Option<&str> {
917 self.fields.get(field.0 as usize).map(|e| e.name.as_str())
918 }
919
920 pub fn fields(&self) -> impl Iterator<Item = (Field, &FieldEntry)> {
921 self.fields
922 .iter()
923 .enumerate()
924 .map(|(i, e)| (Field(i as u32), e))
925 }
926
927 pub fn num_fields(&self) -> usize {
928 self.fields.len()
929 }
930
931 pub fn has_reorder_fields(&self) -> bool {
933 self.fields
934 .iter()
935 .any(|entry| entry.reorder && entry.supports_reorder())
936 }
937
938 pub fn has_background_maintenance_fields(&self) -> bool {
940 self.fields.iter().any(|entry| {
941 (entry.reorder && entry.supports_reorder())
942 || (entry.indexed
943 && (entry
944 .binary_dense_vector_config
945 .as_ref()
946 .is_some_and(|config| {
947 matches!(
948 config.index_type,
949 BinaryIndexType::Ivf | BinaryIndexType::Scann
950 )
951 })
952 || entry.sparse_vector_config.as_ref().is_some_and(|config| {
953 config.format == crate::structures::SparseFormat::Seismic
954 })))
955 })
956 }
957
958 pub fn reorder_on_merge(&self) -> bool {
961 self.reorder_on_merge
962 }
963
964 pub fn max_l1_phrase_terms(&self) -> usize {
967 self.max_l1_phrase_terms
968 .map_or(64, |limit| limit.get() as usize)
969 }
970
971 pub fn index_label(&self) -> &str {
974 if self.index_name.is_empty() {
975 "unknown"
976 } else {
977 &self.index_name
978 }
979 }
980
981 pub fn set_index_name(&mut self, name: impl Into<String>) {
983 self.index_name = name.into();
984 }
985
986 pub fn default_fields(&self) -> &[Field] {
988 &self.default_fields
989 }
990
991 pub fn set_default_fields(&mut self, fields: Vec<Field>) {
993 self.default_fields = fields;
994 }
995
996 pub fn query_routers(&self) -> &[QueryRouterRule] {
998 &self.query_routers
999 }
1000
1001 pub fn set_query_routers(&mut self, rules: Vec<QueryRouterRule>) {
1003 self.query_routers = rules;
1004 }
1005
1006 pub fn content_hash_field(&self) -> Option<Field> {
1008 *self.content_hash_field.get_or_init(|| {
1009 self.fields
1010 .iter()
1011 .position(|entry| entry.content_hash)
1012 .map(|id| Field(id as u32))
1013 })
1014 }
1015
1016 pub(crate) fn validate(&self) -> crate::Result<()> {
1018 for entry in &self.fields {
1019 if entry.reorder && !entry.supports_reorder() {
1020 return Err(crate::Error::Schema(format!(
1021 "field '{}' uses reorder, which requires indexed text or BMP; Seismic and binary ANN maintenance is automatic",
1022 entry.name
1023 )));
1024 }
1025 }
1026 self.validate_content_hash()
1027 }
1028
1029 fn validate_content_hash(&self) -> crate::Result<()> {
1030 let hashes: Vec<_> = self
1031 .fields
1032 .iter()
1033 .filter(|entry| entry.content_hash)
1034 .collect();
1035 if hashes.is_empty() {
1036 return Ok(());
1037 }
1038 if hashes.len() != 1 || self.fields.iter().filter(|entry| entry.primary_key).count() != 1 {
1039 return Err(crate::Error::Schema(
1040 "content_hash requires exactly one hash field and one primary key".into(),
1041 ));
1042 }
1043 let primary = self.get_field_entry(self.primary_field().unwrap()).unwrap();
1044 if primary.field_type != FieldType::Text
1045 || primary.multi
1046 || !primary.fast
1047 || !primary.indexed
1048 {
1049 return Err(crate::Error::Schema("content_hash requires a single-valued text primary key with fast and indexed enabled".into()));
1050 }
1051 let entry = hashes[0];
1052 if !entry.stored
1053 || entry.multi
1054 || !matches!(
1055 entry.field_type,
1056 FieldType::Text | FieldType::Bytes | FieldType::U64
1057 )
1058 {
1059 return Err(crate::Error::Schema(
1060 "content_hash must be a stored, single-valued text, bytes, or u64 field".into(),
1061 ));
1062 }
1063 Ok(())
1064 }
1065
1066 pub fn primary_field(&self) -> Option<Field> {
1068 self.fields
1069 .iter()
1070 .enumerate()
1071 .find(|(_, e)| e.primary_key)
1072 .map(|(i, _)| Field(i as u32))
1073 }
1074}
1075
1076#[derive(Debug, Default)]
1078pub struct SchemaBuilder {
1079 fields: Vec<FieldEntry>,
1080 default_fields: Vec<String>,
1081 query_routers: Vec<QueryRouterRule>,
1082 reorder_on_merge: bool,
1083 max_l1_phrase_terms: Option<NonZeroU32>,
1084 index_name: String,
1085}
1086
1087impl SchemaBuilder {
1088 pub fn add_text_field(&mut self, name: &str, indexed: bool, stored: bool) -> Field {
1089 self.add_field_with_tokenizer(
1090 name,
1091 FieldType::Text,
1092 indexed,
1093 stored,
1094 Some("simple".to_string()),
1095 )
1096 }
1097
1098 pub fn add_text_field_with_tokenizer(
1099 &mut self,
1100 name: &str,
1101 indexed: bool,
1102 stored: bool,
1103 tokenizer: &str,
1104 ) -> Field {
1105 self.add_field_with_tokenizer(
1106 name,
1107 FieldType::Text,
1108 indexed,
1109 stored,
1110 Some(tokenizer.to_string()),
1111 )
1112 }
1113
1114 pub fn add_u64_field(&mut self, name: &str, indexed: bool, stored: bool) -> Field {
1115 self.add_field(name, FieldType::U64, indexed, stored)
1116 }
1117
1118 pub fn add_i64_field(&mut self, name: &str, indexed: bool, stored: bool) -> Field {
1119 self.add_field(name, FieldType::I64, indexed, stored)
1120 }
1121
1122 pub fn add_f64_field(&mut self, name: &str, indexed: bool, stored: bool) -> Field {
1123 self.add_field(name, FieldType::F64, indexed, stored)
1124 }
1125
1126 pub fn add_bytes_field(&mut self, name: &str, stored: bool) -> Field {
1127 self.add_field(name, FieldType::Bytes, false, stored)
1128 }
1129
1130 pub fn add_json_field(&mut self, name: &str, stored: bool) -> Field {
1135 self.add_field(name, FieldType::Json, false, stored)
1136 }
1137
1138 pub fn add_sparse_vector_field(&mut self, name: &str, indexed: bool, stored: bool) -> Field {
1143 self.add_sparse_vector_field_with_config(
1144 name,
1145 indexed,
1146 stored,
1147 crate::structures::SparseVectorConfig::default(),
1148 )
1149 }
1150
1151 pub fn add_sparse_vector_field_with_config(
1156 &mut self,
1157 name: &str,
1158 indexed: bool,
1159 stored: bool,
1160 config: crate::structures::SparseVectorConfig,
1161 ) -> Field {
1162 let field = Field(self.fields.len() as u32);
1163 self.fields.push(FieldEntry {
1164 name: name.to_string(),
1165 field_type: FieldType::SparseVector,
1166 indexed,
1167 stored,
1168 tokenizer: None,
1169 multi: false,
1170 positions: None,
1171 sparse_vector_config: Some(config),
1172 dense_vector_config: None,
1173 binary_dense_vector_config: None,
1174 fast: false,
1175 primary_key: false,
1176 content_hash: false,
1177 reorder: false,
1178 chunked: false,
1179 bm25_k1: None,
1180 bm25_b: None,
1181 });
1182 field
1183 }
1184
1185 pub fn set_sparse_vector_config(
1187 &mut self,
1188 field: Field,
1189 config: crate::structures::SparseVectorConfig,
1190 ) {
1191 if let Some(entry) = self.fields.get_mut(field.0 as usize) {
1192 entry.sparse_vector_config = Some(config);
1193 }
1194 }
1195
1196 pub fn add_dense_vector_field(
1201 &mut self,
1202 name: &str,
1203 dim: usize,
1204 indexed: bool,
1205 stored: bool,
1206 ) -> Field {
1207 self.add_dense_vector_field_with_config(name, indexed, stored, DenseVectorConfig::new(dim))
1208 }
1209
1210 pub fn add_dense_vector_field_with_config(
1212 &mut self,
1213 name: &str,
1214 indexed: bool,
1215 stored: bool,
1216 config: DenseVectorConfig,
1217 ) -> Field {
1218 let field = Field(self.fields.len() as u32);
1219 self.fields.push(FieldEntry {
1220 name: name.to_string(),
1221 field_type: FieldType::DenseVector,
1222 indexed,
1223 stored,
1224 tokenizer: None,
1225 multi: false,
1226 positions: None,
1227 sparse_vector_config: None,
1228 dense_vector_config: Some(config),
1229 binary_dense_vector_config: None,
1230 fast: false,
1231 primary_key: false,
1232 content_hash: false,
1233 reorder: false,
1234 chunked: false,
1235 bm25_k1: None,
1236 bm25_b: None,
1237 });
1238 field
1239 }
1240
1241 pub fn add_binary_dense_vector_field(
1247 &mut self,
1248 name: &str,
1249 dim: usize,
1250 indexed: bool,
1251 stored: bool,
1252 ) -> Field {
1253 self.add_binary_dense_vector_field_with_config(
1254 name,
1255 indexed,
1256 stored,
1257 BinaryDenseVectorConfig::new(dim),
1258 )
1259 }
1260
1261 pub fn add_binary_dense_vector_field_with_config(
1263 &mut self,
1264 name: &str,
1265 indexed: bool,
1266 stored: bool,
1267 config: BinaryDenseVectorConfig,
1268 ) -> Field {
1269 let field = Field(self.fields.len() as u32);
1270 self.fields.push(FieldEntry {
1271 name: name.to_string(),
1272 field_type: FieldType::BinaryDenseVector,
1273 indexed,
1274 stored,
1275 tokenizer: None,
1276 multi: false,
1277 positions: None,
1278 sparse_vector_config: None,
1279 dense_vector_config: None,
1280 binary_dense_vector_config: Some(config),
1281 fast: false,
1282 primary_key: false,
1283 content_hash: false,
1284 reorder: false,
1285 chunked: false,
1286 bm25_k1: None,
1287 bm25_b: None,
1288 });
1289 field
1290 }
1291
1292 fn add_field(
1293 &mut self,
1294 name: &str,
1295 field_type: FieldType,
1296 indexed: bool,
1297 stored: bool,
1298 ) -> Field {
1299 self.add_field_with_tokenizer(name, field_type, indexed, stored, None)
1300 }
1301
1302 fn add_field_with_tokenizer(
1303 &mut self,
1304 name: &str,
1305 field_type: FieldType,
1306 indexed: bool,
1307 stored: bool,
1308 tokenizer: Option<String>,
1309 ) -> Field {
1310 self.add_field_full(name, field_type, indexed, stored, tokenizer, false)
1311 }
1312
1313 fn add_field_full(
1314 &mut self,
1315 name: &str,
1316 field_type: FieldType,
1317 indexed: bool,
1318 stored: bool,
1319 tokenizer: Option<String>,
1320 multi: bool,
1321 ) -> Field {
1322 let field = Field(self.fields.len() as u32);
1323 self.fields.push(FieldEntry {
1324 name: name.to_string(),
1325 field_type,
1326 indexed,
1327 stored,
1328 tokenizer,
1329 multi,
1330 positions: None,
1331 sparse_vector_config: None,
1332 dense_vector_config: None,
1333 binary_dense_vector_config: None,
1334 fast: false,
1335 primary_key: false,
1336 content_hash: false,
1337 reorder: false,
1338 chunked: false,
1339 bm25_k1: None,
1340 bm25_b: None,
1341 });
1342 field
1343 }
1344
1345 pub fn set_multi(&mut self, field: Field, multi: bool) {
1347 if let Some(entry) = self.fields.get_mut(field.0 as usize) {
1348 entry.multi = multi;
1349 }
1350 }
1351
1352 pub fn set_fast(&mut self, field: Field, fast: bool) {
1355 if let Some(entry) = self.fields.get_mut(field.0 as usize) {
1356 entry.fast = fast;
1357 }
1358 }
1359
1360 pub fn set_primary_key(&mut self, field: Field) {
1366 if let Some(entry) = self.fields.get_mut(field.0 as usize) {
1367 entry.primary_key = true;
1368 entry.fast = true;
1369 entry.indexed = true;
1370 }
1371 }
1372
1373 pub fn set_content_hash(&mut self, field: Field) {
1376 self.fields
1377 .get_mut(field.0 as usize)
1378 .expect("unknown content hash field")
1379 .content_hash = true;
1380 }
1381
1382 pub fn set_reorder(&mut self, field: Field, reorder: bool) {
1385 if let Some(entry) = self.fields.get_mut(field.0 as usize) {
1386 entry.reorder = reorder;
1387 }
1388 }
1389
1390 pub fn set_bm25_params(&mut self, field: Field, k1: Option<f32>, b: Option<f32>) {
1392 if let Some(entry) = self.fields.get_mut(field.0 as usize) {
1393 entry.bm25_k1 = k1;
1394 entry.bm25_b = b;
1395 }
1396 }
1397
1398 pub fn set_chunked(&mut self, field: Field, chunked: bool) {
1401 if let Some(entry) = self.fields.get_mut(field.0 as usize) {
1402 entry.chunked = chunked;
1403 if chunked {
1404 entry.multi = true;
1405 }
1406 }
1407 }
1408
1409 pub fn set_reorder_on_merge(&mut self, on: bool) {
1412 self.reorder_on_merge = on;
1413 }
1414
1415 pub fn set_max_l1_phrase_terms(&mut self, limit: NonZeroU32) {
1419 self.max_l1_phrase_terms = Some(limit);
1420 }
1421
1422 pub fn set_index_name(&mut self, name: impl Into<String>) {
1424 self.index_name = name.into();
1425 }
1426
1427 pub fn set_positions(&mut self, field: Field, mode: PositionMode) {
1429 if let Some(entry) = self.fields.get_mut(field.0 as usize) {
1430 entry.positions = Some(mode);
1431 }
1432 }
1433
1434 pub fn set_default_fields(&mut self, field_names: Vec<String>) {
1436 self.default_fields = field_names;
1437 }
1438
1439 pub fn set_query_routers(&mut self, rules: Vec<QueryRouterRule>) {
1441 self.query_routers = rules;
1442 }
1443
1444 pub fn build(self) -> Schema {
1445 let mut name_to_field = HashMap::new();
1446 for (i, entry) in self.fields.iter().enumerate() {
1447 name_to_field.insert(entry.name.clone(), Field(i as u32));
1448 }
1449
1450 let default_fields: Vec<Field> = self
1452 .default_fields
1453 .iter()
1454 .filter_map(|name| name_to_field.get(name).copied())
1455 .collect();
1456
1457 Schema {
1458 content_hash_field: Default::default(),
1459 fields: self.fields,
1460 name_to_field,
1461 default_fields,
1462 query_routers: self.query_routers,
1463 reorder_on_merge: self.reorder_on_merge,
1464 max_l1_phrase_terms: self.max_l1_phrase_terms,
1465 index_name: self.index_name,
1466 }
1467 }
1468}
1469
1470#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1472pub enum FieldValue {
1473 #[serde(rename = "text")]
1474 Text(String),
1475 #[serde(rename = "u64")]
1476 U64(u64),
1477 #[serde(rename = "i64")]
1478 I64(i64),
1479 #[serde(rename = "f64")]
1480 F64(f64),
1481 #[serde(rename = "bytes")]
1482 Bytes(Vec<u8>),
1483 #[serde(rename = "sparse_vector")]
1485 SparseVector(Vec<(u32, f32)>),
1486 #[serde(rename = "dense_vector")]
1488 DenseVector(Vec<f32>),
1489 #[serde(rename = "json")]
1491 Json(serde_json::Value),
1492 #[serde(rename = "binary_dense_vector")]
1494 BinaryDenseVector(Vec<u8>),
1495}
1496
1497impl FieldValue {
1498 pub fn as_text(&self) -> Option<&str> {
1499 match self {
1500 FieldValue::Text(s) => Some(s),
1501 _ => None,
1502 }
1503 }
1504
1505 pub fn as_u64(&self) -> Option<u64> {
1506 match self {
1507 FieldValue::U64(v) => Some(*v),
1508 _ => None,
1509 }
1510 }
1511
1512 pub fn as_i64(&self) -> Option<i64> {
1513 match self {
1514 FieldValue::I64(v) => Some(*v),
1515 _ => None,
1516 }
1517 }
1518
1519 pub fn as_f64(&self) -> Option<f64> {
1520 match self {
1521 FieldValue::F64(v) => Some(*v),
1522 _ => None,
1523 }
1524 }
1525
1526 pub fn as_bytes(&self) -> Option<&[u8]> {
1527 match self {
1528 FieldValue::Bytes(b) => Some(b),
1529 _ => None,
1530 }
1531 }
1532
1533 pub fn as_sparse_vector(&self) -> Option<&[(u32, f32)]> {
1534 match self {
1535 FieldValue::SparseVector(entries) => Some(entries),
1536 _ => None,
1537 }
1538 }
1539
1540 pub fn as_dense_vector(&self) -> Option<&[f32]> {
1541 match self {
1542 FieldValue::DenseVector(v) => Some(v),
1543 _ => None,
1544 }
1545 }
1546
1547 pub fn as_json(&self) -> Option<&serde_json::Value> {
1548 match self {
1549 FieldValue::Json(v) => Some(v),
1550 _ => None,
1551 }
1552 }
1553
1554 pub fn as_binary_dense_vector(&self) -> Option<&[u8]> {
1555 match self {
1556 FieldValue::BinaryDenseVector(v) => Some(v),
1557 _ => None,
1558 }
1559 }
1560}
1561
1562#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1564pub struct Document {
1565 field_values: Vec<(Field, FieldValue)>,
1566}
1567
1568impl Document {
1569 pub fn new() -> Self {
1570 Self::default()
1571 }
1572
1573 pub fn add_text(&mut self, field: Field, value: impl Into<String>) {
1574 self.field_values
1575 .push((field, FieldValue::Text(value.into())));
1576 }
1577
1578 pub fn add_u64(&mut self, field: Field, value: u64) {
1579 self.field_values.push((field, FieldValue::U64(value)));
1580 }
1581
1582 pub fn add_i64(&mut self, field: Field, value: i64) {
1583 self.field_values.push((field, FieldValue::I64(value)));
1584 }
1585
1586 pub fn add_f64(&mut self, field: Field, value: f64) {
1587 self.field_values.push((field, FieldValue::F64(value)));
1588 }
1589
1590 pub fn add_bytes(&mut self, field: Field, value: Vec<u8>) {
1591 self.field_values.push((field, FieldValue::Bytes(value)));
1592 }
1593
1594 pub fn add_sparse_vector(&mut self, field: Field, entries: Vec<(u32, f32)>) {
1595 self.field_values
1596 .push((field, FieldValue::SparseVector(entries)));
1597 }
1598
1599 pub fn add_dense_vector(&mut self, field: Field, values: Vec<f32>) {
1600 self.field_values
1601 .push((field, FieldValue::DenseVector(values)));
1602 }
1603
1604 pub fn add_json(&mut self, field: Field, value: serde_json::Value) {
1605 self.field_values.push((field, FieldValue::Json(value)));
1606 }
1607
1608 pub fn add_binary_dense_vector(&mut self, field: Field, values: Vec<u8>) {
1609 self.field_values
1610 .push((field, FieldValue::BinaryDenseVector(values)));
1611 }
1612
1613 pub fn get_first(&self, field: Field) -> Option<&FieldValue> {
1614 self.field_values
1615 .iter()
1616 .find(|(f, _)| *f == field)
1617 .map(|(_, v)| v)
1618 }
1619
1620 pub fn get_all(&self, field: Field) -> impl Iterator<Item = &FieldValue> {
1621 self.field_values
1622 .iter()
1623 .filter(move |(f, _)| *f == field)
1624 .map(|(_, v)| v)
1625 }
1626
1627 pub fn field_values(&self) -> &[(Field, FieldValue)] {
1628 &self.field_values
1629 }
1630
1631 pub fn filter_stored(&self, schema: &Schema) -> Document {
1633 Document {
1634 field_values: self
1635 .field_values
1636 .iter()
1637 .filter(|(field, _)| {
1638 schema
1639 .get_field_entry(*field)
1640 .is_some_and(|entry| entry.stored)
1641 })
1642 .cloned()
1643 .collect(),
1644 }
1645 }
1646
1647 pub fn to_json(&self, schema: &Schema) -> serde_json::Value {
1653 use std::collections::HashMap;
1654
1655 let mut field_values_map: HashMap<Field, (String, bool, Vec<serde_json::Value>)> =
1657 HashMap::new();
1658
1659 for (field, value) in &self.field_values {
1660 if let Some(entry) = schema.get_field_entry(*field) {
1661 let json_value = match value {
1662 FieldValue::Text(s) => serde_json::Value::String(s.clone()),
1663 FieldValue::U64(n) => serde_json::Value::Number((*n).into()),
1664 FieldValue::I64(n) => serde_json::Value::Number((*n).into()),
1665 FieldValue::F64(n) => serde_json::json!(n),
1666 FieldValue::Bytes(b) => {
1667 use base64::Engine;
1668 serde_json::Value::String(
1669 base64::engine::general_purpose::STANDARD.encode(b),
1670 )
1671 }
1672 FieldValue::SparseVector(entries) => {
1673 let indices: Vec<u32> = entries.iter().map(|(i, _)| *i).collect();
1674 let values: Vec<f32> = entries.iter().map(|(_, v)| *v).collect();
1675 serde_json::json!({
1676 "indices": indices,
1677 "values": values
1678 })
1679 }
1680 FieldValue::DenseVector(values) => {
1681 serde_json::json!(values)
1682 }
1683 FieldValue::Json(v) => v.clone(),
1684 FieldValue::BinaryDenseVector(b) => {
1685 use base64::Engine;
1686 serde_json::Value::String(
1687 base64::engine::general_purpose::STANDARD.encode(b),
1688 )
1689 }
1690 };
1691 field_values_map
1692 .entry(*field)
1693 .or_insert_with(|| (entry.name.clone(), entry.multi, Vec::new()))
1694 .2
1695 .push(json_value);
1696 }
1697 }
1698
1699 let mut map = serde_json::Map::new();
1701 for (_field, (name, is_multi, values)) in field_values_map {
1702 let json_value = if is_multi || values.len() > 1 {
1703 serde_json::Value::Array(values)
1704 } else {
1705 values.into_iter().next().unwrap()
1706 };
1707 map.insert(name, json_value);
1708 }
1709
1710 serde_json::Value::Object(map)
1711 }
1712
1713 pub fn from_json(json: &serde_json::Value, schema: &Schema) -> Option<Self> {
1722 let obj = json.as_object()?;
1723 let mut doc = Document::new();
1724
1725 for (key, value) in obj {
1726 if let Some(field) = schema.get_field(key) {
1727 let field_entry = schema.get_field_entry(field)?;
1728 if field_entry.content_hash {
1731 match (&field_entry.field_type, value) {
1732 (FieldType::Text, serde_json::Value::String(text)) => {
1733 doc.add_text(field, text)
1734 }
1735 (FieldType::U64, serde_json::Value::Number(number)) => {
1736 doc.add_u64(field, number.as_u64()?)
1737 }
1738 (FieldType::Bytes, serde_json::Value::String(encoded)) => {
1739 use base64::Engine;
1740 doc.add_bytes(
1741 field,
1742 base64::engine::general_purpose::STANDARD
1743 .decode(encoded)
1744 .ok()?,
1745 );
1746 }
1747 _ => return None,
1748 }
1749 continue;
1750 }
1751 Self::add_json_value(&mut doc, field, &field_entry.field_type, value);
1752 }
1753 }
1754
1755 Some(doc)
1756 }
1757
1758 fn add_json_value(
1760 doc: &mut Document,
1761 field: Field,
1762 field_type: &FieldType,
1763 value: &serde_json::Value,
1764 ) {
1765 match value {
1766 serde_json::Value::String(s) => {
1767 if matches!(field_type, FieldType::Text) {
1768 doc.add_text(field, s.clone());
1769 }
1770 }
1771 serde_json::Value::Number(n) => {
1772 match field_type {
1773 FieldType::I64 => {
1774 if let Some(i) = n.as_i64() {
1775 doc.add_i64(field, i);
1776 }
1777 }
1778 FieldType::U64 => {
1779 if let Some(u) = n.as_u64() {
1780 doc.add_u64(field, u);
1781 } else if let Some(i) = n.as_i64() {
1782 if i >= 0 {
1784 doc.add_u64(field, i as u64);
1785 }
1786 }
1787 }
1788 FieldType::F64 => {
1789 if let Some(f) = n.as_f64() {
1790 doc.add_f64(field, f);
1791 }
1792 }
1793 _ => {}
1794 }
1795 }
1796 serde_json::Value::Array(arr) => {
1798 for item in arr {
1799 Self::add_json_value(doc, field, field_type, item);
1800 }
1801 }
1802 serde_json::Value::Object(obj) if matches!(field_type, FieldType::SparseVector) => {
1804 if let (Some(indices_val), Some(values_val)) =
1805 (obj.get("indices"), obj.get("values"))
1806 {
1807 let indices: Vec<u32> = indices_val
1808 .as_array()
1809 .map(|arr| {
1810 arr.iter()
1811 .filter_map(|v| v.as_u64().map(|n| n as u32))
1812 .collect()
1813 })
1814 .unwrap_or_default();
1815 let values: Vec<f32> = values_val
1816 .as_array()
1817 .map(|arr| {
1818 arr.iter()
1819 .filter_map(|v| v.as_f64().map(|n| n as f32))
1820 .collect()
1821 })
1822 .unwrap_or_default();
1823 if indices.len() == values.len() {
1824 let entries: Vec<(u32, f32)> = indices.into_iter().zip(values).collect();
1825 doc.add_sparse_vector(field, entries);
1826 }
1827 }
1828 }
1829 _ if matches!(field_type, FieldType::Json) => {
1831 doc.add_json(field, value.clone());
1832 }
1833 serde_json::Value::Object(_) => {}
1834 _ => {}
1835 }
1836 }
1837}
1838
1839#[cfg(test)]
1840mod tests {
1841 use super::*;
1842
1843 #[test]
1844 fn reorder_accepts_indexed_text_and_bmp_and_rejects_unsupported_fields() {
1845 for field in [
1846 "sparse_vector [indexed<format: seismic>, reorder]",
1847 "sparse_vector [indexed<format: maxscore>, reorder]",
1848 "dense_vector<4> [indexed, reorder]",
1849 "u64 [indexed, reorder]",
1850 "text [stored, reorder]",
1851 ] {
1852 let error =
1853 crate::dsl::sdl::parse_sdl(&format!("index test {{ field value: {field} }}"))
1854 .unwrap_err();
1855 assert!(error.to_string().contains("reorder"), "{error}");
1856 }
1857 for field in [
1858 "text [indexed, reorder]",
1859 "text [indexed<chunked>, reorder]",
1860 "sparse_vector [indexed, reorder]",
1861 ] {
1862 let definitions =
1863 crate::dsl::sdl::parse_sdl(&format!("index test {{ field value: {field} }}"))
1864 .unwrap();
1865 assert!(definitions[0].to_schema().has_reorder_fields());
1866 }
1867 let mut builder = Schema::builder();
1868 let sparse = builder.add_sparse_vector_field("sparse", true, false);
1869 builder.set_reorder(sparse, true);
1870 let schema = builder.build();
1871 assert!(schema.has_reorder_fields());
1872 assert!(schema.has_background_maintenance_fields());
1873 assert!(schema.validate().is_ok());
1874 }
1875
1876 #[test]
1877 fn json_content_hashes_roundtrip_and_reject_invalid_values() {
1878 for (kind, valid, invalid) in [
1879 ("text", serde_json::json!("hash"), serde_json::json!(17)),
1880 ("u64", serde_json::json!(17), serde_json::json!(-1)),
1881 (
1882 "bytes",
1883 serde_json::json!("AP8="),
1884 serde_json::json!("not base64!"),
1885 ),
1886 ] {
1887 let schema = crate::dsl::sdl::parse_sdl(&format!("index test {{ field id: text [primary, stored] field hash: {kind} [stored, content_hash] }}")).unwrap()[0].to_schema();
1888 let value = serde_json::json!({"id":"a", "hash":valid});
1889 let doc = Document::from_json(&value, &schema).unwrap();
1890 assert_eq!(doc.to_json(&schema), value);
1891 for invalid in [invalid, serde_json::Value::Null, serde_json::json!([valid])] {
1892 assert!(
1893 Document::from_json(&serde_json::json!({"id":"a", "hash":invalid}), &schema)
1894 .is_none()
1895 );
1896 }
1897 }
1898 }
1899
1900 #[test]
1901 fn test_schema_builder() {
1902 let mut builder = Schema::builder();
1903 let title = builder.add_text_field("title", true, true);
1904 let body = builder.add_text_field("body", true, false);
1905 let count = builder.add_u64_field("count", true, true);
1906 let schema = builder.build();
1907
1908 assert_eq!(schema.get_field("title"), Some(title));
1909 assert_eq!(schema.get_field("body"), Some(body));
1910 assert_eq!(schema.get_field("count"), Some(count));
1911 assert_eq!(schema.get_field("nonexistent"), None);
1912 }
1913
1914 #[test]
1915 fn ivf_tq_defaults_to_selective_soar() {
1916 for config in [
1917 DenseVectorConfig::new(8),
1918 DenseVectorConfig::ivf_tq(8, Some(4), 2),
1919 ] {
1920 let soar = config.soar.expect("IVF-TQ should enable SOAR by default");
1921 assert_eq!(soar.num_secondary, 1);
1922 assert!(soar.selective);
1923 assert_eq!(soar.calibration_target(), Some(0.30));
1924 }
1925
1926 assert!(DenseVectorConfig::flat(8).soar.is_none());
1927 assert!(DenseVectorConfig::tq(8).soar.is_none());
1928 }
1929
1930 #[test]
1931 fn binary_ivf_uses_measured_balanced_fifteen_million_geometry() {
1932 let config = BinaryDenseVectorConfig::new(2_560);
1933 assert_eq!(config.optimal_num_clusters(15_000_000), 3_873);
1934
1935 let explicit = config.with_ivf(Some(8_192), 128);
1936 assert_eq!(explicit.optimal_num_clusters(15_000_000), 8_192);
1937 }
1938
1939 #[test]
1940 fn target_vectors_sizes_automatic_topology_but_explicit_clusters_win() {
1941 let hinted = BinaryDenseVectorConfig::new(2_560).with_target_vectors(1_000_000_000);
1942 assert_eq!(hinted.optimal_num_clusters(1_000_000), 31_623);
1943
1944 let lower_hint = BinaryDenseVectorConfig::new(2_560).with_target_vectors(1_000_000);
1945 assert_eq!(
1946 lower_hint.optimal_num_clusters(15_000_000),
1947 BinaryDenseVectorConfig::new(2_560).optimal_num_clusters(15_000_000),
1948 "a steady-state hint is a lower bound and must not shrink live-corpus geometry"
1949 );
1950
1951 let explicit = hinted.with_ivf(Some(8_192), 128);
1952 assert_eq!(explicit.optimal_num_clusters(1_000_000), 8_192);
1953
1954 let float = DenseVectorConfig::ivf_tq(1_024, None, 64).with_target_vectors(1_000_000_000);
1955 assert_eq!(float.optimal_num_clusters(1_000_000), 252_982);
1956 }
1957
1958 #[test]
1959 fn persisted_target_vectors_must_be_positive_and_topology_bearing() {
1960 let mut zero = BinaryDenseVectorConfig::new(256);
1961 zero.target_vectors = Some(0);
1962 let mut builder = Schema::builder();
1963 builder.add_binary_dense_vector_field_with_config("hash", true, false, zero);
1964 let error = reject_removed_vector_index_types(&builder.build()).unwrap_err();
1965 assert!(error.contains("positive steady-state"), "{error}");
1966
1967 let hinted = DenseVectorConfig::ivf_tq(128, None, 64).with_target_vectors(1_000_000_000);
1968 let encoded = serde_json::to_value(&hinted).unwrap();
1969 let decoded: DenseVectorConfig = serde_json::from_value(encoded).unwrap();
1970 assert_eq!(decoded.target_vectors, Some(1_000_000_000));
1971
1972 let binary = BinaryDenseVectorConfig::new(2_560).with_target_vectors(1_000_000_000);
1973 let encoded = serde_json::to_value(&binary).unwrap();
1974 let decoded: BinaryDenseVectorConfig = serde_json::from_value(encoded).unwrap();
1975 assert_eq!(decoded.target_vectors, Some(1_000_000_000));
1976
1977 let old_dense: DenseVectorConfig = serde_json::from_value(serde_json::json!({
1978 "dim": 128,
1979 "index_type": "ivf_tq"
1980 }))
1981 .unwrap();
1982 assert_eq!(old_dense.target_vectors, None);
1983 let old_binary: BinaryDenseVectorConfig = serde_json::from_value(serde_json::json!({
1984 "dim": 256,
1985 "index_type": "ivf"
1986 }))
1987 .unwrap();
1988 assert_eq!(old_binary.target_vectors, None);
1989
1990 let flat = DenseVectorConfig::flat(128).with_target_vectors(1_000_000);
1991 let mut builder = Schema::builder();
1992 builder.add_dense_vector_field_with_config("embedding", true, false, flat);
1993 let error = reject_removed_vector_index_types(&builder.build()).unwrap_err();
1994 assert!(error.contains("flat/training-free"), "{error}");
1995
1996 let mut binary_flat = BinaryDenseVectorConfig::new(256);
1997 binary_flat.index_type = BinaryIndexType::Flat;
1998 binary_flat.target_vectors = Some(1_000_000);
1999 let mut builder = Schema::builder();
2000 builder.add_binary_dense_vector_field_with_config("hash", true, false, binary_flat);
2001 let error = reject_removed_vector_index_types(&builder.build()).unwrap_err();
2002 assert!(error.contains("flat/training-free"), "{error}");
2003 }
2004
2005 #[test]
2006 fn omitted_and_explicitly_disabled_soar_are_distinct_in_serde() {
2007 let omitted: DenseVectorConfig = serde_json::from_value(serde_json::json!({
2008 "dim": 8,
2009 "index_type": "ivf_tq"
2010 }))
2011 .unwrap();
2012 let default_soar = omitted
2013 .soar
2014 .as_ref()
2015 .expect("an omitted SOAR setting should enable the selective default");
2016 assert_eq!(default_soar.num_secondary, 1);
2017 assert!(default_soar.selective);
2018 assert_eq!(default_soar.calibration_target(), Some(0.30));
2019
2020 let disabled: DenseVectorConfig = serde_json::from_value(serde_json::json!({
2021 "dim": 8,
2022 "index_type": "ivf_tq",
2023 "soar": null
2024 }))
2025 .unwrap();
2026 assert!(disabled.soar.is_none());
2027
2028 let encoded = serde_json::to_value(&disabled).unwrap();
2029 assert_eq!(encoded.get("soar"), Some(&serde_json::Value::Null));
2030 let round_trip: DenseVectorConfig = serde_json::from_value(encoded).unwrap();
2031 assert!(
2032 round_trip.soar.is_none(),
2033 "explicit off must survive a schema round trip"
2034 );
2035 }
2036
2037 #[test]
2038 fn scann_config_serde_preserves_old_json_defaults_and_new_parameters() {
2039 let old_dense: DenseVectorConfig = serde_json::from_value(serde_json::json!({
2040 "dim": 768,
2041 "index_type": "ivf_tq"
2042 }))
2043 .unwrap();
2044 assert_eq!(old_dense.tree_levels, None);
2045 let old_json = serde_json::to_value(&old_dense).unwrap();
2046 assert!(old_json.get("tree_levels").is_none());
2047
2048 let scann: DenseVectorConfig = serde_json::from_value(serde_json::json!({
2049 "dim": 1024,
2050 "index_type": "scann",
2051 "num_clusters": 10_000_000,
2052 "tree_levels": 2,
2053 "nprobe": 1024
2054 }))
2055 .unwrap();
2056 assert_eq!(scann.index_type, VectorIndexType::Scann);
2057 assert_eq!(scann.tree_levels, Some(2));
2058 assert!(scann.soar.is_none());
2059
2060 let binary: BinaryDenseVectorConfig = serde_json::from_value(serde_json::json!({
2061 "dim": 1024,
2062 "index_type": "scann",
2063 "tree_levels": 3
2064 }))
2065 .unwrap();
2066 assert_eq!(binary.index_type, BinaryIndexType::Scann);
2067 assert_eq!(binary.tree_levels, Some(3));
2068 }
2069
2070 #[test]
2071 fn persisted_scann_geometry_is_validated_on_schema_load() {
2072 let mut invalid_levels = DenseVectorConfig::new(128);
2073 invalid_levels.index_type = VectorIndexType::Scann;
2074 invalid_levels.tree_levels = Some(4);
2075 invalid_levels.soar = None;
2076 let mut builder = Schema::builder();
2077 builder.add_dense_vector_field_with_config("embedding", true, false, invalid_levels);
2078 let error = reject_removed_vector_index_types(&builder.build())
2079 .expect_err("invalid persisted ScaNN levels must fail at the schema gate");
2080 assert!(error.contains("1..=3"), "{error}");
2081
2082 let mut wrong_algorithm = BinaryDenseVectorConfig::new(256);
2083 wrong_algorithm.tree_levels = Some(2);
2084 let mut builder = Schema::builder();
2085 builder.add_binary_dense_vector_field_with_config("hash", true, false, wrong_algorithm);
2086 let error = reject_removed_vector_index_types(&builder.build())
2087 .expect_err("ScaNN-only persisted options must fail on IVF");
2088 assert!(error.contains("does not use the ScaNN index"), "{error}");
2089
2090 let mut invalid_soar = DenseVectorConfig::flat(128);
2091 invalid_soar.index_type = VectorIndexType::Scann;
2092 invalid_soar.nprobe = 1;
2093 invalid_soar.soar = Some(crate::structures::SoarConfig::default());
2094 let mut builder = Schema::builder();
2095 builder.add_dense_vector_field_with_config("embedding", true, false, invalid_soar);
2096 let error = reject_removed_vector_index_types(&builder.build())
2097 .expect_err("persisted ScaNN SOAR must fail until assignments exist");
2098 assert!(error.contains("not implemented"), "{error}");
2099
2100 let mut one_leaf = DenseVectorConfig::flat(128);
2101 one_leaf.index_type = VectorIndexType::Scann;
2102 one_leaf.num_clusters = Some(1);
2103 one_leaf.nprobe = 1;
2104 let mut builder = Schema::builder();
2105 builder.add_dense_vector_field_with_config("embedding", true, false, one_leaf);
2106 let error = reject_removed_vector_index_types(&builder.build())
2107 .expect_err("one-leaf ScaNN geometry must fail at schema load");
2108 assert!(error.contains("2..=30000000"), "{error}");
2109
2110 let binary = BinaryDenseVectorConfig {
2111 dim: 255,
2112 index_type: BinaryIndexType::Scann,
2113 num_clusters: Some(2),
2114 target_vectors: None,
2115 tree_levels: Some(1),
2116 ivf_routing: IvfRoutingMode::Auto,
2117 nprobe: 1,
2118 soar: None,
2119 };
2120 let mut builder = Schema::builder();
2121 builder.add_binary_dense_vector_field_with_config("hash", true, false, binary);
2122 let error = reject_removed_vector_index_types(&builder.build())
2123 .expect_err("binary ScaNN dimensions must be byte-aligned");
2124 assert!(error.contains("multiple of 8"), "{error}");
2125 }
2126
2127 #[test]
2128 fn test_set_primary_key_forces_fast_and_indexed() {
2129 let mut builder = Schema::builder();
2134 let id = builder.add_text_field("id", false, true);
2135 builder.set_primary_key(id);
2136 let schema = builder.build();
2137
2138 let entry = schema.get_field_entry(id).unwrap();
2139 assert!(entry.primary_key);
2140 assert!(
2141 entry.fast,
2142 "primary key must imply fast (dedup reads the fast-field text dict)"
2143 );
2144 assert!(entry.indexed, "primary key must imply indexed");
2145 }
2146
2147 #[test]
2148 fn test_document() {
2149 let mut builder = Schema::builder();
2150 let title = builder.add_text_field("title", true, true);
2151 let count = builder.add_u64_field("count", true, true);
2152 let _schema = builder.build();
2153
2154 let mut doc = Document::new();
2155 doc.add_text(title, "Hello World");
2156 doc.add_u64(count, 42);
2157
2158 assert_eq!(doc.get_first(title).unwrap().as_text(), Some("Hello World"));
2159 assert_eq!(doc.get_first(count).unwrap().as_u64(), Some(42));
2160 }
2161
2162 #[test]
2163 fn test_document_serialization() {
2164 let mut builder = Schema::builder();
2165 let title = builder.add_text_field("title", true, true);
2166 let count = builder.add_u64_field("count", true, true);
2167 let _schema = builder.build();
2168
2169 let mut doc = Document::new();
2170 doc.add_text(title, "Hello World");
2171 doc.add_u64(count, 42);
2172
2173 let json = serde_json::to_string(&doc).unwrap();
2175 println!("Serialized doc: {}", json);
2176
2177 let doc2: Document = serde_json::from_str(&json).unwrap();
2179 assert_eq!(
2180 doc2.field_values().len(),
2181 2,
2182 "Should have 2 field values after deserialization"
2183 );
2184 assert_eq!(
2185 doc2.get_first(title).unwrap().as_text(),
2186 Some("Hello World")
2187 );
2188 assert_eq!(doc2.get_first(count).unwrap().as_u64(), Some(42));
2189 }
2190
2191 #[test]
2192 fn test_multivalue_field() {
2193 let mut builder = Schema::builder();
2194 let uris = builder.add_text_field("uris", true, true);
2195 let title = builder.add_text_field("title", true, true);
2196 let schema = builder.build();
2197
2198 let mut doc = Document::new();
2200 doc.add_text(uris, "one");
2201 doc.add_text(uris, "two");
2202 doc.add_text(title, "Test Document");
2203
2204 assert_eq!(doc.get_first(uris).unwrap().as_text(), Some("one"));
2206
2207 let all_uris: Vec<_> = doc.get_all(uris).collect();
2209 assert_eq!(all_uris.len(), 2);
2210 assert_eq!(all_uris[0].as_text(), Some("one"));
2211 assert_eq!(all_uris[1].as_text(), Some("two"));
2212
2213 let json = doc.to_json(&schema);
2215 let uris_json = json.get("uris").unwrap();
2216 assert!(uris_json.is_array(), "Multi-value field should be an array");
2217 let uris_arr = uris_json.as_array().unwrap();
2218 assert_eq!(uris_arr.len(), 2);
2219 assert_eq!(uris_arr[0].as_str(), Some("one"));
2220 assert_eq!(uris_arr[1].as_str(), Some("two"));
2221
2222 let title_json = json.get("title").unwrap();
2224 assert!(
2225 title_json.is_string(),
2226 "Single-value field should be a string"
2227 );
2228 assert_eq!(title_json.as_str(), Some("Test Document"));
2229 }
2230
2231 #[test]
2232 fn test_multivalue_from_json() {
2233 let mut builder = Schema::builder();
2234 let uris = builder.add_text_field("uris", true, true);
2235 let title = builder.add_text_field("title", true, true);
2236 let schema = builder.build();
2237
2238 let json = serde_json::json!({
2240 "uris": ["one", "two"],
2241 "title": "Test Document"
2242 });
2243
2244 let doc = Document::from_json(&json, &schema).unwrap();
2246
2247 let all_uris: Vec<_> = doc.get_all(uris).collect();
2249 assert_eq!(all_uris.len(), 2);
2250 assert_eq!(all_uris[0].as_text(), Some("one"));
2251 assert_eq!(all_uris[1].as_text(), Some("two"));
2252
2253 assert_eq!(
2255 doc.get_first(title).unwrap().as_text(),
2256 Some("Test Document")
2257 );
2258
2259 let json_out = doc.to_json(&schema);
2261 let uris_out = json_out.get("uris").unwrap().as_array().unwrap();
2262 assert_eq!(uris_out.len(), 2);
2263 assert_eq!(uris_out[0].as_str(), Some("one"));
2264 assert_eq!(uris_out[1].as_str(), Some("two"));
2265 }
2266
2267 #[test]
2268 fn test_multi_attribute_forces_array() {
2269 let mut builder = Schema::builder();
2272 let uris = builder.add_text_field("uris", true, true);
2273 builder.set_multi(uris, true); let title = builder.add_text_field("title", true, true);
2275 let schema = builder.build();
2276
2277 assert!(schema.get_field_entry(uris).unwrap().multi);
2279 assert!(!schema.get_field_entry(title).unwrap().multi);
2280
2281 let mut doc = Document::new();
2283 doc.add_text(uris, "only_one");
2284 doc.add_text(title, "Test Document");
2285
2286 let json = doc.to_json(&schema);
2288
2289 let uris_json = json.get("uris").unwrap();
2290 assert!(
2291 uris_json.is_array(),
2292 "Multi field should be array even with single value"
2293 );
2294 let uris_arr = uris_json.as_array().unwrap();
2295 assert_eq!(uris_arr.len(), 1);
2296 assert_eq!(uris_arr[0].as_str(), Some("only_one"));
2297
2298 let title_json = json.get("title").unwrap();
2300 assert!(
2301 title_json.is_string(),
2302 "Non-multi single-value field should be a string"
2303 );
2304 assert_eq!(title_json.as_str(), Some("Test Document"));
2305 }
2306
2307 #[test]
2308 fn test_sparse_vector_field() {
2309 let mut builder = Schema::builder();
2310 let embedding = builder.add_sparse_vector_field("embedding", true, true);
2311 let title = builder.add_text_field("title", true, true);
2312 let schema = builder.build();
2313
2314 assert_eq!(schema.get_field("embedding"), Some(embedding));
2315 assert_eq!(
2316 schema.get_field_entry(embedding).unwrap().field_type,
2317 FieldType::SparseVector
2318 );
2319
2320 let mut doc = Document::new();
2322 doc.add_sparse_vector(embedding, vec![(0, 1.0), (5, 2.5), (10, 0.5)]);
2323 doc.add_text(title, "Test Document");
2324
2325 let entries = doc
2327 .get_first(embedding)
2328 .unwrap()
2329 .as_sparse_vector()
2330 .unwrap();
2331 assert_eq!(entries, &[(0, 1.0), (5, 2.5), (10, 0.5)]);
2332
2333 let json = doc.to_json(&schema);
2335 let embedding_json = json.get("embedding").unwrap();
2336 assert!(embedding_json.is_object());
2337 assert_eq!(
2338 embedding_json
2339 .get("indices")
2340 .unwrap()
2341 .as_array()
2342 .unwrap()
2343 .len(),
2344 3
2345 );
2346
2347 let doc2 = Document::from_json(&json, &schema).unwrap();
2349 let entries2 = doc2
2350 .get_first(embedding)
2351 .unwrap()
2352 .as_sparse_vector()
2353 .unwrap();
2354 assert_eq!(entries2[0].0, 0);
2355 assert!((entries2[0].1 - 1.0).abs() < 1e-6);
2356 assert_eq!(entries2[1].0, 5);
2357 assert!((entries2[1].1 - 2.5).abs() < 1e-6);
2358 assert_eq!(entries2[2].0, 10);
2359 assert!((entries2[2].1 - 0.5).abs() < 1e-6);
2360 }
2361
2362 #[test]
2363 fn test_json_field() {
2364 let mut builder = Schema::builder();
2365 let metadata = builder.add_json_field("metadata", true);
2366 let title = builder.add_text_field("title", true, true);
2367 let schema = builder.build();
2368
2369 assert_eq!(schema.get_field("metadata"), Some(metadata));
2370 assert_eq!(
2371 schema.get_field_entry(metadata).unwrap().field_type,
2372 FieldType::Json
2373 );
2374 assert!(!schema.get_field_entry(metadata).unwrap().indexed);
2376 assert!(schema.get_field_entry(metadata).unwrap().stored);
2377
2378 let json_value = serde_json::json!({
2380 "author": "John Doe",
2381 "tags": ["rust", "search"],
2382 "nested": {"key": "value"}
2383 });
2384 let mut doc = Document::new();
2385 doc.add_json(metadata, json_value.clone());
2386 doc.add_text(title, "Test Document");
2387
2388 let stored_json = doc.get_first(metadata).unwrap().as_json().unwrap();
2390 assert_eq!(stored_json, &json_value);
2391 assert_eq!(
2392 stored_json.get("author").unwrap().as_str(),
2393 Some("John Doe")
2394 );
2395
2396 let doc_json = doc.to_json(&schema);
2398 let metadata_out = doc_json.get("metadata").unwrap();
2399 assert_eq!(metadata_out, &json_value);
2400
2401 let doc2 = Document::from_json(&doc_json, &schema).unwrap();
2403 let stored_json2 = doc2.get_first(metadata).unwrap().as_json().unwrap();
2404 assert_eq!(stored_json2, &json_value);
2405 }
2406
2407 #[test]
2408 fn test_json_field_various_types() {
2409 let mut builder = Schema::builder();
2410 let data = builder.add_json_field("data", true);
2411 let _schema = builder.build();
2412
2413 let arr_value = serde_json::json!([1, 2, 3, "four", null]);
2415 let mut doc = Document::new();
2416 doc.add_json(data, arr_value.clone());
2417 assert_eq!(doc.get_first(data).unwrap().as_json().unwrap(), &arr_value);
2418
2419 let str_value = serde_json::json!("just a string");
2421 let mut doc2 = Document::new();
2422 doc2.add_json(data, str_value.clone());
2423 assert_eq!(doc2.get_first(data).unwrap().as_json().unwrap(), &str_value);
2424
2425 let num_value = serde_json::json!(42.5);
2427 let mut doc3 = Document::new();
2428 doc3.add_json(data, num_value.clone());
2429 assert_eq!(doc3.get_first(data).unwrap().as_json().unwrap(), &num_value);
2430
2431 let null_value = serde_json::Value::Null;
2433 let mut doc4 = Document::new();
2434 doc4.add_json(data, null_value.clone());
2435 assert_eq!(
2436 doc4.get_first(data).unwrap().as_json().unwrap(),
2437 &null_value
2438 );
2439
2440 let bool_value = serde_json::json!(true);
2442 let mut doc5 = Document::new();
2443 doc5.add_json(data, bool_value.clone());
2444 assert_eq!(
2445 doc5.get_first(data).unwrap().as_json().unwrap(),
2446 &bool_value
2447 );
2448 }
2449}