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<&PropertyValue> for LoraValue {
232 fn from(value: &PropertyValue) -> Self {
233 match value {
234 PropertyValue::Null => LoraValue::Null,
235 PropertyValue::Bool(v) => LoraValue::Bool(*v),
236 PropertyValue::Int(v) => LoraValue::Int(*v),
237 PropertyValue::Float(v) => LoraValue::Float(*v),
238 PropertyValue::String(v) => LoraValue::String(v.clone()),
239 PropertyValue::Binary(v) => LoraValue::Binary(v.clone()),
240 PropertyValue::List(values) => {
241 LoraValue::List(values.iter().map(LoraValue::from).collect())
242 }
243 PropertyValue::Map(map) => LoraValue::Map(
244 map.iter()
245 .map(|(k, v)| (k.clone(), LoraValue::from(v)))
246 .collect(),
247 ),
248 PropertyValue::Date(d) => LoraValue::Date(d.clone()),
249 PropertyValue::Time(t) => LoraValue::Time(t.clone()),
250 PropertyValue::LocalTime(t) => LoraValue::LocalTime(t.clone()),
251 PropertyValue::DateTime(dt) => LoraValue::DateTime(dt.clone()),
252 PropertyValue::LocalDateTime(dt) => LoraValue::LocalDateTime(dt.clone()),
253 PropertyValue::Duration(dur) => LoraValue::Duration(dur.clone()),
254 PropertyValue::Point(p) => LoraValue::Point(p.clone()),
255 PropertyValue::Vector(v) => LoraValue::Vector(v.clone()),
256 }
257 }
258}
259
260impl From<LoraValue> for PropertyValue {
261 fn from(value: LoraValue) -> Self {
262 match value {
263 LoraValue::Null => PropertyValue::Null,
264 LoraValue::Bool(v) => PropertyValue::Bool(v),
265 LoraValue::Int(v) => PropertyValue::Int(v),
266 LoraValue::Float(v) => PropertyValue::Float(v),
267 LoraValue::String(v) => PropertyValue::String(v),
268 LoraValue::Binary(v) => PropertyValue::Binary(v),
269 LoraValue::List(values) => {
270 PropertyValue::List(values.into_iter().map(PropertyValue::from).collect())
271 }
272 LoraValue::Map(map) => PropertyValue::Map(
273 map.into_iter()
274 .map(|(k, v)| (k, PropertyValue::from(v)))
275 .collect(),
276 ),
277 LoraValue::Node(id) => PropertyValue::String(format!("node:{id}")),
278 LoraValue::Relationship(id) => PropertyValue::String(format!("rel:{id}")),
279 LoraValue::Path(_) => PropertyValue::Null,
280 LoraValue::Date(d) => PropertyValue::Date(d),
281 LoraValue::Time(t) => PropertyValue::Time(t),
282 LoraValue::LocalTime(t) => PropertyValue::LocalTime(t),
283 LoraValue::DateTime(dt) => PropertyValue::DateTime(dt),
284 LoraValue::LocalDateTime(dt) => PropertyValue::LocalDateTime(dt),
285 LoraValue::Duration(dur) => PropertyValue::Duration(dur),
286 LoraValue::Point(p) => PropertyValue::Point(p),
287 LoraValue::Vector(v) => PropertyValue::Vector(v),
288 }
289 }
290}
291
292#[derive(Debug, Clone, PartialEq)]
295pub enum PropertyConversionError {
296 NestedVectorInList,
299 UnsupportedKind(&'static str),
303}
304
305impl std::fmt::Display for PropertyConversionError {
306 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
307 match self {
308 PropertyConversionError::NestedVectorInList => {
309 write!(f, "lists stored as properties cannot contain VECTOR values")
310 }
311 PropertyConversionError::UnsupportedKind(kind) => {
312 write!(f, "cannot store {kind} as a property")
313 }
314 }
315 }
316}
317
318impl std::error::Error for PropertyConversionError {}
319
320pub fn lora_value_to_property(value: LoraValue) -> Result<PropertyValue, PropertyConversionError> {
331 fn visit(value: &LoraValue, inside_list: bool) -> Result<(), PropertyConversionError> {
336 match value {
337 LoraValue::Vector(_) if inside_list => Err(PropertyConversionError::NestedVectorInList),
338 LoraValue::Node(_) => Err(PropertyConversionError::UnsupportedKind("a node")),
339 LoraValue::Relationship(_) => {
340 Err(PropertyConversionError::UnsupportedKind("a relationship"))
341 }
342 LoraValue::Path(_) => Err(PropertyConversionError::UnsupportedKind("a path")),
343 LoraValue::List(items) => {
344 for item in items {
345 visit(item, true)?;
346 }
347 Ok(())
348 }
349 LoraValue::Map(m) => {
350 for v in m.values() {
351 visit(v, inside_list)?;
352 }
353 Ok(())
354 }
355 _ => Ok(()),
356 }
357 }
358
359 visit(&value, false)?;
360 Ok(PropertyValue::from(value))
361}
362
363#[derive(Debug, Clone)]
370enum SlotValue {
371 Inline(LoraValue),
372 Shared(Arc<LoraValue>),
373}
374
375impl SlotValue {
376 #[inline]
377 fn new(value: LoraValue) -> Self {
378 let large = match &value {
381 LoraValue::String(s) => s.len() >= LARGE_BYTES,
382 LoraValue::List(_)
383 | LoraValue::Map(_)
384 | LoraValue::Path(_)
385 | LoraValue::Binary(_)
386 | LoraValue::Vector(_) => is_large(&value),
387 _ => false,
388 };
389 if large {
390 SlotValue::Shared(Arc::new(value))
391 } else {
392 SlotValue::Inline(value)
393 }
394 }
395
396 #[inline]
397 fn get(&self) -> &LoraValue {
398 match self {
399 SlotValue::Inline(v) => v,
400 SlotValue::Shared(v) => v,
401 }
402 }
403
404 #[inline]
405 fn into_value(self) -> LoraValue {
406 match self {
407 SlotValue::Inline(v) => v,
408 SlotValue::Shared(v) => Arc::try_unwrap(v).unwrap_or_else(|v| (*v).clone()),
409 }
410 }
411}
412
413const LARGE_BYTES: usize = 256;
415
416impl PartialEq for SlotValue {
417 fn eq(&self, other: &Self) -> bool {
418 self.get() == other.get()
419 }
420}
421
422fn is_large(value: &LoraValue) -> bool {
426 const ENTRIES: usize = 8;
427 let heavy = |v: &LoraValue| match v {
428 LoraValue::List(_)
429 | LoraValue::Map(_)
430 | LoraValue::Path(_)
431 | LoraValue::Vector(_)
432 | LoraValue::Binary(_) => true,
433 LoraValue::String(s) => s.len() >= LARGE_BYTES,
434 _ => false,
435 };
436 match value {
437 LoraValue::List(items) => items.len() >= ENTRIES || items.iter().any(heavy),
438 LoraValue::Map(map) => map.len() >= ENTRIES || map.values().any(heavy),
439 LoraValue::String(s) => s.len() >= LARGE_BYTES,
440 LoraValue::Path(p) => p.nodes.len() >= ENTRIES,
441 LoraValue::Binary(_) | LoraValue::Vector(_) => true,
442 _ => false,
443 }
444}
445
446#[derive(Debug, Clone, PartialEq)]
447struct RowEntry {
448 var: VarId,
452 name: Option<Arc<str>>,
459 value: SlotValue,
460}
461
462#[derive(Debug, Clone, Default, PartialEq)]
477pub struct Row {
478 entries: smallvec::SmallVec<Option<RowEntry>, 8>,
479 len_set: u32,
480}
481
482impl Serialize for Row {
483 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
484 where
485 S: Serializer,
486 {
487 let mut ser_map = serializer.serialize_map(Some(self.len()))?;
488 for entry in self.entries.iter().flatten() {
489 match &entry.name {
490 Some(name) => ser_map.serialize_entry(&**name, entry.value.get())?,
491 None => {
492 let fallback = format!("_{}", entry.var);
493 ser_map.serialize_entry(fallback.as_str(), entry.value.get())?;
494 }
495 }
496 }
497 ser_map.end()
498 }
499}
500
501impl Row {
502 pub fn new() -> Self {
503 Self::default()
504 }
505
506 #[inline]
507 pub fn get(&self, key: VarId) -> Option<&LoraValue> {
508 self.slot(key).map(SlotValue::get)
509 }
510
511 pub fn get_name(&self, key: VarId) -> Option<String> {
514 self.entries
515 .get(key.0 as usize)
516 .and_then(|slot| slot.as_ref())
517 .map(|entry| match &entry.name {
518 Some(n) => n.to_string(),
519 None => format!("_{}", entry.var),
520 })
521 }
522
523 #[inline]
524 pub fn insert(&mut self, key: VarId, value: LoraValue) {
525 self.set_value(key, SlotValue::new(value));
526 }
527
528 #[inline]
529 pub fn insert_named(&mut self, key: VarId, name: impl Into<Arc<str>>, value: LoraValue) {
530 self.set_named(key, name.into(), SlotValue::new(value));
531 }
532
533 #[inline]
537 pub fn insert_inline(&mut self, key: VarId, value: LoraValue) {
538 self.set_value(key, SlotValue::Inline(value));
539 }
540
541 pub fn insert_named_inline(&mut self, key: VarId, name: impl Into<Arc<str>>, value: LoraValue) {
545 self.set_named(key, name.into(), SlotValue::Inline(value));
546 }
547
548 pub fn insert_named_from(
552 &mut self,
553 key: VarId,
554 name: impl Into<Arc<str>>,
555 source: &Row,
556 from: VarId,
557 ) -> bool {
558 let Some(value) = source.slot(from).cloned() else {
559 return false;
560 };
561 self.set_named(key, name.into(), value);
562 true
563 }
564
565 pub fn insert_named_from_self(
567 &mut self,
568 key: VarId,
569 name: impl Into<Arc<str>>,
570 from: VarId,
571 ) -> bool {
572 let Some(value) = self.slot(from).cloned() else {
573 return false;
574 };
575 self.set_named(key, name.into(), value);
576 true
577 }
578
579 #[inline]
580 fn slot(&self, key: VarId) -> Option<&SlotValue> {
581 self.entries
582 .get(key.0 as usize)
583 .and_then(|slot| slot.as_ref())
584 .map(|entry| &entry.value)
585 }
586
587 #[inline]
590 fn set_value(&mut self, key: VarId, value: SlotValue) {
591 let idx = self.ensure_slot(key);
592 match &mut self.entries[idx] {
593 Some(existing) => existing.value = value,
594 slot @ None => {
595 *slot = Some(RowEntry {
596 var: key,
597 name: None,
598 value,
599 });
600 self.len_set += 1;
601 }
602 }
603 }
604
605 #[inline]
606 fn set_named(&mut self, key: VarId, name: Arc<str>, value: SlotValue) {
607 let idx = self.ensure_slot(key);
608 let was_set = self.entries[idx].is_some();
609 self.entries[idx] = Some(RowEntry {
610 var: key,
611 name: Some(name),
612 value,
613 });
614 if !was_set {
615 self.len_set += 1;
616 }
617 }
618
619 pub fn extend_from(&mut self, other: &Row) {
620 for entry in other.entries.iter().flatten() {
623 let idx = self.ensure_slot(entry.var);
624 let was_set = self.entries[idx].is_some();
625 self.entries[idx] = Some(entry.clone());
626 if !was_set {
627 self.len_set += 1;
628 }
629 }
630 }
631
632 pub fn fill_missing_from(&mut self, other: &Row) {
636 for entry in other.entries.iter().flatten() {
637 let idx = self.ensure_slot(entry.var);
638 if self.entries[idx].is_none() {
639 self.entries[idx] = Some(entry.clone());
640 self.len_set += 1;
641 }
642 }
643 }
644
645 pub fn iter(&self) -> impl Iterator<Item = (&VarId, &LoraValue)> {
646 self.entries
647 .iter()
648 .flatten()
649 .map(|entry| (&entry.var, entry.value.get()))
650 }
651
652 pub fn iter_named(
656 &self,
657 ) -> impl Iterator<Item = (&VarId, std::borrow::Cow<'_, str>, &LoraValue)> {
658 self.entries.iter().flatten().map(|entry| {
659 let name: std::borrow::Cow<'_, str> = match &entry.name {
660 Some(n) => std::borrow::Cow::Borrowed(&**n),
661 None => std::borrow::Cow::Owned(format!("_{}", entry.var)),
662 };
663 (&entry.var, name, entry.value.get())
664 })
665 }
666
667 pub fn into_iter_named(self) -> impl Iterator<Item = (VarId, Arc<str>, LoraValue)> {
672 self.entries.into_iter().flatten().map(|entry| {
673 let RowEntry { var, name, value } = entry;
674 (
675 var,
676 name.unwrap_or_else(|| Arc::from(format!("_{var}"))),
677 value.into_value(),
678 )
679 })
680 }
681
682 pub fn len(&self) -> usize {
683 self.len_set as usize
684 }
685
686 pub fn is_empty(&self) -> bool {
687 self.len_set == 0
688 }
689
690 pub fn contains_key(&self, key: VarId) -> bool {
691 self.entries
692 .get(key.0 as usize)
693 .is_some_and(|slot| slot.is_some())
694 }
695
696 fn ensure_slot(&mut self, key: VarId) -> usize {
699 let idx = key.0 as usize;
700 if idx >= self.entries.len() {
701 self.entries.resize_with(idx + 1, || None);
702 }
703 idx
704 }
705}
706
707#[derive(Debug, Clone, Copy, PartialEq, Eq)]
708pub enum ResultFormat {
709 Rows,
710 RowArrays,
711 Graph,
712 Combined,
713}
714
715#[derive(Debug, Clone, Copy, PartialEq, Eq)]
716pub struct ExecuteOptions {
717 pub format: ResultFormat,
718}
719
720impl Default for ExecuteOptions {
721 fn default() -> Self {
722 Self {
723 format: ResultFormat::Graph,
724 }
725 }
726}
727
728#[derive(Debug, Clone, Serialize)]
729#[serde(untagged)]
730pub enum QueryResult {
731 Rows(RowsResult),
732 RowArrays(RowArraysResult),
733 Graph(GraphResult),
734 Combined(CombinedResult),
735}
736
737#[derive(Debug, Clone, Serialize)]
738pub struct RowsResult {
739 pub rows: Vec<Row>,
740}
741
742#[derive(Debug, Clone, Serialize)]
743pub struct RowArraysResult {
744 pub columns: Vec<String>,
745 pub rows: Vec<Vec<LoraValue>>,
746}
747
748#[derive(Debug, Clone, Serialize)]
749pub struct GraphResult {
750 pub graph: HydratedGraph,
751}
752
753#[derive(Debug, Clone, Serialize)]
754pub struct CombinedResult {
755 pub columns: Vec<String>,
756 pub data: Vec<CombinedRow>,
757 pub graph: HydratedGraph,
758}
759
760#[derive(Debug, Clone, Serialize)]
761pub struct CombinedRow {
762 pub row: Vec<LoraValue>,
763}
764
765#[derive(Debug, Clone, Serialize, Default)]
766pub struct HydratedGraph {
767 pub nodes: Vec<HydratedNode>,
768 pub relationships: Vec<HydratedRelationship>,
769}
770
771#[derive(Debug, Clone, Serialize, PartialEq)]
772pub struct HydratedNode {
773 pub id: i64,
774 pub labels: Vec<String>,
775 pub properties: BTreeMap<String, LoraValue>,
776}
777
778#[derive(Debug, Clone, Serialize, PartialEq)]
779pub struct HydratedRelationship {
780 pub id: i64,
781 #[serde(rename = "startId")]
782 pub start_id: i64,
783 #[serde(rename = "endId")]
784 pub end_id: i64,
785 #[serde(rename = "type")]
786 pub rel_type: String,
787 pub properties: BTreeMap<String, LoraValue>,
788}
789
790pub fn project_rows(rows: Vec<Row>, options: ExecuteOptions) -> QueryResult {
791 match options.format {
792 ResultFormat::Rows => QueryResult::Rows(RowsResult { rows }),
793
794 ResultFormat::RowArrays => {
795 let columns = infer_columns(&rows);
796 let projected_rows = rows.iter().map(|row| row_to_array(row, &columns)).collect();
797
798 QueryResult::RowArrays(RowArraysResult {
799 columns,
800 rows: projected_rows,
801 })
802 }
803
804 ResultFormat::Graph => QueryResult::Graph(GraphResult {
805 graph: collect_hydrated_graph(&rows),
806 }),
807
808 ResultFormat::Combined => {
809 let columns = infer_columns(&rows);
810 let data = rows
811 .iter()
812 .map(|row| CombinedRow {
813 row: row_to_array(row, &columns),
814 })
815 .collect();
816
817 QueryResult::Combined(CombinedResult {
818 columns,
819 data,
820 graph: collect_hydrated_graph(&rows),
821 })
822 }
823 }
824}
825
826fn infer_columns(rows: &[Row]) -> Vec<String> {
827 rows.first()
828 .map(|row| {
829 row.iter_named()
830 .map(|(_, name, _)| name.into_owned())
831 .collect::<Vec<_>>()
832 })
833 .unwrap_or_default()
834}
835
836fn row_to_array(row: &Row, columns: &[String]) -> Vec<LoraValue> {
837 columns
840 .iter()
841 .map(|col| {
842 row.iter_named()
843 .find(|(_, name, _)| name.as_ref() == col.as_str())
844 .map(|(_, _, v)| v.clone())
845 .unwrap_or(LoraValue::Null)
846 })
847 .collect()
848}
849
850fn collect_hydrated_graph(rows: &[Row]) -> HydratedGraph {
851 let mut nodes = BTreeMap::<i64, HydratedNode>::new();
852 let mut relationships = BTreeMap::<i64, HydratedRelationship>::new();
853
854 for row in rows {
855 for (_, _, value) in row.iter_named() {
856 collect_graph_from_value(value, &mut nodes, &mut relationships);
857 }
858 }
859
860 HydratedGraph {
861 nodes: nodes.into_values().collect(),
862 relationships: relationships.into_values().collect(),
863 }
864}
865
866fn collect_graph_from_value(
867 value: &LoraValue,
868 nodes: &mut BTreeMap<i64, HydratedNode>,
869 relationships: &mut BTreeMap<i64, HydratedRelationship>,
870) {
871 match value {
872 LoraValue::List(values) => {
873 for value in values {
874 collect_graph_from_value(value, nodes, relationships);
875 }
876 }
877
878 LoraValue::Map(map) => {
879 if let Some(node) = try_as_hydrated_node(map) {
880 nodes.entry(node.id).or_insert(node);
881 return;
882 }
883
884 if let Some(rel) = try_as_hydrated_relationship(map) {
885 relationships.entry(rel.id).or_insert(rel);
886 return;
887 }
888
889 for value in map.values() {
890 collect_graph_from_value(value, nodes, relationships);
891 }
892 }
893
894 _ => {}
895 }
896}
897
898fn try_as_hydrated_node(map: &BTreeMap<String, LoraValue>) -> Option<HydratedNode> {
899 let id = match map.get("id")? {
900 LoraValue::Int(v) => *v,
901 _ => return None,
902 };
903
904 let labels = match map.get("labels")? {
905 LoraValue::List(values) => values
906 .iter()
907 .map(|v| match v {
908 LoraValue::String(s) => Some(s.clone()),
909 _ => None,
910 })
911 .collect::<Option<Vec<_>>>()?,
912 _ => return None,
913 };
914
915 let properties = match map.get("properties")? {
916 LoraValue::Map(props) => props.clone(),
917 _ => return None,
918 };
919
920 Some(HydratedNode {
921 id,
922 labels,
923 properties,
924 })
925}
926
927fn try_as_hydrated_relationship(map: &BTreeMap<String, LoraValue>) -> Option<HydratedRelationship> {
928 match map.get("kind") {
929 Some(LoraValue::String(kind)) if kind == "relationship" => {}
930 _ => return None,
931 }
932
933 let id = match map.get("id")? {
934 LoraValue::Int(v) => *v,
935 _ => return None,
936 };
937
938 let start_id = match map.get("startId").or_else(|| map.get("src"))? {
939 LoraValue::Int(v) => *v,
940 _ => return None,
941 };
942
943 let end_id = match map.get("endId").or_else(|| map.get("dst"))? {
944 LoraValue::Int(v) => *v,
945 _ => return None,
946 };
947
948 let rel_type = match map.get("type")? {
949 LoraValue::String(s) => s.clone(),
950 _ => return None,
951 };
952
953 let properties = match map.get("properties")? {
954 LoraValue::Map(props) => props.clone(),
955 _ => return None,
956 };
957
958 Some(HydratedRelationship {
959 id,
960 start_id,
961 end_id,
962 rel_type,
963 properties,
964 })
965}
966
967#[cfg(test)]
968mod tests {
969 use super::*;
970
971 fn big() -> LoraValue {
972 LoraValue::List((0..100).map(LoraValue::Int).collect())
973 }
974
975 #[test]
976 fn a_slot_is_no_larger_than_its_value() {
977 assert_eq!(
980 std::mem::size_of::<SlotValue>(),
981 std::mem::size_of::<LoraValue>()
982 );
983 }
984
985 #[test]
986 fn a_cloned_row_shares_a_large_value_and_copies_a_small_one() {
987 let mut row = Row::new();
988 row.insert(VarId(0), big());
989 row.insert(VarId(1), LoraValue::List(vec![LoraValue::Int(1)]));
990 let copy = row.clone();
991 assert!(std::ptr::eq(
992 row.get(VarId(0)).unwrap(),
993 copy.get(VarId(0)).unwrap()
994 ));
995 assert!(!std::ptr::eq(
996 row.get(VarId(1)).unwrap(),
997 copy.get(VarId(1)).unwrap()
998 ));
999 assert_eq!(row, copy);
1000 }
1001
1002 #[test]
1003 fn a_shared_value_is_handed_out_whole_by_each_owner() {
1004 let mut row = Row::new();
1005 row.insert_named(VarId(0), "big", big());
1006 let mut projected = Row::new();
1007 assert!(projected.insert_named_from(VarId(1), "alias", &row, VarId(0)));
1008 assert!(!projected.insert_named_from(VarId(2), "missing", &row, VarId(5)));
1009 assert!(std::ptr::eq(
1010 row.get(VarId(0)).unwrap(),
1011 projected.get(VarId(1)).unwrap()
1012 ));
1013 assert_eq!(projected.len(), 1);
1014 let (_, name, value) = projected.into_iter_named().next().unwrap();
1015 assert_eq!(&*name, "alias");
1016 assert_eq!(value, big());
1017 assert_eq!(row.get(VarId(0)), Some(&big()));
1019 }
1020}