Skip to main content

hermes_core/dsl/
schema.rs

1//! Schema definitions for documents and fields
2
3use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5
6/// Field identifier
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
8pub struct Field(pub u32);
9
10/// Types of fields supported
11#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
12pub enum FieldType {
13    /// Text field - tokenized and indexed
14    #[serde(rename = "text")]
15    Text,
16    /// Unsigned 64-bit integer
17    #[serde(rename = "u64")]
18    U64,
19    /// Signed 64-bit integer
20    #[serde(rename = "i64")]
21    I64,
22    /// 64-bit floating point
23    #[serde(rename = "f64")]
24    F64,
25    /// Raw bytes (not tokenized)
26    #[serde(rename = "bytes")]
27    Bytes,
28    /// Sparse vector field - indexed as inverted posting lists with quantized weights
29    #[serde(rename = "sparse_vector")]
30    SparseVector,
31    /// Dense vector field - indexed using RaBitQ binary quantization for ANN search
32    #[serde(rename = "dense_vector")]
33    DenseVector,
34    /// JSON field - arbitrary JSON data, stored but not indexed
35    #[serde(rename = "json")]
36    Json,
37    /// Binary dense vector field - packed-bit storage with Hamming distance scoring
38    #[serde(rename = "binary_dense_vector")]
39    BinaryDenseVector,
40}
41
42/// Field options
43#[derive(Debug, Clone, Serialize, Deserialize)]
44pub struct FieldEntry {
45    pub name: String,
46    pub field_type: FieldType,
47    pub indexed: bool,
48    pub stored: bool,
49    /// Name of the tokenizer to use for this field (for text fields)
50    pub tokenizer: Option<String>,
51    /// Whether this field can have multiple values (serialized as array in JSON)
52    #[serde(default)]
53    pub multi: bool,
54    /// Position tracking mode for phrase queries and multi-field element tracking
55    #[serde(default, skip_serializing_if = "Option::is_none")]
56    pub positions: Option<PositionMode>,
57    /// Configuration for sparse vector fields (index size, weight quantization)
58    #[serde(default, skip_serializing_if = "Option::is_none")]
59    pub sparse_vector_config: Option<crate::structures::SparseVectorConfig>,
60    /// Configuration for dense vector fields (dimension, quantization)
61    #[serde(default, skip_serializing_if = "Option::is_none")]
62    pub dense_vector_config: Option<DenseVectorConfig>,
63    /// Configuration for binary dense vector fields (dimension in bits)
64    #[serde(default, skip_serializing_if = "Option::is_none")]
65    pub binary_dense_vector_config: Option<BinaryDenseVectorConfig>,
66    /// Whether this field has columnar fast-field storage for O(1) doc→value access.
67    /// Valid for u64, i64, f64, and text fields.
68    #[serde(default)]
69    pub fast: bool,
70    /// Whether this field is a primary key (unique constraint, at most one per schema)
71    #[serde(default)]
72    pub primary_key: bool,
73    /// Whether build-time document reordering (Recursive Graph Bisection) is enabled.
74    /// Valid for sparse_vector fields with BMP format. Clusters similar documents
75    /// into the same blocks for better pruning effectiveness.
76    #[serde(default)]
77    pub reorder: bool,
78}
79
80/// Position tracking mode for text fields
81#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
82#[serde(rename_all = "snake_case")]
83pub enum PositionMode {
84    /// Track only element ordinal for multi-valued fields (which array element)
85    /// Useful for returning which element matched without full phrase query support
86    Ordinal,
87    /// Track only token position within text (for phrase queries)
88    /// Does not track element ordinal - all positions are relative to concatenated text
89    TokenPosition,
90    /// Track both element ordinal and token position (full support)
91    /// Position format: (element_ordinal << 20) | token_position
92    Full,
93}
94
95impl PositionMode {
96    /// Whether this mode tracks element ordinals
97    pub fn tracks_ordinal(&self) -> bool {
98        matches!(self, PositionMode::Ordinal | PositionMode::Full)
99    }
100
101    /// Whether this mode tracks token positions
102    pub fn tracks_token_position(&self) -> bool {
103        matches!(self, PositionMode::TokenPosition | PositionMode::Full)
104    }
105}
106
107/// Vector index algorithm type
108#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
109#[serde(rename_all = "snake_case")]
110pub enum VectorIndexType {
111    /// Flat - brute-force search over raw vectors (accumulating state)
112    Flat,
113    /// RaBitQ - binary quantization, good for small datasets (<100K)
114    #[default]
115    RaBitQ,
116    /// IVF-RaBitQ - inverted file with RaBitQ, good for medium datasets
117    IvfRaBitQ,
118    /// ScaNN - product quantization with OPQ and anisotropic loss, best for large datasets
119    ScaNN,
120}
121
122/// Storage quantization for dense vector elements
123///
124/// Controls the precision of each vector coordinate in `.vectors` files.
125/// Lower precision reduces storage and memory bandwidth; scoring uses
126/// native-precision SIMD (no dequantization on the hot path).
127#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
128#[serde(rename_all = "snake_case")]
129pub enum DenseVectorQuantization {
130    /// 32-bit IEEE 754 float (4 bytes/dim) — full precision, baseline
131    #[default]
132    F32,
133    /// 16-bit IEEE 754 half-float (2 bytes/dim) — <0.1% recall loss for normalized embeddings
134    F16,
135    /// 8-bit unsigned scalar quantization (1 byte/dim) — maps [-1,1] → [0,255]
136    UInt8,
137    /// Binary packed-bit storage (1 bit per dimension, ceil(dim/8) bytes per vector).
138    /// Used internally by BinaryDenseVector fields. Not selectable for DenseVector fields.
139    Binary,
140}
141
142impl DenseVectorQuantization {
143    /// Bytes per element for non-binary quantization types.
144    /// Panics for Binary — use `dim.div_ceil(8)` for binary vector byte size.
145    pub fn element_size(self) -> usize {
146        match self {
147            Self::F32 => 4,
148            Self::F16 => 2,
149            Self::UInt8 => 1,
150            Self::Binary => panic!("element_size() not valid for Binary; use dim.div_ceil(8)"),
151        }
152    }
153
154    /// Wire format tag (stored in .vectors header)
155    pub fn tag(self) -> u8 {
156        match self {
157            Self::F32 => 0,
158            Self::F16 => 1,
159            Self::UInt8 => 2,
160            Self::Binary => 3,
161        }
162    }
163
164    /// Decode wire format tag
165    pub fn from_tag(tag: u8) -> Option<Self> {
166        match tag {
167            0 => Some(Self::F32),
168            1 => Some(Self::F16),
169            2 => Some(Self::UInt8),
170            3 => Some(Self::Binary),
171            _ => None,
172        }
173    }
174}
175
176/// Configuration for dense vector fields using Flat, RaBitQ, IVF-RaBitQ, or ScaNN
177///
178/// Indexes operate in two states:
179/// - **Flat (accumulating)**: Brute-force search over raw vectors. Used when vector count
180///   is below `build_threshold` or before `build_index` is called.
181/// - **Built (ANN)**: Fast approximate nearest neighbor search using trained structures.
182///   Centroids and codebooks are trained from data and stored within the segment.
183#[derive(Debug, Clone, Serialize, Deserialize)]
184pub struct DenseVectorConfig {
185    /// Dimensionality of vectors
186    pub dim: usize,
187    /// Target vector index algorithm (Flat, RaBitQ, IVF-RaBitQ, or ScaNN)
188    /// When in accumulating state, search uses brute-force regardless of this setting.
189    #[serde(default)]
190    pub index_type: VectorIndexType,
191    /// Storage quantization for vector elements (f32, f16, uint8)
192    #[serde(default)]
193    pub quantization: DenseVectorQuantization,
194    /// Number of IVF clusters for IVF-RaBitQ and ScaNN (default: sqrt(n) capped at 4096)
195    /// If None, automatically determined based on dataset size.
196    #[serde(default, skip_serializing_if = "Option::is_none")]
197    pub num_clusters: Option<usize>,
198    /// Number of clusters to probe during search (default: 32)
199    #[serde(default = "default_nprobe")]
200    pub nprobe: usize,
201    /// Minimum number of vectors required before building ANN index.
202    /// Below this threshold, brute-force (Flat) search is used.
203    /// Default: 1000 for RaBitQ, 10000 for IVF-RaBitQ/ScaNN.
204    #[serde(default, skip_serializing_if = "Option::is_none")]
205    pub build_threshold: Option<usize>,
206    /// Whether stored vectors are pre-normalized to unit L2 norm.
207    /// When true, scoring skips per-vector norm computation (cosine = dot / ||q||),
208    /// reducing compute by ~40%. Common for embedding models (e.g. OpenAI, Cohere).
209    /// Default: true (most embedding models produce L2-normalized vectors).
210    #[serde(default = "default_unit_norm")]
211    pub unit_norm: bool,
212    /// SOAR spilled cluster assignments for IVF-based indexes (IVF-RaBitQ, ScaNN).
213    /// Assigns vectors to a secondary cluster with an orthogonality-amplified
214    /// residual, improving recall at the same nprobe for ~1.2-2x assignment storage.
215    /// Default: None (disabled). Ignored for Flat/RaBitQ index types.
216    #[serde(default, skip_serializing_if = "Option::is_none")]
217    pub soar: Option<crate::structures::SoarConfig>,
218}
219
220fn default_nprobe() -> usize {
221    32
222}
223
224fn default_unit_norm() -> bool {
225    true
226}
227
228impl DenseVectorConfig {
229    pub fn new(dim: usize) -> Self {
230        Self {
231            dim,
232            index_type: VectorIndexType::RaBitQ,
233            quantization: DenseVectorQuantization::F32,
234            num_clusters: None,
235            nprobe: 32,
236            build_threshold: None,
237            unit_norm: true,
238            soar: None,
239        }
240    }
241
242    /// Create IVF-RaBitQ configuration
243    pub fn with_ivf(dim: usize, num_clusters: Option<usize>, nprobe: usize) -> Self {
244        Self {
245            dim,
246            index_type: VectorIndexType::IvfRaBitQ,
247            quantization: DenseVectorQuantization::F32,
248            num_clusters,
249            nprobe,
250            build_threshold: None,
251            unit_norm: true,
252            soar: None,
253        }
254    }
255
256    /// Create ScaNN configuration
257    pub fn with_scann(dim: usize, num_clusters: Option<usize>, nprobe: usize) -> Self {
258        Self {
259            dim,
260            index_type: VectorIndexType::ScaNN,
261            quantization: DenseVectorQuantization::F32,
262            num_clusters,
263            nprobe,
264            build_threshold: None,
265            unit_norm: true,
266            soar: None,
267        }
268    }
269
270    /// Create Flat (brute-force) configuration - no ANN index
271    pub fn flat(dim: usize) -> Self {
272        Self {
273            dim,
274            index_type: VectorIndexType::Flat,
275            quantization: DenseVectorQuantization::F32,
276            num_clusters: None,
277            nprobe: 0,
278            build_threshold: None,
279            unit_norm: true,
280            soar: None,
281        }
282    }
283
284    /// Set storage quantization
285    pub fn with_quantization(mut self, quantization: DenseVectorQuantization) -> Self {
286        self.quantization = quantization;
287        self
288    }
289
290    /// Set build threshold for auto-building ANN index
291    pub fn with_build_threshold(mut self, threshold: usize) -> Self {
292        self.build_threshold = Some(threshold);
293        self
294    }
295
296    /// Mark vectors as pre-normalized to unit L2 norm
297    pub fn with_unit_norm(mut self) -> Self {
298        self.unit_norm = true;
299        self
300    }
301
302    /// Set number of IVF clusters
303    pub fn with_num_clusters(mut self, num_clusters: usize) -> Self {
304        self.num_clusters = Some(num_clusters);
305        self
306    }
307
308    /// Enable SOAR spilled secondary cluster assignments (IVF-based indexes only)
309    pub fn with_soar(mut self, soar: crate::structures::SoarConfig) -> Self {
310        self.soar = Some(soar);
311        self
312    }
313
314    /// Check if this config uses IVF
315    pub fn uses_ivf(&self) -> bool {
316        matches!(
317            self.index_type,
318            VectorIndexType::IvfRaBitQ | VectorIndexType::ScaNN
319        )
320    }
321
322    /// Check if this config uses ScaNN
323    pub fn uses_scann(&self) -> bool {
324        self.index_type == VectorIndexType::ScaNN
325    }
326
327    /// Check if this config is flat (brute-force)
328    pub fn is_flat(&self) -> bool {
329        self.index_type == VectorIndexType::Flat
330    }
331
332    /// Get the default build threshold for this index type
333    pub fn default_build_threshold(&self) -> usize {
334        self.build_threshold.unwrap_or(match self.index_type {
335            VectorIndexType::Flat => usize::MAX, // Never auto-build
336            VectorIndexType::RaBitQ => 1000,
337            VectorIndexType::IvfRaBitQ | VectorIndexType::ScaNN => 10000,
338        })
339    }
340
341    /// Calculate optimal number of clusters for given vector count
342    pub fn optimal_num_clusters(&self, num_vectors: usize) -> usize {
343        self.num_clusters.unwrap_or_else(|| {
344            // sqrt(n) heuristic, capped at 4096
345            let optimal = (num_vectors as f64).sqrt() as usize;
346            optimal.clamp(16, 4096)
347        })
348    }
349}
350
351/// Configuration for binary dense vector fields
352///
353/// Binary dense vectors store packed bits (1 bit per dimension) and use
354/// Hamming distance for scoring. Always uses brute-force flat search
355/// (Hamming popcount is ~10ns/vec for 768-bit, ANN indexes don't help).
356#[derive(Debug, Clone, Serialize, Deserialize)]
357pub struct BinaryDenseVectorConfig {
358    /// Number of bits (dimensions). Storage is ceil(dim/8) bytes per vector.
359    pub dim: usize,
360}
361
362impl BinaryDenseVectorConfig {
363    pub fn new(dim: usize) -> Self {
364        assert!(
365            dim.is_multiple_of(8),
366            "BinaryDenseVector dimension must be a multiple of 8, got {dim}"
367        );
368        Self { dim }
369    }
370
371    /// Number of bytes needed to store one vector
372    pub fn byte_len(&self) -> usize {
373        self.dim.div_ceil(8)
374    }
375}
376
377use super::query_field_router::QueryRouterRule;
378
379/// Schema defining document structure
380#[derive(Debug, Clone, Default, Serialize, Deserialize)]
381pub struct Schema {
382    fields: Vec<FieldEntry>,
383    name_to_field: HashMap<String, Field>,
384    /// Default fields for query parsing (when no field is specified)
385    #[serde(default)]
386    default_fields: Vec<Field>,
387    /// Query router rules for routing queries to specific fields based on regex patterns
388    #[serde(default)]
389    query_routers: Vec<QueryRouterRule>,
390}
391
392impl Schema {
393    pub fn builder() -> SchemaBuilder {
394        SchemaBuilder::default()
395    }
396
397    pub fn get_field(&self, name: &str) -> Option<Field> {
398        self.name_to_field.get(name).copied()
399    }
400
401    pub fn get_field_entry(&self, field: Field) -> Option<&FieldEntry> {
402        self.fields.get(field.0 as usize)
403    }
404
405    pub fn get_field_name(&self, field: Field) -> Option<&str> {
406        self.fields.get(field.0 as usize).map(|e| e.name.as_str())
407    }
408
409    pub fn fields(&self) -> impl Iterator<Item = (Field, &FieldEntry)> {
410        self.fields
411            .iter()
412            .enumerate()
413            .map(|(i, e)| (Field(i as u32), e))
414    }
415
416    pub fn num_fields(&self) -> usize {
417        self.fields.len()
418    }
419
420    /// Whether any field has the `reorder` attribute set.
421    /// Used by the background optimizer to determine which indexes need BP reordering.
422    pub fn has_reorder_fields(&self) -> bool {
423        self.fields.iter().any(|e| e.reorder)
424    }
425
426    /// Get the default fields for query parsing
427    pub fn default_fields(&self) -> &[Field] {
428        &self.default_fields
429    }
430
431    /// Set default fields (used by builder)
432    pub fn set_default_fields(&mut self, fields: Vec<Field>) {
433        self.default_fields = fields;
434    }
435
436    /// Get the query router rules
437    pub fn query_routers(&self) -> &[QueryRouterRule] {
438        &self.query_routers
439    }
440
441    /// Set query router rules
442    pub fn set_query_routers(&mut self, rules: Vec<QueryRouterRule>) {
443        self.query_routers = rules;
444    }
445
446    /// Get the primary key field, if one is defined
447    pub fn primary_field(&self) -> Option<Field> {
448        self.fields
449            .iter()
450            .enumerate()
451            .find(|(_, e)| e.primary_key)
452            .map(|(i, _)| Field(i as u32))
453    }
454}
455
456/// Builder for Schema
457#[derive(Debug, Default)]
458pub struct SchemaBuilder {
459    fields: Vec<FieldEntry>,
460    default_fields: Vec<String>,
461    query_routers: Vec<QueryRouterRule>,
462}
463
464impl SchemaBuilder {
465    pub fn add_text_field(&mut self, name: &str, indexed: bool, stored: bool) -> Field {
466        self.add_field_with_tokenizer(
467            name,
468            FieldType::Text,
469            indexed,
470            stored,
471            Some("simple".to_string()),
472        )
473    }
474
475    pub fn add_text_field_with_tokenizer(
476        &mut self,
477        name: &str,
478        indexed: bool,
479        stored: bool,
480        tokenizer: &str,
481    ) -> Field {
482        self.add_field_with_tokenizer(
483            name,
484            FieldType::Text,
485            indexed,
486            stored,
487            Some(tokenizer.to_string()),
488        )
489    }
490
491    pub fn add_u64_field(&mut self, name: &str, indexed: bool, stored: bool) -> Field {
492        self.add_field(name, FieldType::U64, indexed, stored)
493    }
494
495    pub fn add_i64_field(&mut self, name: &str, indexed: bool, stored: bool) -> Field {
496        self.add_field(name, FieldType::I64, indexed, stored)
497    }
498
499    pub fn add_f64_field(&mut self, name: &str, indexed: bool, stored: bool) -> Field {
500        self.add_field(name, FieldType::F64, indexed, stored)
501    }
502
503    pub fn add_bytes_field(&mut self, name: &str, stored: bool) -> Field {
504        self.add_field(name, FieldType::Bytes, false, stored)
505    }
506
507    /// Add a JSON field for storing arbitrary JSON data
508    ///
509    /// JSON fields are never indexed, only stored. They can hold any valid JSON value
510    /// (objects, arrays, strings, numbers, booleans, null).
511    pub fn add_json_field(&mut self, name: &str, stored: bool) -> Field {
512        self.add_field(name, FieldType::Json, false, stored)
513    }
514
515    /// Add a sparse vector field with default configuration
516    ///
517    /// Sparse vectors are indexed as inverted posting lists where each dimension
518    /// becomes a "term" and documents have quantized weights for each dimension.
519    pub fn add_sparse_vector_field(&mut self, name: &str, indexed: bool, stored: bool) -> Field {
520        self.add_sparse_vector_field_with_config(
521            name,
522            indexed,
523            stored,
524            crate::structures::SparseVectorConfig::default(),
525        )
526    }
527
528    /// Add a sparse vector field with custom configuration
529    ///
530    /// Use `SparseVectorConfig::splade()` for SPLADE models (u16 indices, uint8 weights).
531    /// Use `SparseVectorConfig::compact()` for maximum compression (u16 indices, uint4 weights).
532    pub fn add_sparse_vector_field_with_config(
533        &mut self,
534        name: &str,
535        indexed: bool,
536        stored: bool,
537        config: crate::structures::SparseVectorConfig,
538    ) -> Field {
539        let field = Field(self.fields.len() as u32);
540        self.fields.push(FieldEntry {
541            name: name.to_string(),
542            field_type: FieldType::SparseVector,
543            indexed,
544            stored,
545            tokenizer: None,
546            multi: false,
547            positions: None,
548            sparse_vector_config: Some(config),
549            dense_vector_config: None,
550            binary_dense_vector_config: None,
551            fast: false,
552            primary_key: false,
553            reorder: false,
554        });
555        field
556    }
557
558    /// Set sparse vector configuration for an existing field
559    pub fn set_sparse_vector_config(
560        &mut self,
561        field: Field,
562        config: crate::structures::SparseVectorConfig,
563    ) {
564        if let Some(entry) = self.fields.get_mut(field.0 as usize) {
565            entry.sparse_vector_config = Some(config);
566        }
567    }
568
569    /// Add a dense vector field with default configuration
570    ///
571    /// Dense vectors are indexed using RaBitQ binary quantization for fast ANN search.
572    /// The dimension must be specified as it determines the quantization structure.
573    pub fn add_dense_vector_field(
574        &mut self,
575        name: &str,
576        dim: usize,
577        indexed: bool,
578        stored: bool,
579    ) -> Field {
580        self.add_dense_vector_field_with_config(name, indexed, stored, DenseVectorConfig::new(dim))
581    }
582
583    /// Add a dense vector field with custom configuration
584    pub fn add_dense_vector_field_with_config(
585        &mut self,
586        name: &str,
587        indexed: bool,
588        stored: bool,
589        config: DenseVectorConfig,
590    ) -> Field {
591        let field = Field(self.fields.len() as u32);
592        self.fields.push(FieldEntry {
593            name: name.to_string(),
594            field_type: FieldType::DenseVector,
595            indexed,
596            stored,
597            tokenizer: None,
598            multi: false,
599            positions: None,
600            sparse_vector_config: None,
601            dense_vector_config: Some(config),
602            binary_dense_vector_config: None,
603            fast: false,
604            primary_key: false,
605            reorder: false,
606        });
607        field
608    }
609
610    /// Add a binary dense vector field
611    ///
612    /// Binary dense vectors use packed-bit storage (1 bit per dimension)
613    /// and Hamming distance scoring. Always brute-force flat search.
614    pub fn add_binary_dense_vector_field(
615        &mut self,
616        name: &str,
617        dim: usize,
618        indexed: bool,
619        stored: bool,
620    ) -> Field {
621        self.add_binary_dense_vector_field_with_config(
622            name,
623            indexed,
624            stored,
625            BinaryDenseVectorConfig::new(dim),
626        )
627    }
628
629    /// Add a binary dense vector field with custom configuration
630    pub fn add_binary_dense_vector_field_with_config(
631        &mut self,
632        name: &str,
633        indexed: bool,
634        stored: bool,
635        config: BinaryDenseVectorConfig,
636    ) -> Field {
637        let field = Field(self.fields.len() as u32);
638        self.fields.push(FieldEntry {
639            name: name.to_string(),
640            field_type: FieldType::BinaryDenseVector,
641            indexed,
642            stored,
643            tokenizer: None,
644            multi: false,
645            positions: None,
646            sparse_vector_config: None,
647            dense_vector_config: None,
648            binary_dense_vector_config: Some(config),
649            fast: false,
650            primary_key: false,
651            reorder: false,
652        });
653        field
654    }
655
656    fn add_field(
657        &mut self,
658        name: &str,
659        field_type: FieldType,
660        indexed: bool,
661        stored: bool,
662    ) -> Field {
663        self.add_field_with_tokenizer(name, field_type, indexed, stored, None)
664    }
665
666    fn add_field_with_tokenizer(
667        &mut self,
668        name: &str,
669        field_type: FieldType,
670        indexed: bool,
671        stored: bool,
672        tokenizer: Option<String>,
673    ) -> Field {
674        self.add_field_full(name, field_type, indexed, stored, tokenizer, false)
675    }
676
677    fn add_field_full(
678        &mut self,
679        name: &str,
680        field_type: FieldType,
681        indexed: bool,
682        stored: bool,
683        tokenizer: Option<String>,
684        multi: bool,
685    ) -> Field {
686        let field = Field(self.fields.len() as u32);
687        self.fields.push(FieldEntry {
688            name: name.to_string(),
689            field_type,
690            indexed,
691            stored,
692            tokenizer,
693            multi,
694            positions: None,
695            sparse_vector_config: None,
696            dense_vector_config: None,
697            binary_dense_vector_config: None,
698            fast: false,
699            primary_key: false,
700            reorder: false,
701        });
702        field
703    }
704
705    /// Set the multi attribute on the last added field
706    pub fn set_multi(&mut self, field: Field, multi: bool) {
707        if let Some(entry) = self.fields.get_mut(field.0 as usize) {
708            entry.multi = multi;
709        }
710    }
711
712    /// Set fast-field columnar storage for O(1) doc→value access.
713    /// Valid for u64, i64, f64, and text fields.
714    pub fn set_fast(&mut self, field: Field, fast: bool) {
715        if let Some(entry) = self.fields.get_mut(field.0 as usize) {
716            entry.fast = fast;
717        }
718    }
719
720    /// Mark a field as the primary key (unique constraint)
721    pub fn set_primary_key(&mut self, field: Field) {
722        if let Some(entry) = self.fields.get_mut(field.0 as usize) {
723            entry.primary_key = true;
724        }
725    }
726
727    /// Enable build-time document reordering (Recursive Graph Bisection) for BMP fields
728    pub fn set_reorder(&mut self, field: Field, reorder: bool) {
729        if let Some(entry) = self.fields.get_mut(field.0 as usize) {
730            entry.reorder = reorder;
731        }
732    }
733
734    /// Set position tracking mode for phrase queries and multi-field element tracking
735    pub fn set_positions(&mut self, field: Field, mode: PositionMode) {
736        if let Some(entry) = self.fields.get_mut(field.0 as usize) {
737            entry.positions = Some(mode);
738        }
739    }
740
741    /// Set default fields by name
742    pub fn set_default_fields(&mut self, field_names: Vec<String>) {
743        self.default_fields = field_names;
744    }
745
746    /// Set query router rules
747    pub fn set_query_routers(&mut self, rules: Vec<QueryRouterRule>) {
748        self.query_routers = rules;
749    }
750
751    pub fn build(self) -> Schema {
752        let mut name_to_field = HashMap::new();
753        for (i, entry) in self.fields.iter().enumerate() {
754            name_to_field.insert(entry.name.clone(), Field(i as u32));
755        }
756
757        // Resolve default field names to Field IDs
758        let default_fields: Vec<Field> = self
759            .default_fields
760            .iter()
761            .filter_map(|name| name_to_field.get(name).copied())
762            .collect();
763
764        Schema {
765            fields: self.fields,
766            name_to_field,
767            default_fields,
768            query_routers: self.query_routers,
769        }
770    }
771}
772
773/// Value that can be stored in a field
774#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
775pub enum FieldValue {
776    #[serde(rename = "text")]
777    Text(String),
778    #[serde(rename = "u64")]
779    U64(u64),
780    #[serde(rename = "i64")]
781    I64(i64),
782    #[serde(rename = "f64")]
783    F64(f64),
784    #[serde(rename = "bytes")]
785    Bytes(Vec<u8>),
786    /// Sparse vector: list of (dimension_id, weight) pairs
787    #[serde(rename = "sparse_vector")]
788    SparseVector(Vec<(u32, f32)>),
789    /// Dense vector: float32 values
790    #[serde(rename = "dense_vector")]
791    DenseVector(Vec<f32>),
792    /// Arbitrary JSON value
793    #[serde(rename = "json")]
794    Json(serde_json::Value),
795    /// Binary dense vector: packed bits (ceil(dim/8) bytes)
796    #[serde(rename = "binary_dense_vector")]
797    BinaryDenseVector(Vec<u8>),
798}
799
800impl FieldValue {
801    pub fn as_text(&self) -> Option<&str> {
802        match self {
803            FieldValue::Text(s) => Some(s),
804            _ => None,
805        }
806    }
807
808    pub fn as_u64(&self) -> Option<u64> {
809        match self {
810            FieldValue::U64(v) => Some(*v),
811            _ => None,
812        }
813    }
814
815    pub fn as_i64(&self) -> Option<i64> {
816        match self {
817            FieldValue::I64(v) => Some(*v),
818            _ => None,
819        }
820    }
821
822    pub fn as_f64(&self) -> Option<f64> {
823        match self {
824            FieldValue::F64(v) => Some(*v),
825            _ => None,
826        }
827    }
828
829    pub fn as_bytes(&self) -> Option<&[u8]> {
830        match self {
831            FieldValue::Bytes(b) => Some(b),
832            _ => None,
833        }
834    }
835
836    pub fn as_sparse_vector(&self) -> Option<&[(u32, f32)]> {
837        match self {
838            FieldValue::SparseVector(entries) => Some(entries),
839            _ => None,
840        }
841    }
842
843    pub fn as_dense_vector(&self) -> Option<&[f32]> {
844        match self {
845            FieldValue::DenseVector(v) => Some(v),
846            _ => None,
847        }
848    }
849
850    pub fn as_json(&self) -> Option<&serde_json::Value> {
851        match self {
852            FieldValue::Json(v) => Some(v),
853            _ => None,
854        }
855    }
856
857    pub fn as_binary_dense_vector(&self) -> Option<&[u8]> {
858        match self {
859            FieldValue::BinaryDenseVector(v) => Some(v),
860            _ => None,
861        }
862    }
863}
864
865/// A document to be indexed
866#[derive(Debug, Clone, Default, Serialize, Deserialize)]
867pub struct Document {
868    field_values: Vec<(Field, FieldValue)>,
869}
870
871impl Document {
872    pub fn new() -> Self {
873        Self::default()
874    }
875
876    pub fn add_text(&mut self, field: Field, value: impl Into<String>) {
877        self.field_values
878            .push((field, FieldValue::Text(value.into())));
879    }
880
881    pub fn add_u64(&mut self, field: Field, value: u64) {
882        self.field_values.push((field, FieldValue::U64(value)));
883    }
884
885    pub fn add_i64(&mut self, field: Field, value: i64) {
886        self.field_values.push((field, FieldValue::I64(value)));
887    }
888
889    pub fn add_f64(&mut self, field: Field, value: f64) {
890        self.field_values.push((field, FieldValue::F64(value)));
891    }
892
893    pub fn add_bytes(&mut self, field: Field, value: Vec<u8>) {
894        self.field_values.push((field, FieldValue::Bytes(value)));
895    }
896
897    pub fn add_sparse_vector(&mut self, field: Field, entries: Vec<(u32, f32)>) {
898        self.field_values
899            .push((field, FieldValue::SparseVector(entries)));
900    }
901
902    pub fn add_dense_vector(&mut self, field: Field, values: Vec<f32>) {
903        self.field_values
904            .push((field, FieldValue::DenseVector(values)));
905    }
906
907    pub fn add_json(&mut self, field: Field, value: serde_json::Value) {
908        self.field_values.push((field, FieldValue::Json(value)));
909    }
910
911    pub fn add_binary_dense_vector(&mut self, field: Field, values: Vec<u8>) {
912        self.field_values
913            .push((field, FieldValue::BinaryDenseVector(values)));
914    }
915
916    pub fn get_first(&self, field: Field) -> Option<&FieldValue> {
917        self.field_values
918            .iter()
919            .find(|(f, _)| *f == field)
920            .map(|(_, v)| v)
921    }
922
923    pub fn get_all(&self, field: Field) -> impl Iterator<Item = &FieldValue> {
924        self.field_values
925            .iter()
926            .filter(move |(f, _)| *f == field)
927            .map(|(_, v)| v)
928    }
929
930    pub fn field_values(&self) -> &[(Field, FieldValue)] {
931        &self.field_values
932    }
933
934    /// Return a new Document containing only fields marked as `stored` in the schema
935    pub fn filter_stored(&self, schema: &Schema) -> Document {
936        Document {
937            field_values: self
938                .field_values
939                .iter()
940                .filter(|(field, _)| {
941                    schema
942                        .get_field_entry(*field)
943                        .is_some_and(|entry| entry.stored)
944                })
945                .cloned()
946                .collect(),
947        }
948    }
949
950    /// Convert document to a JSON object using field names from schema
951    ///
952    /// Fields marked as `multi` in the schema are always returned as JSON arrays.
953    /// Other fields with multiple values are also returned as arrays.
954    /// Fields with a single value (and not marked multi) are returned as scalar values.
955    pub fn to_json(&self, schema: &Schema) -> serde_json::Value {
956        use std::collections::HashMap;
957
958        // Group values by field, keeping track of field entry for multi check
959        let mut field_values_map: HashMap<Field, (String, bool, Vec<serde_json::Value>)> =
960            HashMap::new();
961
962        for (field, value) in &self.field_values {
963            if let Some(entry) = schema.get_field_entry(*field) {
964                let json_value = match value {
965                    FieldValue::Text(s) => serde_json::Value::String(s.clone()),
966                    FieldValue::U64(n) => serde_json::Value::Number((*n).into()),
967                    FieldValue::I64(n) => serde_json::Value::Number((*n).into()),
968                    FieldValue::F64(n) => serde_json::json!(n),
969                    FieldValue::Bytes(b) => {
970                        use base64::Engine;
971                        serde_json::Value::String(
972                            base64::engine::general_purpose::STANDARD.encode(b),
973                        )
974                    }
975                    FieldValue::SparseVector(entries) => {
976                        let indices: Vec<u32> = entries.iter().map(|(i, _)| *i).collect();
977                        let values: Vec<f32> = entries.iter().map(|(_, v)| *v).collect();
978                        serde_json::json!({
979                            "indices": indices,
980                            "values": values
981                        })
982                    }
983                    FieldValue::DenseVector(values) => {
984                        serde_json::json!(values)
985                    }
986                    FieldValue::Json(v) => v.clone(),
987                    FieldValue::BinaryDenseVector(b) => {
988                        use base64::Engine;
989                        serde_json::Value::String(
990                            base64::engine::general_purpose::STANDARD.encode(b),
991                        )
992                    }
993                };
994                field_values_map
995                    .entry(*field)
996                    .or_insert_with(|| (entry.name.clone(), entry.multi, Vec::new()))
997                    .2
998                    .push(json_value);
999            }
1000        }
1001
1002        // Convert to JSON object, using arrays for multi fields or when multiple values exist
1003        let mut map = serde_json::Map::new();
1004        for (_field, (name, is_multi, values)) in field_values_map {
1005            let json_value = if is_multi || values.len() > 1 {
1006                serde_json::Value::Array(values)
1007            } else {
1008                values.into_iter().next().unwrap()
1009            };
1010            map.insert(name, json_value);
1011        }
1012
1013        serde_json::Value::Object(map)
1014    }
1015
1016    /// Create a Document from a JSON object using field names from schema
1017    ///
1018    /// Supports:
1019    /// - String values -> Text fields
1020    /// - Number values -> U64/I64/F64 fields (based on schema type)
1021    /// - Array values -> Multiple values for the same field (multifields)
1022    ///
1023    /// Unknown fields (not in schema) are silently ignored.
1024    pub fn from_json(json: &serde_json::Value, schema: &Schema) -> Option<Self> {
1025        let obj = json.as_object()?;
1026        let mut doc = Document::new();
1027
1028        for (key, value) in obj {
1029            if let Some(field) = schema.get_field(key) {
1030                let field_entry = schema.get_field_entry(field)?;
1031                Self::add_json_value(&mut doc, field, &field_entry.field_type, value);
1032            }
1033        }
1034
1035        Some(doc)
1036    }
1037
1038    /// Helper to add a JSON value to a document, handling type conversion
1039    fn add_json_value(
1040        doc: &mut Document,
1041        field: Field,
1042        field_type: &FieldType,
1043        value: &serde_json::Value,
1044    ) {
1045        match value {
1046            serde_json::Value::String(s) => {
1047                if matches!(field_type, FieldType::Text) {
1048                    doc.add_text(field, s.clone());
1049                }
1050            }
1051            serde_json::Value::Number(n) => {
1052                match field_type {
1053                    FieldType::I64 => {
1054                        if let Some(i) = n.as_i64() {
1055                            doc.add_i64(field, i);
1056                        }
1057                    }
1058                    FieldType::U64 => {
1059                        if let Some(u) = n.as_u64() {
1060                            doc.add_u64(field, u);
1061                        } else if let Some(i) = n.as_i64() {
1062                            // Allow positive i64 as u64
1063                            if i >= 0 {
1064                                doc.add_u64(field, i as u64);
1065                            }
1066                        }
1067                    }
1068                    FieldType::F64 => {
1069                        if let Some(f) = n.as_f64() {
1070                            doc.add_f64(field, f);
1071                        }
1072                    }
1073                    _ => {}
1074                }
1075            }
1076            // Handle arrays (multifields) - add each element separately
1077            serde_json::Value::Array(arr) => {
1078                for item in arr {
1079                    Self::add_json_value(doc, field, field_type, item);
1080                }
1081            }
1082            // Handle sparse vector objects
1083            serde_json::Value::Object(obj) if matches!(field_type, FieldType::SparseVector) => {
1084                if let (Some(indices_val), Some(values_val)) =
1085                    (obj.get("indices"), obj.get("values"))
1086                {
1087                    let indices: Vec<u32> = indices_val
1088                        .as_array()
1089                        .map(|arr| {
1090                            arr.iter()
1091                                .filter_map(|v| v.as_u64().map(|n| n as u32))
1092                                .collect()
1093                        })
1094                        .unwrap_or_default();
1095                    let values: Vec<f32> = values_val
1096                        .as_array()
1097                        .map(|arr| {
1098                            arr.iter()
1099                                .filter_map(|v| v.as_f64().map(|n| n as f32))
1100                                .collect()
1101                        })
1102                        .unwrap_or_default();
1103                    if indices.len() == values.len() {
1104                        let entries: Vec<(u32, f32)> = indices.into_iter().zip(values).collect();
1105                        doc.add_sparse_vector(field, entries);
1106                    }
1107                }
1108            }
1109            // Handle JSON fields - accept any value directly
1110            _ if matches!(field_type, FieldType::Json) => {
1111                doc.add_json(field, value.clone());
1112            }
1113            serde_json::Value::Object(_) => {}
1114            _ => {}
1115        }
1116    }
1117}
1118
1119#[cfg(test)]
1120mod tests {
1121    use super::*;
1122
1123    #[test]
1124    fn test_schema_builder() {
1125        let mut builder = Schema::builder();
1126        let title = builder.add_text_field("title", true, true);
1127        let body = builder.add_text_field("body", true, false);
1128        let count = builder.add_u64_field("count", true, true);
1129        let schema = builder.build();
1130
1131        assert_eq!(schema.get_field("title"), Some(title));
1132        assert_eq!(schema.get_field("body"), Some(body));
1133        assert_eq!(schema.get_field("count"), Some(count));
1134        assert_eq!(schema.get_field("nonexistent"), None);
1135    }
1136
1137    #[test]
1138    fn test_document() {
1139        let mut builder = Schema::builder();
1140        let title = builder.add_text_field("title", true, true);
1141        let count = builder.add_u64_field("count", true, true);
1142        let _schema = builder.build();
1143
1144        let mut doc = Document::new();
1145        doc.add_text(title, "Hello World");
1146        doc.add_u64(count, 42);
1147
1148        assert_eq!(doc.get_first(title).unwrap().as_text(), Some("Hello World"));
1149        assert_eq!(doc.get_first(count).unwrap().as_u64(), Some(42));
1150    }
1151
1152    #[test]
1153    fn test_document_serialization() {
1154        let mut builder = Schema::builder();
1155        let title = builder.add_text_field("title", true, true);
1156        let count = builder.add_u64_field("count", true, true);
1157        let _schema = builder.build();
1158
1159        let mut doc = Document::new();
1160        doc.add_text(title, "Hello World");
1161        doc.add_u64(count, 42);
1162
1163        // Serialize
1164        let json = serde_json::to_string(&doc).unwrap();
1165        println!("Serialized doc: {}", json);
1166
1167        // Deserialize
1168        let doc2: Document = serde_json::from_str(&json).unwrap();
1169        assert_eq!(
1170            doc2.field_values().len(),
1171            2,
1172            "Should have 2 field values after deserialization"
1173        );
1174        assert_eq!(
1175            doc2.get_first(title).unwrap().as_text(),
1176            Some("Hello World")
1177        );
1178        assert_eq!(doc2.get_first(count).unwrap().as_u64(), Some(42));
1179    }
1180
1181    #[test]
1182    fn test_multivalue_field() {
1183        let mut builder = Schema::builder();
1184        let uris = builder.add_text_field("uris", true, true);
1185        let title = builder.add_text_field("title", true, true);
1186        let schema = builder.build();
1187
1188        // Create document with multiple values for the same field
1189        let mut doc = Document::new();
1190        doc.add_text(uris, "one");
1191        doc.add_text(uris, "two");
1192        doc.add_text(title, "Test Document");
1193
1194        // Verify get_first returns the first value
1195        assert_eq!(doc.get_first(uris).unwrap().as_text(), Some("one"));
1196
1197        // Verify get_all returns all values
1198        let all_uris: Vec<_> = doc.get_all(uris).collect();
1199        assert_eq!(all_uris.len(), 2);
1200        assert_eq!(all_uris[0].as_text(), Some("one"));
1201        assert_eq!(all_uris[1].as_text(), Some("two"));
1202
1203        // Verify to_json returns array for multi-value field
1204        let json = doc.to_json(&schema);
1205        let uris_json = json.get("uris").unwrap();
1206        assert!(uris_json.is_array(), "Multi-value field should be an array");
1207        let uris_arr = uris_json.as_array().unwrap();
1208        assert_eq!(uris_arr.len(), 2);
1209        assert_eq!(uris_arr[0].as_str(), Some("one"));
1210        assert_eq!(uris_arr[1].as_str(), Some("two"));
1211
1212        // Verify single-value field is NOT an array
1213        let title_json = json.get("title").unwrap();
1214        assert!(
1215            title_json.is_string(),
1216            "Single-value field should be a string"
1217        );
1218        assert_eq!(title_json.as_str(), Some("Test Document"));
1219    }
1220
1221    #[test]
1222    fn test_multivalue_from_json() {
1223        let mut builder = Schema::builder();
1224        let uris = builder.add_text_field("uris", true, true);
1225        let title = builder.add_text_field("title", true, true);
1226        let schema = builder.build();
1227
1228        // Create JSON with array value
1229        let json = serde_json::json!({
1230            "uris": ["one", "two"],
1231            "title": "Test Document"
1232        });
1233
1234        // Parse from JSON
1235        let doc = Document::from_json(&json, &schema).unwrap();
1236
1237        // Verify all values are present
1238        let all_uris: Vec<_> = doc.get_all(uris).collect();
1239        assert_eq!(all_uris.len(), 2);
1240        assert_eq!(all_uris[0].as_text(), Some("one"));
1241        assert_eq!(all_uris[1].as_text(), Some("two"));
1242
1243        // Verify single value
1244        assert_eq!(
1245            doc.get_first(title).unwrap().as_text(),
1246            Some("Test Document")
1247        );
1248
1249        // Verify roundtrip: to_json should produce equivalent JSON
1250        let json_out = doc.to_json(&schema);
1251        let uris_out = json_out.get("uris").unwrap().as_array().unwrap();
1252        assert_eq!(uris_out.len(), 2);
1253        assert_eq!(uris_out[0].as_str(), Some("one"));
1254        assert_eq!(uris_out[1].as_str(), Some("two"));
1255    }
1256
1257    #[test]
1258    fn test_multi_attribute_forces_array() {
1259        // Test that fields marked as 'multi' are always serialized as arrays,
1260        // even when they have only one value
1261        let mut builder = Schema::builder();
1262        let uris = builder.add_text_field("uris", true, true);
1263        builder.set_multi(uris, true); // Mark as multi
1264        let title = builder.add_text_field("title", true, true);
1265        let schema = builder.build();
1266
1267        // Verify the multi attribute is set
1268        assert!(schema.get_field_entry(uris).unwrap().multi);
1269        assert!(!schema.get_field_entry(title).unwrap().multi);
1270
1271        // Create document with single value for multi field
1272        let mut doc = Document::new();
1273        doc.add_text(uris, "only_one");
1274        doc.add_text(title, "Test Document");
1275
1276        // Verify to_json returns array for multi field even with single value
1277        let json = doc.to_json(&schema);
1278
1279        let uris_json = json.get("uris").unwrap();
1280        assert!(
1281            uris_json.is_array(),
1282            "Multi field should be array even with single value"
1283        );
1284        let uris_arr = uris_json.as_array().unwrap();
1285        assert_eq!(uris_arr.len(), 1);
1286        assert_eq!(uris_arr[0].as_str(), Some("only_one"));
1287
1288        // Verify non-multi field with single value is NOT an array
1289        let title_json = json.get("title").unwrap();
1290        assert!(
1291            title_json.is_string(),
1292            "Non-multi single-value field should be a string"
1293        );
1294        assert_eq!(title_json.as_str(), Some("Test Document"));
1295    }
1296
1297    #[test]
1298    fn test_sparse_vector_field() {
1299        let mut builder = Schema::builder();
1300        let embedding = builder.add_sparse_vector_field("embedding", true, true);
1301        let title = builder.add_text_field("title", true, true);
1302        let schema = builder.build();
1303
1304        assert_eq!(schema.get_field("embedding"), Some(embedding));
1305        assert_eq!(
1306            schema.get_field_entry(embedding).unwrap().field_type,
1307            FieldType::SparseVector
1308        );
1309
1310        // Create document with sparse vector
1311        let mut doc = Document::new();
1312        doc.add_sparse_vector(embedding, vec![(0, 1.0), (5, 2.5), (10, 0.5)]);
1313        doc.add_text(title, "Test Document");
1314
1315        // Verify accessor
1316        let entries = doc
1317            .get_first(embedding)
1318            .unwrap()
1319            .as_sparse_vector()
1320            .unwrap();
1321        assert_eq!(entries, &[(0, 1.0), (5, 2.5), (10, 0.5)]);
1322
1323        // Verify JSON roundtrip
1324        let json = doc.to_json(&schema);
1325        let embedding_json = json.get("embedding").unwrap();
1326        assert!(embedding_json.is_object());
1327        assert_eq!(
1328            embedding_json
1329                .get("indices")
1330                .unwrap()
1331                .as_array()
1332                .unwrap()
1333                .len(),
1334            3
1335        );
1336
1337        // Parse back from JSON
1338        let doc2 = Document::from_json(&json, &schema).unwrap();
1339        let entries2 = doc2
1340            .get_first(embedding)
1341            .unwrap()
1342            .as_sparse_vector()
1343            .unwrap();
1344        assert_eq!(entries2[0].0, 0);
1345        assert!((entries2[0].1 - 1.0).abs() < 1e-6);
1346        assert_eq!(entries2[1].0, 5);
1347        assert!((entries2[1].1 - 2.5).abs() < 1e-6);
1348        assert_eq!(entries2[2].0, 10);
1349        assert!((entries2[2].1 - 0.5).abs() < 1e-6);
1350    }
1351
1352    #[test]
1353    fn test_json_field() {
1354        let mut builder = Schema::builder();
1355        let metadata = builder.add_json_field("metadata", true);
1356        let title = builder.add_text_field("title", true, true);
1357        let schema = builder.build();
1358
1359        assert_eq!(schema.get_field("metadata"), Some(metadata));
1360        assert_eq!(
1361            schema.get_field_entry(metadata).unwrap().field_type,
1362            FieldType::Json
1363        );
1364        // JSON fields are never indexed
1365        assert!(!schema.get_field_entry(metadata).unwrap().indexed);
1366        assert!(schema.get_field_entry(metadata).unwrap().stored);
1367
1368        // Create document with JSON value (object)
1369        let json_value = serde_json::json!({
1370            "author": "John Doe",
1371            "tags": ["rust", "search"],
1372            "nested": {"key": "value"}
1373        });
1374        let mut doc = Document::new();
1375        doc.add_json(metadata, json_value.clone());
1376        doc.add_text(title, "Test Document");
1377
1378        // Verify accessor
1379        let stored_json = doc.get_first(metadata).unwrap().as_json().unwrap();
1380        assert_eq!(stored_json, &json_value);
1381        assert_eq!(
1382            stored_json.get("author").unwrap().as_str(),
1383            Some("John Doe")
1384        );
1385
1386        // Verify JSON roundtrip via to_json/from_json
1387        let doc_json = doc.to_json(&schema);
1388        let metadata_out = doc_json.get("metadata").unwrap();
1389        assert_eq!(metadata_out, &json_value);
1390
1391        // Parse back from JSON
1392        let doc2 = Document::from_json(&doc_json, &schema).unwrap();
1393        let stored_json2 = doc2.get_first(metadata).unwrap().as_json().unwrap();
1394        assert_eq!(stored_json2, &json_value);
1395    }
1396
1397    #[test]
1398    fn test_json_field_various_types() {
1399        let mut builder = Schema::builder();
1400        let data = builder.add_json_field("data", true);
1401        let _schema = builder.build();
1402
1403        // Test with array
1404        let arr_value = serde_json::json!([1, 2, 3, "four", null]);
1405        let mut doc = Document::new();
1406        doc.add_json(data, arr_value.clone());
1407        assert_eq!(doc.get_first(data).unwrap().as_json().unwrap(), &arr_value);
1408
1409        // Test with string
1410        let str_value = serde_json::json!("just a string");
1411        let mut doc2 = Document::new();
1412        doc2.add_json(data, str_value.clone());
1413        assert_eq!(doc2.get_first(data).unwrap().as_json().unwrap(), &str_value);
1414
1415        // Test with number
1416        let num_value = serde_json::json!(42.5);
1417        let mut doc3 = Document::new();
1418        doc3.add_json(data, num_value.clone());
1419        assert_eq!(doc3.get_first(data).unwrap().as_json().unwrap(), &num_value);
1420
1421        // Test with null
1422        let null_value = serde_json::Value::Null;
1423        let mut doc4 = Document::new();
1424        doc4.add_json(data, null_value.clone());
1425        assert_eq!(
1426            doc4.get_first(data).unwrap().as_json().unwrap(),
1427            &null_value
1428        );
1429
1430        // Test with boolean
1431        let bool_value = serde_json::json!(true);
1432        let mut doc5 = Document::new();
1433        doc5.add_json(data, bool_value.clone());
1434        assert_eq!(
1435            doc5.get_first(data).unwrap().as_json().unwrap(),
1436            &bool_value
1437        );
1438    }
1439}