1use std::collections::{BTreeMap, HashMap, HashSet};
29
30use super::entity::EntityId;
31use crate::storage::schema::Value;
32
33#[derive(Debug, Clone, PartialEq)]
35pub enum RefTarget {
36 TableRow { table: String, row_id: u64 },
38 Node {
40 collection: String,
41 node_id: EntityId,
42 },
43 Edge {
45 collection: String,
46 edge_id: EntityId,
47 },
48 Vector {
50 collection: String,
51 vector_id: EntityId,
52 },
53 Entity {
55 collection: String,
56 entity_id: EntityId,
57 },
58}
59
60impl RefTarget {
61 pub fn table(table: impl Into<String>, row_id: u64) -> Self {
63 Self::TableRow {
64 table: table.into(),
65 row_id,
66 }
67 }
68
69 pub fn node(collection: impl Into<String>, node_id: EntityId) -> Self {
71 Self::Node {
72 collection: collection.into(),
73 node_id,
74 }
75 }
76
77 pub fn vector(collection: impl Into<String>, vector_id: EntityId) -> Self {
79 Self::Vector {
80 collection: collection.into(),
81 vector_id,
82 }
83 }
84
85 pub fn collection(&self) -> &str {
87 match self {
88 Self::TableRow { table, .. } => table,
89 Self::Node { collection, .. }
90 | Self::Edge { collection, .. }
91 | Self::Vector { collection, .. }
92 | Self::Entity { collection, .. } => collection,
93 }
94 }
95
96 pub fn entity_id(&self) -> EntityId {
98 match self {
99 Self::TableRow { row_id, .. } => EntityId(*row_id),
100 Self::Node { node_id, .. } => *node_id,
101 Self::Edge { edge_id, .. } => *edge_id,
102 Self::Vector { vector_id, .. } => *vector_id,
103 Self::Entity { entity_id, .. } => *entity_id,
104 }
105 }
106}
107
108#[derive(Debug, Clone, PartialEq)]
110pub enum MetadataValue {
111 Null,
112 Bool(bool),
113 Int(i64),
114 Float(f64),
115 String(String),
116 Bytes(Vec<u8>),
117 Array(Vec<MetadataValue>),
118 Object(HashMap<String, MetadataValue>),
119 Timestamp(u64),
120 Geo {
121 lat: f64,
122 lon: f64,
123 },
124 Reference(RefTarget),
126 References(Vec<RefTarget>),
128}
129
130impl MetadataValue {
131 pub fn metadata_type(&self) -> MetadataType {
133 match self {
134 Self::Null => MetadataType::Null,
135 Self::Bool(_) => MetadataType::Bool,
136 Self::Int(_) => MetadataType::Int,
137 Self::Float(_) => MetadataType::Float,
138 Self::String(_) => MetadataType::String,
139 Self::Bytes(_) => MetadataType::Bytes,
140 Self::Array(_) => MetadataType::Array,
141 Self::Object(_) => MetadataType::Object,
142 Self::Timestamp(_) => MetadataType::Timestamp,
143 Self::Geo { .. } => MetadataType::Geo,
144 Self::Reference(_) => MetadataType::Reference,
145 Self::References(_) => MetadataType::References,
146 }
147 }
148
149 pub fn is_reference(&self) -> bool {
151 matches!(self, Self::Reference(_) | Self::References(_))
152 }
153
154 pub fn as_reference(&self) -> Option<&RefTarget> {
156 match self {
157 Self::Reference(r) => Some(r),
158 _ => None,
159 }
160 }
161
162 pub fn as_references(&self) -> Option<&[RefTarget]> {
164 match self {
165 Self::References(refs) => Some(refs),
166 _ => None,
167 }
168 }
169
170 pub fn to_value(&self) -> Value {
172 match self {
173 Self::Null => Value::Null,
174 Self::Bool(b) => Value::Boolean(*b),
175 Self::Int(i) => Value::Integer(*i),
176 Self::Float(f) => Value::Float(*f),
177 Self::String(s) => Value::text(s.clone()),
178 Self::Bytes(b) => Value::Blob(b.clone()),
179 Self::Array(_) | Self::Object(_) => {
180 Value::Json(Vec::new())
182 }
183 Self::Timestamp(t) => Value::Timestamp(*t as i64),
184 Self::Geo { lat, lon } => {
185 Value::Json(format!("{{\"lat\":{},\"lon\":{}}}", lat, lon).into_bytes())
187 }
188 Self::Reference(r) => {
189 Value::text(format!("{}:{}", r.collection(), r.entity_id().0))
191 }
192 Self::References(refs) => {
193 let parts: Vec<String> = refs
195 .iter()
196 .map(|r| format!("{}:{}", r.collection(), r.entity_id().0))
197 .collect();
198 Value::text(parts.join(","))
199 }
200 }
201 }
202
203 pub fn from_value(value: &Value) -> Self {
205 match value {
206 Value::Null => Self::Null,
207 Value::Boolean(b) => Self::Bool(*b),
208 Value::Integer(i) => Self::Int(*i),
209 Value::Float(f) => Self::Float(*f),
210 Value::Text(s) => Self::String(s.to_string()),
211 Value::Blob(b) => Self::Bytes(b.clone()),
212 Value::Timestamp(t) => Self::Timestamp(*t as u64),
213 Value::Json(_) => Self::Object(HashMap::new()), _ => Self::Null, }
216 }
217
218 pub fn matches(&self, filter: &MetadataFilter) -> bool {
220 match filter {
221 MetadataFilter::Eq(v) => self == v,
222 MetadataFilter::Ne(v) => self != v,
223 MetadataFilter::Lt(v) => self.compare(v) == Some(std::cmp::Ordering::Less),
224 MetadataFilter::Le(v) => matches!(
225 self.compare(v),
226 Some(std::cmp::Ordering::Less | std::cmp::Ordering::Equal)
227 ),
228 MetadataFilter::Gt(v) => self.compare(v) == Some(std::cmp::Ordering::Greater),
229 MetadataFilter::Ge(v) => matches!(
230 self.compare(v),
231 Some(std::cmp::Ordering::Greater | std::cmp::Ordering::Equal)
232 ),
233 MetadataFilter::In(values) => values.contains(self),
234 MetadataFilter::NotIn(values) => !values.contains(self),
235 MetadataFilter::Contains(s) => {
236 if let Self::String(str_val) = self {
237 str_val.contains(s)
238 } else {
239 false
240 }
241 }
242 MetadataFilter::StartsWith(s) => {
243 if let Self::String(str_val) = self {
244 str_val.starts_with(s)
245 } else {
246 false
247 }
248 }
249 MetadataFilter::EndsWith(s) => {
250 if let Self::String(str_val) = self {
251 str_val.ends_with(s)
252 } else {
253 false
254 }
255 }
256 MetadataFilter::IsNull => matches!(self, Self::Null),
257 MetadataFilter::IsNotNull => !matches!(self, Self::Null),
258 MetadataFilter::Between(low, high) => {
259 matches!(
260 self.compare(low),
261 Some(std::cmp::Ordering::Greater | std::cmp::Ordering::Equal)
262 ) && matches!(
263 self.compare(high),
264 Some(std::cmp::Ordering::Less | std::cmp::Ordering::Equal)
265 )
266 }
267 }
268 }
269
270 fn compare(&self, other: &Self) -> Option<std::cmp::Ordering> {
272 match (self, other) {
273 (Self::Int(a), Self::Int(b)) => Some(a.cmp(b)),
274 (Self::Float(a), Self::Float(b)) => a.partial_cmp(b),
275 (Self::String(a), Self::String(b)) => Some(a.cmp(b)),
276 (Self::Timestamp(a), Self::Timestamp(b)) => Some(a.cmp(b)),
277 (Self::Int(a), Self::Float(b)) => (*a as f64).partial_cmp(b),
279 (Self::Float(a), Self::Int(b)) => a.partial_cmp(&(*b as f64)),
280 _ => None,
281 }
282 }
283}
284
285#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
287pub enum MetadataType {
288 Null,
289 Bool,
290 Int,
291 Float,
292 String,
293 Bytes,
294 Array,
295 Object,
296 Timestamp,
297 Geo,
298 Reference,
300 References,
302}
303
304#[derive(Debug, Clone, PartialEq)]
306pub enum MetadataFilter {
307 Eq(MetadataValue),
308 Ne(MetadataValue),
309 Lt(MetadataValue),
310 Le(MetadataValue),
311 Gt(MetadataValue),
312 Ge(MetadataValue),
313 In(Vec<MetadataValue>),
314 NotIn(Vec<MetadataValue>),
315 Contains(String),
316 StartsWith(String),
317 EndsWith(String),
318 IsNull,
319 IsNotNull,
320 Between(MetadataValue, MetadataValue),
321}
322
323#[derive(Debug, Clone, Default)]
325pub struct Metadata {
326 pub fields: HashMap<String, MetadataValue>,
328}
329
330impl Metadata {
331 pub fn new() -> Self {
333 Self::default()
334 }
335
336 pub fn with_fields(fields: HashMap<String, MetadataValue>) -> Self {
338 Self { fields }
339 }
340
341 pub fn set(&mut self, key: impl Into<String>, value: MetadataValue) {
343 self.fields.insert(key.into(), value);
344 }
345
346 pub fn get(&self, key: &str) -> Option<&MetadataValue> {
348 self.fields.get(key)
349 }
350
351 pub fn remove(&mut self, key: &str) -> Option<MetadataValue> {
353 self.fields.remove(key)
354 }
355
356 pub fn has(&self, key: &str) -> bool {
358 self.fields.contains_key(key)
359 }
360
361 pub fn len(&self) -> usize {
363 self.fields.len()
364 }
365
366 pub fn is_empty(&self) -> bool {
368 self.fields.is_empty()
369 }
370
371 pub fn iter(&self) -> impl Iterator<Item = (&String, &MetadataValue)> {
373 self.fields.iter()
374 }
375
376 pub fn matches_all(&self, filters: &[(String, MetadataFilter)]) -> bool {
378 filters.iter().all(|(key, filter)| {
379 if let Some(value) = self.get(key) {
380 value.matches(filter)
381 } else {
382 matches!(filter, MetadataFilter::IsNull)
383 }
384 })
385 }
386
387 pub fn matches_any(&self, filters: &[(String, MetadataFilter)]) -> bool {
389 filters.iter().any(|(key, filter)| {
390 if let Some(value) = self.get(key) {
391 value.matches(filter)
392 } else {
393 matches!(filter, MetadataFilter::IsNull)
394 }
395 })
396 }
397
398 pub fn merge(&mut self, other: Metadata) {
400 for (key, value) in other.fields {
401 self.fields.insert(key, value);
402 }
403 }
404}
405
406#[derive(Debug, Clone)]
408pub enum TypedColumn {
409 Bool(BTreeMap<(EntityId, String), bool>),
410 Int(BTreeMap<(EntityId, String), i64>),
411 Float(BTreeMap<(EntityId, String), f64>),
412 String(BTreeMap<(EntityId, String), String>),
413 Bytes(BTreeMap<(EntityId, String), Vec<u8>>),
414 Timestamp(BTreeMap<(EntityId, String), u64>),
415}
416
417#[derive(Debug, Default)]
424pub struct MetadataStorage {
425 bool_values: BTreeMap<(EntityId, String), bool>,
427 int_values: BTreeMap<(EntityId, String), i64>,
429 float_values: BTreeMap<(EntityId, String), f64>,
431 string_values: BTreeMap<(EntityId, String), String>,
433 bytes_values: BTreeMap<(EntityId, String), Vec<u8>>,
435 timestamp_values: BTreeMap<(EntityId, String), u64>,
437 complex_values: HashMap<(EntityId, String), MetadataValue>,
439 entity_keys: HashMap<EntityId, HashSet<String>>,
441}
442
443impl MetadataStorage {
444 pub fn new() -> Self {
446 Self::default()
447 }
448
449 pub fn set(&mut self, entity_id: EntityId, key: impl Into<String>, value: MetadataValue) {
451 let key = key.into();
452
453 self.remove_value(&entity_id, &key);
455
456 self.entity_keys
458 .entry(entity_id)
459 .or_default()
460 .insert(key.clone());
461
462 match value {
464 MetadataValue::Null => {
465 }
467 MetadataValue::Bool(b) => {
468 self.bool_values.insert((entity_id, key), b);
469 }
470 MetadataValue::Int(i) => {
471 self.int_values.insert((entity_id, key), i);
472 }
473 MetadataValue::Float(f) => {
474 self.float_values.insert((entity_id, key), f);
475 }
476 MetadataValue::String(s) => {
477 self.string_values.insert((entity_id, key), s);
478 }
479 MetadataValue::Bytes(b) => {
480 self.bytes_values.insert((entity_id, key), b);
481 }
482 MetadataValue::Timestamp(t) => {
483 self.timestamp_values.insert((entity_id, key), t);
484 }
485 complex @ (MetadataValue::Array(_)
486 | MetadataValue::Object(_)
487 | MetadataValue::Geo { .. }
488 | MetadataValue::Reference(_)
489 | MetadataValue::References(_)) => {
490 self.complex_values.insert((entity_id, key), complex);
491 }
492 }
493 }
494
495 pub fn get(&self, entity_id: EntityId, key: &str) -> Option<MetadataValue> {
497 let key_tuple = (entity_id, key.to_string());
498
499 if let Some(b) = self.bool_values.get(&key_tuple) {
501 return Some(MetadataValue::Bool(*b));
502 }
503 if let Some(i) = self.int_values.get(&key_tuple) {
504 return Some(MetadataValue::Int(*i));
505 }
506 if let Some(f) = self.float_values.get(&key_tuple) {
507 return Some(MetadataValue::Float(*f));
508 }
509 if let Some(s) = self.string_values.get(&key_tuple) {
510 return Some(MetadataValue::String(s.clone()));
511 }
512 if let Some(b) = self.bytes_values.get(&key_tuple) {
513 return Some(MetadataValue::Bytes(b.clone()));
514 }
515 if let Some(t) = self.timestamp_values.get(&key_tuple) {
516 return Some(MetadataValue::Timestamp(*t));
517 }
518 if let Some(c) = self.complex_values.get(&key_tuple) {
519 return Some(c.clone());
520 }
521
522 if self
524 .entity_keys
525 .get(&entity_id)
526 .is_some_and(|keys| keys.contains(key))
527 {
528 return Some(MetadataValue::Null);
529 }
530
531 None
532 }
533
534 pub fn get_all(&self, entity_id: EntityId) -> Metadata {
536 let mut metadata = Metadata::new();
537
538 if let Some(keys) = self.entity_keys.get(&entity_id) {
539 for key in keys {
540 if let Some(value) = self.get(entity_id, key) {
541 metadata.set(key.clone(), value);
542 }
543 }
544 }
545
546 metadata
547 }
548
549 pub fn set_all(&mut self, entity_id: EntityId, metadata: &Metadata) {
551 self.remove_all(entity_id);
553
554 for (key, value) in metadata.iter() {
556 self.set(entity_id, key.clone(), value.clone());
557 }
558 }
559
560 pub fn remove_all(&mut self, entity_id: EntityId) {
562 if let Some(keys) = self.entity_keys.remove(&entity_id) {
563 for key in keys {
564 self.remove_value(&entity_id, &key);
565 }
566 }
567 }
568
569 fn remove_value(&mut self, entity_id: &EntityId, key: &str) {
571 let key_tuple = (*entity_id, key.to_string());
572 self.bool_values.remove(&key_tuple);
573 self.int_values.remove(&key_tuple);
574 self.float_values.remove(&key_tuple);
575 self.string_values.remove(&key_tuple);
576 self.bytes_values.remove(&key_tuple);
577 self.timestamp_values.remove(&key_tuple);
578 self.complex_values.remove(&key_tuple);
579 }
580
581 pub fn filter_int_range(&self, key: &str, min: Option<i64>, max: Option<i64>) -> Vec<EntityId> {
583 let mut results = Vec::new();
584
585 for ((entity_id, k), value) in &self.int_values {
586 if k == key {
587 let in_range = match (min, max) {
588 (Some(lo), Some(hi)) => *value >= lo && *value <= hi,
589 (Some(lo), None) => *value >= lo,
590 (None, Some(hi)) => *value <= hi,
591 (None, None) => true,
592 };
593 if in_range {
594 results.push(*entity_id);
595 }
596 }
597 }
598
599 results
600 }
601
602 pub fn filter_string_prefix(&self, key: &str, prefix: &str) -> Vec<EntityId> {
604 let mut results = Vec::new();
605
606 for ((entity_id, k), value) in &self.string_values {
607 if k == key && value.starts_with(prefix) {
608 results.push(*entity_id);
609 }
610 }
611
612 results
613 }
614
615 pub fn filter_eq(&self, key: &str, value: &MetadataValue) -> Vec<EntityId> {
617 let mut results = Vec::new();
618
619 match value {
620 MetadataValue::Bool(target) => {
621 for ((entity_id, k), v) in &self.bool_values {
622 if k == key && v == target {
623 results.push(*entity_id);
624 }
625 }
626 }
627 MetadataValue::Int(target) => {
628 for ((entity_id, k), v) in &self.int_values {
629 if k == key && v == target {
630 results.push(*entity_id);
631 }
632 }
633 }
634 MetadataValue::Float(target) => {
635 for ((entity_id, k), v) in &self.float_values {
636 if k == key && (v - target).abs() < f64::EPSILON {
637 results.push(*entity_id);
638 }
639 }
640 }
641 MetadataValue::String(target) => {
642 for ((entity_id, k), v) in &self.string_values {
643 if k == key && v == target {
644 results.push(*entity_id);
645 }
646 }
647 }
648 _ => {
649 for ((entity_id, k), v) in &self.complex_values {
651 if k == key && v == value {
652 results.push(*entity_id);
653 }
654 }
655 }
656 }
657
658 results
659 }
660
661 pub fn entity_count(&self) -> usize {
663 self.entity_keys.len()
664 }
665
666 pub fn value_count(&self) -> usize {
668 self.bool_values.len()
669 + self.int_values.len()
670 + self.float_values.len()
671 + self.string_values.len()
672 + self.bytes_values.len()
673 + self.timestamp_values.len()
674 + self.complex_values.len()
675 }
676}
677
678#[cfg(test)]
679mod tests {
680 use super::*;
681
682 #[test]
683 fn test_metadata_storage() {
684 let mut storage = MetadataStorage::new();
685 let entity_id = EntityId::new(1);
686
687 storage.set(
688 entity_id,
689 "name",
690 MetadataValue::String("Alice".to_string()),
691 );
692 storage.set(entity_id, "age", MetadataValue::Int(25));
693 storage.set(entity_id, "active", MetadataValue::Bool(true));
694 storage.set(entity_id, "score", MetadataValue::Float(95.5));
695
696 assert_eq!(
697 storage.get(entity_id, "name"),
698 Some(MetadataValue::String("Alice".to_string()))
699 );
700 assert_eq!(storage.get(entity_id, "age"), Some(MetadataValue::Int(25)));
701 assert_eq!(
702 storage.get(entity_id, "active"),
703 Some(MetadataValue::Bool(true))
704 );
705 }
706
707 #[test]
708 fn test_int_range_filter() {
709 let mut storage = MetadataStorage::new();
710
711 for i in 0..10 {
712 storage.set(EntityId::new(i), "value", MetadataValue::Int(i as i64 * 10));
713 }
714
715 let results = storage.filter_int_range("value", Some(30), Some(70));
716 assert_eq!(results.len(), 5); }
718
719 #[test]
720 fn test_string_prefix_filter() {
721 let mut storage = MetadataStorage::new();
722
723 storage.set(
724 EntityId::new(1),
725 "name",
726 MetadataValue::String("Alice".to_string()),
727 );
728 storage.set(
729 EntityId::new(2),
730 "name",
731 MetadataValue::String("Bob".to_string()),
732 );
733 storage.set(
734 EntityId::new(3),
735 "name",
736 MetadataValue::String("Alicia".to_string()),
737 );
738
739 let results = storage.filter_string_prefix("name", "Ali");
740 assert_eq!(results.len(), 2);
741 }
742
743 #[test]
744 fn test_metadata_matches() {
745 let mut meta = Metadata::new();
746 meta.set("status", MetadataValue::String("active".to_string()));
747 meta.set("count", MetadataValue::Int(5));
748
749 let filters = vec![
750 (
751 "status".to_string(),
752 MetadataFilter::Eq(MetadataValue::String("active".to_string())),
753 ),
754 (
755 "count".to_string(),
756 MetadataFilter::Gt(MetadataValue::Int(3)),
757 ),
758 ];
759
760 assert!(meta.matches_all(&filters));
761 }
762
763 #[test]
764 fn test_get_all_metadata() {
765 let mut storage = MetadataStorage::new();
766 let entity_id = EntityId::new(1);
767
768 storage.set(entity_id, "a", MetadataValue::Int(1));
769 storage.set(entity_id, "b", MetadataValue::String("hello".to_string()));
770 storage.set(entity_id, "c", MetadataValue::Bool(true));
771
772 let metadata = storage.get_all(entity_id);
773 assert_eq!(metadata.len(), 3);
774 assert!(metadata.has("a"));
775 assert!(metadata.has("b"));
776 assert!(metadata.has("c"));
777 }
778}