1use lora_analyzer::symbols::VarId;
2use lora_store::{
3 LoraBinary, LoraDate, LoraDateTime, LoraDuration, LoraLocalDateTime, LoraLocalTime, LoraPoint,
4 LoraTime, LoraVector, NodeId, PropertyValue, RelationshipId, VectorValues,
5};
6
7#[derive(Debug, Clone, PartialEq)]
10pub struct LoraPath {
11 pub nodes: Vec<NodeId>,
12 pub rels: Vec<RelationshipId>,
13}
14use serde::ser::{SerializeMap, SerializeSeq};
15use serde::{Serialize, Serializer};
16use std::collections::BTreeMap;
17use std::sync::Arc;
18
19#[derive(Debug, Clone, PartialEq)]
20pub enum LoraValue {
21 Null,
22 Bool(bool),
23 Int(i64),
24 Float(f64),
25 String(String),
26 Binary(LoraBinary),
27 List(Vec<LoraValue>),
28 Map(BTreeMap<String, LoraValue>),
29 Node(NodeId),
30 Relationship(RelationshipId),
31 Path(LoraPath),
32 Date(LoraDate),
33 Time(LoraTime),
34 LocalTime(LoraLocalTime),
35 DateTime(LoraDateTime),
36 LocalDateTime(LoraLocalDateTime),
37 Duration(LoraDuration),
38 Point(LoraPoint),
39 Vector(LoraVector),
40}
41
42impl LoraValue {
43 pub fn is_truthy(&self) -> bool {
44 match self {
45 LoraValue::Null => false,
46 LoraValue::Bool(v) => *v,
47 _ => true,
48 }
49 }
50
51 pub fn as_i64(&self) -> Option<i64> {
52 match self {
53 LoraValue::Int(v) => Some(*v),
54 _ => None,
55 }
56 }
57
58 pub fn as_f64(&self) -> Option<f64> {
59 match self {
60 LoraValue::Int(v) => Some(*v as f64),
61 LoraValue::Float(v) => Some(*v),
62 _ => None,
63 }
64 }
65
66 pub(crate) fn temporal_cmp(&self, other: &LoraValue) -> Option<std::cmp::Ordering> {
70 let (a, b) = match (self, other) {
71 (LoraValue::Date(a), LoraValue::Date(b)) => (a.order_nanos(), b.order_nanos()),
72 (LoraValue::DateTime(a), LoraValue::DateTime(b)) => (a.order_nanos(), b.order_nanos()),
73 (LoraValue::LocalDateTime(a), LoraValue::LocalDateTime(b)) => {
74 (a.order_nanos(), b.order_nanos())
75 }
76 (LoraValue::Time(a), LoraValue::Time(b)) => (a.order_nanos(), b.order_nanos()),
77 (LoraValue::LocalTime(a), LoraValue::LocalTime(b)) => {
78 (a.order_nanos(), b.order_nanos())
79 }
80 _ => return None,
81 };
82 Some(a.cmp(&b))
83 }
84}
85
86impl Serialize for LoraValue {
87 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
88 where
89 S: Serializer,
90 {
91 match self {
92 LoraValue::Null => serializer.serialize_unit(),
93 LoraValue::Bool(v) => serializer.serialize_bool(*v),
94 LoraValue::Int(v) => serializer.serialize_i64(*v),
95 LoraValue::Float(v) => serializer.serialize_f64(*v),
96 LoraValue::String(v) => serializer.serialize_str(v),
97 LoraValue::Binary(v) => serialize_binary(serializer, v),
98
99 LoraValue::List(values) => {
100 let mut seq = serializer.serialize_seq(Some(values.len()))?;
101 for value in values {
102 seq.serialize_element(value)?;
103 }
104 seq.end()
105 }
106
107 LoraValue::Map(map) => {
108 let mut ser_map = serializer.serialize_map(Some(map.len()))?;
109 for (k, v) in map {
110 ser_map.serialize_entry(k, v)?;
111 }
112 ser_map.end()
113 }
114
115 LoraValue::Node(id) => {
117 let mut ser_map = serializer.serialize_map(Some(2))?;
118 ser_map.serialize_entry("kind", "node")?;
119 ser_map.serialize_entry("id", id)?;
120 ser_map.end()
121 }
122
123 LoraValue::Relationship(id) => {
124 let mut ser_map = serializer.serialize_map(Some(2))?;
125 ser_map.serialize_entry("kind", "relationship")?;
126 ser_map.serialize_entry("id", id)?;
127 ser_map.end()
128 }
129
130 LoraValue::Path(path) => {
131 let mut ser_map = serializer.serialize_map(Some(3))?;
132 ser_map.serialize_entry("kind", "path")?;
133 ser_map.serialize_entry("nodes", &path.nodes)?;
134 ser_map.serialize_entry("rels", &path.rels)?;
135 ser_map.end()
136 }
137
138 LoraValue::Date(d) => serializer.serialize_str(&d.to_string()),
139 LoraValue::Time(t) => serializer.serialize_str(&t.to_string()),
140 LoraValue::LocalTime(t) => serializer.serialize_str(&t.to_string()),
141 LoraValue::DateTime(dt) => serializer.serialize_str(&dt.to_string()),
142 LoraValue::LocalDateTime(dt) => serializer.serialize_str(&dt.to_string()),
143 LoraValue::Duration(dur) => serializer.serialize_str(&dur.to_string()),
144 LoraValue::Point(p) => {
145 let len = if p.z.is_some() { 4 } else { 3 };
146 let mut m = serializer.serialize_map(Some(len))?;
147 m.serialize_entry("srid", &p.srid)?;
148 m.serialize_entry("x", &p.x)?;
149 m.serialize_entry("y", &p.y)?;
150 if let Some(z) = p.z {
151 m.serialize_entry("z", &z)?;
152 }
153 m.end()
154 }
155 LoraValue::Vector(v) => serialize_vector(serializer, v),
156 }
157 }
158}
159
160fn serialize_binary<S: Serializer>(serializer: S, v: &LoraBinary) -> Result<S::Ok, S::Error> {
161 let mut m = serializer.serialize_map(Some(3))?;
162 m.serialize_entry("kind", "binary")?;
163 m.serialize_entry("length", &v.len())?;
164 m.serialize_entry("segments", v.segments())?;
165 m.end()
166}
167
168fn serialize_vector<S: Serializer>(serializer: S, v: &LoraVector) -> Result<S::Ok, S::Error> {
169 let mut m = serializer.serialize_map(Some(4))?;
170 m.serialize_entry("kind", "vector")?;
171 m.serialize_entry("dimension", &v.dimension)?;
172 m.serialize_entry("coordinateType", v.coordinate_type().as_str())?;
173 match &v.values {
177 VectorValues::Float64(values) => m.serialize_entry("values", values)?,
178 VectorValues::Float32(values) => {
179 let widened: Vec<f64> = values.iter().map(|x| *x as f64).collect();
180 m.serialize_entry("values", &widened)?;
181 }
182 VectorValues::Integer64(values) => m.serialize_entry("values", values)?,
183 VectorValues::Integer32(values) => {
184 let widened: Vec<i64> = values.iter().map(|x| *x as i64).collect();
185 m.serialize_entry("values", &widened)?;
186 }
187 VectorValues::Integer16(values) => {
188 let widened: Vec<i64> = values.iter().map(|x| *x as i64).collect();
189 m.serialize_entry("values", &widened)?;
190 }
191 VectorValues::Integer8(values) => {
192 let widened: Vec<i64> = values.iter().map(|x| *x as i64).collect();
193 m.serialize_entry("values", &widened)?;
194 }
195 }
196 m.end()
197}
198
199impl From<PropertyValue> for LoraValue {
200 fn from(value: PropertyValue) -> Self {
201 match value {
202 PropertyValue::Null => LoraValue::Null,
203 PropertyValue::Bool(v) => LoraValue::Bool(v),
204 PropertyValue::Int(v) => LoraValue::Int(v),
205 PropertyValue::Float(v) => LoraValue::Float(v),
206 PropertyValue::String(v) => LoraValue::String(v),
207 PropertyValue::Binary(v) => LoraValue::Binary(v),
208 PropertyValue::List(values) => {
209 LoraValue::List(values.into_iter().map(LoraValue::from).collect())
210 }
211 PropertyValue::Map(map) => LoraValue::Map(
212 map.into_iter()
213 .map(|(k, v)| (k, LoraValue::from(v)))
214 .collect(),
215 ),
216 PropertyValue::Date(d) => LoraValue::Date(d),
217 PropertyValue::Time(t) => LoraValue::Time(t),
218 PropertyValue::LocalTime(t) => LoraValue::LocalTime(t),
219 PropertyValue::DateTime(dt) => LoraValue::DateTime(dt),
220 PropertyValue::LocalDateTime(dt) => LoraValue::LocalDateTime(dt),
221 PropertyValue::Duration(dur) => LoraValue::Duration(dur),
222 PropertyValue::Point(p) => LoraValue::Point(p),
223 PropertyValue::Vector(v) => LoraValue::Vector(v),
224 }
225 }
226}
227
228impl From<lora_store::ValueRef<'_>> for LoraValue {
231 #[inline]
232 fn from(value: lora_store::ValueRef<'_>) -> Self {
233 use lora_store::ValueRef;
234 match value {
235 ValueRef::Null => LoraValue::Null,
236 ValueRef::Bool(v) => LoraValue::Bool(v),
237 ValueRef::Int(v) => LoraValue::Int(v),
238 ValueRef::Float(v) => LoraValue::Float(v),
239 ValueRef::String(v) => LoraValue::String(v.to_owned()),
240 ValueRef::Other(v) => match v.get() {
241 std::borrow::Cow::Borrowed(v) => LoraValue::from(v),
242 std::borrow::Cow::Owned(v) => LoraValue::from(v),
243 },
244 }
245 }
246}
247
248impl From<&PropertyValue> for LoraValue {
251 fn from(value: &PropertyValue) -> Self {
252 match value {
253 PropertyValue::Null => LoraValue::Null,
254 PropertyValue::Bool(v) => LoraValue::Bool(*v),
255 PropertyValue::Int(v) => LoraValue::Int(*v),
256 PropertyValue::Float(v) => LoraValue::Float(*v),
257 PropertyValue::String(v) => LoraValue::String(v.clone()),
258 PropertyValue::Binary(v) => LoraValue::Binary(v.clone()),
259 PropertyValue::List(values) => {
260 LoraValue::List(values.iter().map(LoraValue::from).collect())
261 }
262 PropertyValue::Map(map) => LoraValue::Map(
263 map.iter()
264 .map(|(k, v)| (k.clone(), LoraValue::from(v)))
265 .collect(),
266 ),
267 PropertyValue::Date(d) => LoraValue::Date(d.clone()),
268 PropertyValue::Time(t) => LoraValue::Time(t.clone()),
269 PropertyValue::LocalTime(t) => LoraValue::LocalTime(t.clone()),
270 PropertyValue::DateTime(dt) => LoraValue::DateTime(dt.clone()),
271 PropertyValue::LocalDateTime(dt) => LoraValue::LocalDateTime(dt.clone()),
272 PropertyValue::Duration(dur) => LoraValue::Duration(dur.clone()),
273 PropertyValue::Point(p) => LoraValue::Point(p.clone()),
274 PropertyValue::Vector(v) => LoraValue::Vector(v.clone()),
275 }
276 }
277}
278
279impl From<LoraValue> for PropertyValue {
280 fn from(value: LoraValue) -> Self {
281 match value {
282 LoraValue::Null => PropertyValue::Null,
283 LoraValue::Bool(v) => PropertyValue::Bool(v),
284 LoraValue::Int(v) => PropertyValue::Int(v),
285 LoraValue::Float(v) => PropertyValue::Float(v),
286 LoraValue::String(v) => PropertyValue::String(v),
287 LoraValue::Binary(v) => PropertyValue::Binary(v),
288 LoraValue::List(values) => {
289 PropertyValue::List(values.into_iter().map(PropertyValue::from).collect())
290 }
291 LoraValue::Map(map) => PropertyValue::Map(
292 map.into_iter()
293 .map(|(k, v)| (k, PropertyValue::from(v)))
294 .collect(),
295 ),
296 LoraValue::Node(id) => PropertyValue::String(format!("node:{id}")),
297 LoraValue::Relationship(id) => PropertyValue::String(format!("rel:{id}")),
298 LoraValue::Path(_) => PropertyValue::Null,
299 LoraValue::Date(d) => PropertyValue::Date(d),
300 LoraValue::Time(t) => PropertyValue::Time(t),
301 LoraValue::LocalTime(t) => PropertyValue::LocalTime(t),
302 LoraValue::DateTime(dt) => PropertyValue::DateTime(dt),
303 LoraValue::LocalDateTime(dt) => PropertyValue::LocalDateTime(dt),
304 LoraValue::Duration(dur) => PropertyValue::Duration(dur),
305 LoraValue::Point(p) => PropertyValue::Point(p),
306 LoraValue::Vector(v) => PropertyValue::Vector(v),
307 }
308 }
309}
310
311#[derive(Debug, Clone, PartialEq)]
314pub enum PropertyConversionError {
315 NestedVectorInList,
318 UnsupportedKind(&'static str),
322}
323
324impl std::fmt::Display for PropertyConversionError {
325 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
326 match self {
327 PropertyConversionError::NestedVectorInList => {
328 write!(f, "lists stored as properties cannot contain VECTOR values")
329 }
330 PropertyConversionError::UnsupportedKind(kind) => {
331 write!(f, "cannot store {kind} as a property")
332 }
333 }
334 }
335}
336
337impl std::error::Error for PropertyConversionError {}
338
339pub fn lora_value_to_property(value: LoraValue) -> Result<PropertyValue, PropertyConversionError> {
350 fn visit(value: &LoraValue, inside_list: bool) -> Result<(), PropertyConversionError> {
355 match value {
356 LoraValue::Vector(_) if inside_list => Err(PropertyConversionError::NestedVectorInList),
357 LoraValue::Node(_) => Err(PropertyConversionError::UnsupportedKind("a node")),
358 LoraValue::Relationship(_) => {
359 Err(PropertyConversionError::UnsupportedKind("a relationship"))
360 }
361 LoraValue::Path(_) => Err(PropertyConversionError::UnsupportedKind("a path")),
362 LoraValue::List(items) => {
363 for item in items {
364 visit(item, true)?;
365 }
366 Ok(())
367 }
368 LoraValue::Map(m) => {
369 for v in m.values() {
370 visit(v, inside_list)?;
371 }
372 Ok(())
373 }
374 _ => Ok(()),
375 }
376 }
377
378 visit(&value, false)?;
379 Ok(PropertyValue::from(value))
380}
381
382#[derive(Debug, Clone)]
389enum SlotValue {
390 Inline(LoraValue),
391 Shared(Arc<LoraValue>),
392}
393
394impl SlotValue {
395 #[inline]
396 fn new(value: LoraValue) -> Self {
397 let large = match &value {
400 LoraValue::String(s) => s.len() >= LARGE_BYTES,
401 LoraValue::List(_)
402 | LoraValue::Map(_)
403 | LoraValue::Path(_)
404 | LoraValue::Binary(_)
405 | LoraValue::Vector(_) => is_large(&value),
406 _ => false,
407 };
408 if large {
409 SlotValue::Shared(Arc::new(value))
410 } else {
411 SlotValue::Inline(value)
412 }
413 }
414
415 #[inline]
416 fn get(&self) -> &LoraValue {
417 match self {
418 SlotValue::Inline(v) => v,
419 SlotValue::Shared(v) => v,
420 }
421 }
422
423 #[inline]
424 fn into_value(self) -> LoraValue {
425 match self {
426 SlotValue::Inline(v) => v,
427 SlotValue::Shared(v) => Arc::try_unwrap(v).unwrap_or_else(|v| (*v).clone()),
428 }
429 }
430}
431
432const LARGE_BYTES: usize = 256;
434
435impl PartialEq for SlotValue {
436 fn eq(&self, other: &Self) -> bool {
437 self.get() == other.get()
438 }
439}
440
441fn is_large(value: &LoraValue) -> bool {
445 const ENTRIES: usize = 8;
446 let heavy = |v: &LoraValue| match v {
447 LoraValue::List(_)
448 | LoraValue::Map(_)
449 | LoraValue::Path(_)
450 | LoraValue::Vector(_)
451 | LoraValue::Binary(_) => true,
452 LoraValue::String(s) => s.len() >= LARGE_BYTES,
453 _ => false,
454 };
455 match value {
456 LoraValue::List(items) => items.len() >= ENTRIES || items.iter().any(heavy),
457 LoraValue::Map(map) => map.len() >= ENTRIES || map.values().any(heavy),
458 LoraValue::String(s) => s.len() >= LARGE_BYTES,
459 LoraValue::Path(p) => p.nodes.len() >= ENTRIES,
460 LoraValue::Binary(_) | LoraValue::Vector(_) => true,
461 _ => false,
462 }
463}
464
465#[derive(Debug, Clone, PartialEq)]
466struct RowEntry {
467 var: VarId,
471 name: Option<Arc<str>>,
478 value: SlotValue,
479}
480
481#[derive(Debug, Clone, Default, PartialEq)]
496pub struct Row {
497 entries: smallvec::SmallVec<Option<RowEntry>, 8>,
498 len_set: u32,
499}
500
501impl Serialize for Row {
502 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
503 where
504 S: Serializer,
505 {
506 let mut ser_map = serializer.serialize_map(Some(self.len()))?;
507 for entry in self.entries.iter().flatten() {
508 match &entry.name {
509 Some(name) => ser_map.serialize_entry(&**name, entry.value.get())?,
510 None => {
511 let fallback = format!("_{}", entry.var);
512 ser_map.serialize_entry(fallback.as_str(), entry.value.get())?;
513 }
514 }
515 }
516 ser_map.end()
517 }
518}
519
520impl Row {
521 pub fn new() -> Self {
522 Self::default()
523 }
524
525 #[inline]
526 pub fn get(&self, key: VarId) -> Option<&LoraValue> {
527 self.slot(key).map(SlotValue::get)
528 }
529
530 pub fn get_name(&self, key: VarId) -> Option<String> {
533 self.entries
534 .get(key.0 as usize)
535 .and_then(|slot| slot.as_ref())
536 .map(|entry| match &entry.name {
537 Some(n) => n.to_string(),
538 None => format!("_{}", entry.var),
539 })
540 }
541
542 #[inline]
543 pub fn insert(&mut self, key: VarId, value: LoraValue) {
544 self.set_value(key, SlotValue::new(value));
545 }
546
547 #[inline]
548 pub fn insert_named(&mut self, key: VarId, name: impl Into<Arc<str>>, value: LoraValue) {
549 self.set_named(key, name.into(), SlotValue::new(value));
550 }
551
552 #[inline]
556 pub fn insert_inline(&mut self, key: VarId, value: LoraValue) {
557 self.set_value(key, SlotValue::Inline(value));
558 }
559
560 pub fn insert_named_inline(&mut self, key: VarId, name: impl Into<Arc<str>>, value: LoraValue) {
564 self.set_named(key, name.into(), SlotValue::Inline(value));
565 }
566
567 pub fn insert_named_from(
571 &mut self,
572 key: VarId,
573 name: impl Into<Arc<str>>,
574 source: &Row,
575 from: VarId,
576 ) -> bool {
577 let Some(value) = source.slot(from).cloned() else {
578 return false;
579 };
580 self.set_named(key, name.into(), value);
581 true
582 }
583
584 pub fn insert_named_from_self(
586 &mut self,
587 key: VarId,
588 name: impl Into<Arc<str>>,
589 from: VarId,
590 ) -> bool {
591 let Some(value) = self.slot(from).cloned() else {
592 return false;
593 };
594 self.set_named(key, name.into(), value);
595 true
596 }
597
598 #[inline]
599 fn slot(&self, key: VarId) -> Option<&SlotValue> {
600 self.entries
601 .get(key.0 as usize)
602 .and_then(|slot| slot.as_ref())
603 .map(|entry| &entry.value)
604 }
605
606 #[inline]
609 fn set_value(&mut self, key: VarId, value: SlotValue) {
610 let idx = self.ensure_slot(key);
611 match &mut self.entries[idx] {
612 Some(existing) => existing.value = value,
613 slot @ None => {
614 *slot = Some(RowEntry {
615 var: key,
616 name: None,
617 value,
618 });
619 self.len_set += 1;
620 }
621 }
622 }
623
624 #[inline]
625 fn set_named(&mut self, key: VarId, name: Arc<str>, value: SlotValue) {
626 let idx = self.ensure_slot(key);
627 let was_set = self.entries[idx].is_some();
628 self.entries[idx] = Some(RowEntry {
629 var: key,
630 name: Some(name),
631 value,
632 });
633 if !was_set {
634 self.len_set += 1;
635 }
636 }
637
638 pub fn extend_from(&mut self, other: &Row) {
639 for entry in other.entries.iter().flatten() {
642 let idx = self.ensure_slot(entry.var);
643 let was_set = self.entries[idx].is_some();
644 self.entries[idx] = Some(entry.clone());
645 if !was_set {
646 self.len_set += 1;
647 }
648 }
649 }
650
651 pub fn fill_missing_from(&mut self, other: &Row) {
655 for entry in other.entries.iter().flatten() {
656 let idx = self.ensure_slot(entry.var);
657 if self.entries[idx].is_none() {
658 self.entries[idx] = Some(entry.clone());
659 self.len_set += 1;
660 }
661 }
662 }
663
664 pub fn iter(&self) -> impl Iterator<Item = (&VarId, &LoraValue)> {
665 self.entries
666 .iter()
667 .flatten()
668 .map(|entry| (&entry.var, entry.value.get()))
669 }
670
671 pub fn iter_named(
675 &self,
676 ) -> impl Iterator<Item = (&VarId, std::borrow::Cow<'_, str>, &LoraValue)> {
677 self.entries.iter().flatten().map(|entry| {
678 let name: std::borrow::Cow<'_, str> = match &entry.name {
679 Some(n) => std::borrow::Cow::Borrowed(&**n),
680 None => std::borrow::Cow::Owned(format!("_{}", entry.var)),
681 };
682 (&entry.var, name, entry.value.get())
683 })
684 }
685
686 pub fn into_iter_named(self) -> impl Iterator<Item = (VarId, Arc<str>, LoraValue)> {
691 self.entries.into_iter().flatten().map(|entry| {
692 let RowEntry { var, name, value } = entry;
693 (
694 var,
695 name.unwrap_or_else(|| Arc::from(format!("_{var}"))),
696 value.into_value(),
697 )
698 })
699 }
700
701 pub fn len(&self) -> usize {
702 self.len_set as usize
703 }
704
705 pub fn is_empty(&self) -> bool {
706 self.len_set == 0
707 }
708
709 pub fn contains_key(&self, key: VarId) -> bool {
710 self.entries
711 .get(key.0 as usize)
712 .is_some_and(|slot| slot.is_some())
713 }
714
715 fn ensure_slot(&mut self, key: VarId) -> usize {
718 let idx = key.0 as usize;
719 if idx >= self.entries.len() {
720 self.entries.resize_with(idx + 1, || None);
721 }
722 idx
723 }
724}
725
726#[derive(Debug, Clone, Copy, PartialEq, Eq)]
727pub enum ResultFormat {
728 Rows,
729 RowArrays,
730 Graph,
731 Combined,
732}
733
734#[derive(Debug, Clone, Copy, PartialEq, Eq)]
735pub struct ExecuteOptions {
736 pub format: ResultFormat,
737}
738
739impl Default for ExecuteOptions {
740 fn default() -> Self {
741 Self {
742 format: ResultFormat::Graph,
743 }
744 }
745}
746
747#[derive(Debug, Clone, Serialize)]
748#[serde(untagged)]
749pub enum QueryResult {
750 Rows(RowsResult),
751 RowArrays(RowArraysResult),
752 Graph(GraphResult),
753 Combined(CombinedResult),
754}
755
756#[derive(Debug, Clone, Serialize)]
757pub struct RowsResult {
758 pub rows: Vec<Row>,
759}
760
761#[derive(Debug, Clone, Serialize)]
762pub struct RowArraysResult {
763 pub columns: Vec<String>,
764 pub rows: Vec<Vec<LoraValue>>,
765}
766
767#[derive(Debug, Clone, Serialize)]
768pub struct GraphResult {
769 pub graph: HydratedGraph,
770}
771
772#[derive(Debug, Clone, Serialize)]
773pub struct CombinedResult {
774 pub columns: Vec<String>,
775 pub data: Vec<CombinedRow>,
776 pub graph: HydratedGraph,
777}
778
779#[derive(Debug, Clone, Serialize)]
780pub struct CombinedRow {
781 pub row: Vec<LoraValue>,
782}
783
784#[derive(Debug, Clone, Serialize, Default)]
785pub struct HydratedGraph {
786 pub nodes: Vec<HydratedNode>,
787 pub relationships: Vec<HydratedRelationship>,
788}
789
790#[derive(Debug, Clone, Serialize, PartialEq)]
791pub struct HydratedNode {
792 pub id: i64,
793 pub labels: Vec<String>,
794 pub properties: BTreeMap<String, LoraValue>,
795}
796
797#[derive(Debug, Clone, Serialize, PartialEq)]
798pub struct HydratedRelationship {
799 pub id: i64,
800 #[serde(rename = "startId")]
801 pub start_id: i64,
802 #[serde(rename = "endId")]
803 pub end_id: i64,
804 #[serde(rename = "type")]
805 pub rel_type: String,
806 pub properties: BTreeMap<String, LoraValue>,
807}
808
809pub fn project_rows(rows: Vec<Row>, options: ExecuteOptions) -> QueryResult {
810 match options.format {
811 ResultFormat::Rows => QueryResult::Rows(RowsResult { rows }),
812
813 ResultFormat::RowArrays => {
814 let columns = infer_columns(&rows);
815 let projected_rows = rows.iter().map(|row| row_to_array(row, &columns)).collect();
816
817 QueryResult::RowArrays(RowArraysResult {
818 columns,
819 rows: projected_rows,
820 })
821 }
822
823 ResultFormat::Graph => QueryResult::Graph(GraphResult {
824 graph: collect_hydrated_graph(&rows),
825 }),
826
827 ResultFormat::Combined => {
828 let columns = infer_columns(&rows);
829 let data = rows
830 .iter()
831 .map(|row| CombinedRow {
832 row: row_to_array(row, &columns),
833 })
834 .collect();
835
836 QueryResult::Combined(CombinedResult {
837 columns,
838 data,
839 graph: collect_hydrated_graph(&rows),
840 })
841 }
842 }
843}
844
845fn infer_columns(rows: &[Row]) -> Vec<String> {
846 rows.first()
847 .map(|row| {
848 row.iter_named()
849 .map(|(_, name, _)| name.into_owned())
850 .collect::<Vec<_>>()
851 })
852 .unwrap_or_default()
853}
854
855fn row_to_array(row: &Row, columns: &[String]) -> Vec<LoraValue> {
856 columns
859 .iter()
860 .map(|col| {
861 row.iter_named()
862 .find(|(_, name, _)| name.as_ref() == col.as_str())
863 .map(|(_, _, v)| v.clone())
864 .unwrap_or(LoraValue::Null)
865 })
866 .collect()
867}
868
869fn collect_hydrated_graph(rows: &[Row]) -> HydratedGraph {
870 let mut nodes = BTreeMap::<i64, HydratedNode>::new();
871 let mut relationships = BTreeMap::<i64, HydratedRelationship>::new();
872
873 for row in rows {
874 for (_, _, value) in row.iter_named() {
875 collect_graph_from_value(value, &mut nodes, &mut relationships);
876 }
877 }
878
879 HydratedGraph {
880 nodes: nodes.into_values().collect(),
881 relationships: relationships.into_values().collect(),
882 }
883}
884
885fn collect_graph_from_value(
886 value: &LoraValue,
887 nodes: &mut BTreeMap<i64, HydratedNode>,
888 relationships: &mut BTreeMap<i64, HydratedRelationship>,
889) {
890 match value {
891 LoraValue::List(values) => {
892 for value in values {
893 collect_graph_from_value(value, nodes, relationships);
894 }
895 }
896
897 LoraValue::Map(map) => {
898 if let Some(node) = try_as_hydrated_node(map) {
899 nodes.entry(node.id).or_insert(node);
900 return;
901 }
902
903 if let Some(rel) = try_as_hydrated_relationship(map) {
904 relationships.entry(rel.id).or_insert(rel);
905 return;
906 }
907
908 for value in map.values() {
909 collect_graph_from_value(value, nodes, relationships);
910 }
911 }
912
913 _ => {}
914 }
915}
916
917fn try_as_hydrated_node(map: &BTreeMap<String, LoraValue>) -> Option<HydratedNode> {
918 let id = match map.get("id")? {
919 LoraValue::Int(v) => *v,
920 _ => return None,
921 };
922
923 let labels = match map.get("labels")? {
924 LoraValue::List(values) => values
925 .iter()
926 .map(|v| match v {
927 LoraValue::String(s) => Some(s.clone()),
928 _ => None,
929 })
930 .collect::<Option<Vec<_>>>()?,
931 _ => return None,
932 };
933
934 let properties = match map.get("properties")? {
935 LoraValue::Map(props) => props.clone(),
936 _ => return None,
937 };
938
939 Some(HydratedNode {
940 id,
941 labels,
942 properties,
943 })
944}
945
946fn try_as_hydrated_relationship(map: &BTreeMap<String, LoraValue>) -> Option<HydratedRelationship> {
947 match map.get("kind") {
948 Some(LoraValue::String(kind)) if kind == "relationship" => {}
949 _ => return None,
950 }
951
952 let id = match map.get("id")? {
953 LoraValue::Int(v) => *v,
954 _ => return None,
955 };
956
957 let start_id = match map.get("startId").or_else(|| map.get("src"))? {
958 LoraValue::Int(v) => *v,
959 _ => return None,
960 };
961
962 let end_id = match map.get("endId").or_else(|| map.get("dst"))? {
963 LoraValue::Int(v) => *v,
964 _ => return None,
965 };
966
967 let rel_type = match map.get("type")? {
968 LoraValue::String(s) => s.clone(),
969 _ => return None,
970 };
971
972 let properties = match map.get("properties")? {
973 LoraValue::Map(props) => props.clone(),
974 _ => return None,
975 };
976
977 Some(HydratedRelationship {
978 id,
979 start_id,
980 end_id,
981 rel_type,
982 properties,
983 })
984}
985
986#[cfg(test)]
987mod tests {
988 use super::*;
989
990 fn big() -> LoraValue {
991 LoraValue::List((0..100).map(LoraValue::Int).collect())
992 }
993
994 #[test]
995 fn a_slot_is_no_larger_than_its_value() {
996 assert_eq!(
999 std::mem::size_of::<SlotValue>(),
1000 std::mem::size_of::<LoraValue>()
1001 );
1002 }
1003
1004 #[test]
1005 fn a_cloned_row_shares_a_large_value_and_copies_a_small_one() {
1006 let mut row = Row::new();
1007 row.insert(VarId(0), big());
1008 row.insert(VarId(1), LoraValue::List(vec![LoraValue::Int(1)]));
1009 let copy = row.clone();
1010 assert!(std::ptr::eq(
1011 row.get(VarId(0)).unwrap(),
1012 copy.get(VarId(0)).unwrap()
1013 ));
1014 assert!(!std::ptr::eq(
1015 row.get(VarId(1)).unwrap(),
1016 copy.get(VarId(1)).unwrap()
1017 ));
1018 assert_eq!(row, copy);
1019 }
1020
1021 #[test]
1022 fn a_shared_value_is_handed_out_whole_by_each_owner() {
1023 let mut row = Row::new();
1024 row.insert_named(VarId(0), "big", big());
1025 let mut projected = Row::new();
1026 assert!(projected.insert_named_from(VarId(1), "alias", &row, VarId(0)));
1027 assert!(!projected.insert_named_from(VarId(2), "missing", &row, VarId(5)));
1028 assert!(std::ptr::eq(
1029 row.get(VarId(0)).unwrap(),
1030 projected.get(VarId(1)).unwrap()
1031 ));
1032 assert_eq!(projected.len(), 1);
1033 let (_, name, value) = projected.into_iter_named().next().unwrap();
1034 assert_eq!(&*name, "alias");
1035 assert_eq!(value, big());
1036 assert_eq!(row.get(VarId(0)), Some(&big()));
1038 }
1039}