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