1use linkrs_core::types::DataType;
2
3#[derive(Debug, Clone, PartialEq)]
14pub enum ColumnValues {
15 I64 { values: Vec<i64>, valid: Vec<u8> },
16 F64 { values: Vec<f64>, valid: Vec<u8> },
17 I32 { values: Vec<i32>, valid: Vec<u8> },
18 Bool { values: Vec<u8>, valid: Vec<u8> },
19 I16 { values: Vec<i16>, valid: Vec<u8> },
20 F32 { values: Vec<f32>, valid: Vec<u8> },
21 General(Vec<Option<linkrs_core::Value>>),
22}
23
24impl ColumnValues {
25 pub fn len(&self) -> usize {
27 match self {
28 ColumnValues::I64 { values, .. } => values.len(),
29 ColumnValues::F64 { values, .. } => values.len(),
30 ColumnValues::I32 { values, .. } => values.len(),
31 ColumnValues::Bool { values, .. } => values.len(),
32 ColumnValues::I16 { values, .. } => values.len(),
33 ColumnValues::F32 { values, .. } => values.len(),
34 ColumnValues::General(values) => values.len(),
35 }
36 }
37
38 pub fn is_empty(&self) -> bool {
39 self.len() == 0
40 }
41
42 pub fn value_at(&self, idx: usize) -> Option<linkrs_core::Value> {
44 match self {
45 ColumnValues::I64 { values, valid } => {
46 if valid.get(idx).copied().unwrap_or(0) == 1 {
47 values.get(idx).map(|&v| linkrs_core::Value::BigInt(v))
48 } else {
49 None
50 }
51 }
52 ColumnValues::F64 { values, valid } => {
53 if valid.get(idx).copied().unwrap_or(0) == 1 {
54 values.get(idx).map(|&v| linkrs_core::Value::Double(v))
55 } else {
56 None
57 }
58 }
59 ColumnValues::I32 { values, valid } => {
60 if valid.get(idx).copied().unwrap_or(0) == 1 {
61 values.get(idx).map(|&v| linkrs_core::Value::Int(v))
62 } else {
63 None
64 }
65 }
66 ColumnValues::Bool { values, valid } => {
67 if valid.get(idx).copied().unwrap_or(0) == 1 {
68 values.get(idx).map(|&v| linkrs_core::Value::Bool(v != 0))
69 } else {
70 None
71 }
72 }
73 ColumnValues::I16 { values, valid } => {
74 if valid.get(idx).copied().unwrap_or(0) == 1 {
75 values.get(idx).map(|&v| linkrs_core::Value::SmallInt(v))
76 } else {
77 None
78 }
79 }
80 ColumnValues::F32 { values, valid } => {
81 if valid.get(idx).copied().unwrap_or(0) == 1 {
82 values.get(idx).map(|&v| linkrs_core::Value::Float(v))
83 } else {
84 None
85 }
86 }
87 ColumnValues::General(values) => values.get(idx).cloned().flatten(),
88 }
89 }
90
91 pub fn append(&mut self, other: ColumnValues) {
96 if matches!(self, ColumnValues::General(values) if values.is_empty()) {
99 *self = other;
100 return;
101 }
102 match (self, other) {
103 (
104 ColumnValues::I64 { values, valid },
105 ColumnValues::I64 {
106 values: v2,
107 valid: v2v,
108 },
109 ) => {
110 values.extend(v2);
111 valid.extend(v2v);
112 }
113 (
114 ColumnValues::F64 { values, valid },
115 ColumnValues::F64 {
116 values: v2,
117 valid: v2v,
118 },
119 ) => {
120 values.extend(v2);
121 valid.extend(v2v);
122 }
123 (
124 ColumnValues::I32 { values, valid },
125 ColumnValues::I32 {
126 values: v2,
127 valid: v2v,
128 },
129 ) => {
130 values.extend(v2);
131 valid.extend(v2v);
132 }
133 (
134 ColumnValues::Bool { values, valid },
135 ColumnValues::Bool {
136 values: v2,
137 valid: v2v,
138 },
139 ) => {
140 values.extend(v2);
141 valid.extend(v2v);
142 }
143 (
144 ColumnValues::I16 { values, valid },
145 ColumnValues::I16 {
146 values: v2,
147 valid: v2v,
148 },
149 ) => {
150 values.extend(v2);
151 valid.extend(v2v);
152 }
153 (
154 ColumnValues::F32 { values, valid },
155 ColumnValues::F32 {
156 values: v2,
157 valid: v2v,
158 },
159 ) => {
160 values.extend(v2);
161 valid.extend(v2v);
162 }
163 (ColumnValues::General(values), ColumnValues::General(values2)) => {
164 values.extend(values2);
165 }
166 (self_col, other) => {
167 let mut general = self_col.to_general();
168 general.extend(other.to_general());
169 *self_col = ColumnValues::General(general);
170 }
171 }
172 }
173
174 pub fn append_nulls(&mut self, n: usize) {
177 match self {
178 ColumnValues::I64 { values, valid } => {
179 values.resize(values.len() + n, 0);
180 valid.resize(valid.len() + n, 0);
181 }
182 ColumnValues::F64 { values, valid } => {
183 values.resize(values.len() + n, 0.0);
184 valid.resize(valid.len() + n, 0);
185 }
186 ColumnValues::I32 { values, valid } => {
187 values.resize(values.len() + n, 0);
188 valid.resize(valid.len() + n, 0);
189 }
190 ColumnValues::Bool { values, valid } => {
191 values.resize(values.len() + n, 0);
192 valid.resize(valid.len() + n, 0);
193 }
194 ColumnValues::I16 { values, valid } => {
195 values.resize(values.len() + n, 0);
196 valid.resize(valid.len() + n, 0);
197 }
198 ColumnValues::F32 { values, valid } => {
199 values.resize(values.len() + n, 0.0);
200 valid.resize(valid.len() + n, 0);
201 }
202 ColumnValues::General(values) => {
203 values.resize(values.len() + n, None);
204 }
205 }
206 }
207
208 pub fn truncate(&mut self, n: usize) {
210 match self {
211 ColumnValues::I64 { values, valid } => {
212 values.truncate(n);
213 valid.truncate(n);
214 }
215 ColumnValues::F64 { values, valid } => {
216 values.truncate(n);
217 valid.truncate(n);
218 }
219 ColumnValues::I32 { values, valid } => {
220 values.truncate(n);
221 valid.truncate(n);
222 }
223 ColumnValues::Bool { values, valid } => {
224 values.truncate(n);
225 valid.truncate(n);
226 }
227 ColumnValues::I16 { values, valid } => {
228 values.truncate(n);
229 valid.truncate(n);
230 }
231 ColumnValues::F32 { values, valid } => {
232 values.truncate(n);
233 valid.truncate(n);
234 }
235 ColumnValues::General(values) => values.truncate(n),
236 }
237 }
238
239 pub fn compact(&mut self, keep: &[bool]) {
241 let selection: Vec<usize> = keep
242 .iter()
243 .enumerate()
244 .filter_map(|(i, &k)| if k { Some(i) } else { None })
245 .collect();
246 self.select(&selection);
247 }
248
249 pub fn select(&mut self, selection: &[usize]) {
257 match self {
258 ColumnValues::I64 { values, valid } => {
259 let mut next_values = Vec::with_capacity(selection.len());
260 let mut next_valid = Vec::with_capacity(selection.len());
261 for &i in selection {
262 next_values.push(values[i]);
263 next_valid.push(valid[i]);
264 }
265 *values = next_values;
266 *valid = next_valid;
267 }
268 ColumnValues::F64 { values, valid } => {
269 let mut next_values = Vec::with_capacity(selection.len());
270 let mut next_valid = Vec::with_capacity(selection.len());
271 for &i in selection {
272 next_values.push(values[i]);
273 next_valid.push(valid[i]);
274 }
275 *values = next_values;
276 *valid = next_valid;
277 }
278 ColumnValues::I32 { values, valid } => {
279 let mut next_values = Vec::with_capacity(selection.len());
280 let mut next_valid = Vec::with_capacity(selection.len());
281 for &i in selection {
282 next_values.push(values[i]);
283 next_valid.push(valid[i]);
284 }
285 *values = next_values;
286 *valid = next_valid;
287 }
288 ColumnValues::Bool { values, valid } => {
289 let mut next_values = Vec::with_capacity(selection.len());
290 let mut next_valid = Vec::with_capacity(selection.len());
291 for &i in selection {
292 next_values.push(values[i]);
293 next_valid.push(valid[i]);
294 }
295 *values = next_values;
296 *valid = next_valid;
297 }
298 ColumnValues::I16 { values, valid } => {
299 let mut next_values = Vec::with_capacity(selection.len());
300 let mut next_valid = Vec::with_capacity(selection.len());
301 for &i in selection {
302 next_values.push(values[i]);
303 next_valid.push(valid[i]);
304 }
305 *values = next_values;
306 *valid = next_valid;
307 }
308 ColumnValues::F32 { values, valid } => {
309 let mut next_values = Vec::with_capacity(selection.len());
310 let mut next_valid = Vec::with_capacity(selection.len());
311 for &i in selection {
312 next_values.push(values[i]);
313 next_valid.push(valid[i]);
314 }
315 *values = next_values;
316 *valid = next_valid;
317 }
318 ColumnValues::General(values) => {
319 let mut next = Vec::with_capacity(selection.len());
320 for &i in selection {
321 next.push(values[i].take());
322 }
323 *values = next;
324 }
325 }
326 }
327
328 pub fn to_general(&self) -> Vec<Option<linkrs_core::Value>> {
330 (0..self.len()).map(|i| self.value_at(i)).collect()
331 }
332
333 pub fn scatter(&self, target: &mut ColumnValues, positions: &[(usize, u32)]) {
339 for (i, &(out_idx, _)) in positions.iter().enumerate() {
340 let value = self.value_at(i);
341 if !target.set_value_at(out_idx, value.clone()) {
342 let mut general = target.to_general();
343 if out_idx < general.len() {
344 general[out_idx] = value;
345 }
346 *target = ColumnValues::General(general);
347 }
348 }
349 }
350
351 fn set_value_at(&mut self, idx: usize, value: Option<linkrs_core::Value>) -> bool {
354 match self {
355 ColumnValues::I64 { values, valid } => match value {
356 Some(linkrs_core::Value::BigInt(v)) => {
357 if idx < values.len() {
358 values[idx] = v;
359 valid[idx] = 1;
360 true
361 } else {
362 false
363 }
364 }
365 None => {
366 if idx < valid.len() {
367 valid[idx] = 0;
368 true
369 } else {
370 false
371 }
372 }
373 Some(_) => false,
374 },
375 ColumnValues::F64 { values, valid } => match value {
376 Some(linkrs_core::Value::Double(v)) => {
377 if idx < values.len() {
378 values[idx] = v;
379 valid[idx] = 1;
380 true
381 } else {
382 false
383 }
384 }
385 None => {
386 if idx < valid.len() {
387 valid[idx] = 0;
388 true
389 } else {
390 false
391 }
392 }
393 Some(_) => false,
394 },
395 ColumnValues::I32 { values, valid } => match value {
396 Some(linkrs_core::Value::Int(v)) => {
397 if idx < values.len() {
398 values[idx] = v;
399 valid[idx] = 1;
400 true
401 } else {
402 false
403 }
404 }
405 None => {
406 if idx < valid.len() {
407 valid[idx] = 0;
408 true
409 } else {
410 false
411 }
412 }
413 Some(_) => false,
414 },
415 ColumnValues::Bool { values, valid } => match value {
416 Some(linkrs_core::Value::Bool(v)) => {
417 if idx < values.len() {
418 values[idx] = u8::from(v);
419 valid[idx] = 1;
420 true
421 } else {
422 false
423 }
424 }
425 None => {
426 if idx < valid.len() {
427 valid[idx] = 0;
428 true
429 } else {
430 false
431 }
432 }
433 Some(_) => false,
434 },
435 ColumnValues::I16 { values, valid } => match value {
436 Some(linkrs_core::Value::SmallInt(v)) => {
437 if idx < values.len() {
438 values[idx] = v;
439 valid[idx] = 1;
440 true
441 } else {
442 false
443 }
444 }
445 None => {
446 if idx < valid.len() {
447 valid[idx] = 0;
448 true
449 } else {
450 false
451 }
452 }
453 Some(_) => false,
454 },
455 ColumnValues::F32 { values, valid } => match value {
456 Some(linkrs_core::Value::Float(v)) => {
457 if idx < values.len() {
458 values[idx] = v;
459 valid[idx] = 1;
460 true
461 } else {
462 false
463 }
464 }
465 None => {
466 if idx < valid.len() {
467 valid[idx] = 0;
468 true
469 } else {
470 false
471 }
472 }
473 Some(_) => false,
474 },
475 ColumnValues::General(target_rows) => {
476 if idx < target_rows.len() {
477 target_rows[idx] = value;
478 true
479 } else {
480 false
481 }
482 }
483 }
484 }
485
486 pub fn from_general_with_type(
490 values: Vec<Option<linkrs_core::Value>>,
491 data_type: &DataType,
492 ) -> Option<ColumnValues> {
493 match data_type {
494 DataType::BigInt => {
495 let mut vs = Vec::with_capacity(values.len());
496 let mut valid = vec![0u8; values.len()];
497 for (i, value) in values.into_iter().enumerate() {
498 match value {
499 Some(linkrs_core::Value::BigInt(v)) => {
500 vs.push(v);
501 valid[i] = 1;
502 }
503 None => vs.push(0),
504 Some(_) => return None,
505 }
506 }
507 Some(ColumnValues::I64 { values: vs, valid })
508 }
509 DataType::Double => {
510 let mut vs = Vec::with_capacity(values.len());
511 let mut valid = vec![0u8; values.len()];
512 for (i, value) in values.into_iter().enumerate() {
513 match value {
514 Some(linkrs_core::Value::Double(v)) => {
515 vs.push(v);
516 valid[i] = 1;
517 }
518 None => vs.push(0.0),
519 Some(_) => return None,
520 }
521 }
522 Some(ColumnValues::F64 { values: vs, valid })
523 }
524 DataType::Int => {
525 let mut vs = Vec::with_capacity(values.len());
526 let mut valid = vec![0u8; values.len()];
527 for (i, value) in values.into_iter().enumerate() {
528 match value {
529 Some(linkrs_core::Value::Int(v)) => {
530 vs.push(v);
531 valid[i] = 1;
532 }
533 None => vs.push(0),
534 Some(_) => return None,
535 }
536 }
537 Some(ColumnValues::I32 { values: vs, valid })
538 }
539 DataType::Bool => {
540 let mut vs = Vec::with_capacity(values.len());
541 let mut valid = vec![0u8; values.len()];
542 for (i, value) in values.into_iter().enumerate() {
543 match value {
544 Some(linkrs_core::Value::Bool(v)) => {
545 vs.push(u8::from(v));
546 valid[i] = 1;
547 }
548 None => vs.push(0),
549 Some(_) => return None,
550 }
551 }
552 Some(ColumnValues::Bool { values: vs, valid })
553 }
554 DataType::SmallInt => {
555 let mut vs = Vec::with_capacity(values.len());
556 let mut valid = vec![0u8; values.len()];
557 for (i, value) in values.into_iter().enumerate() {
558 match value {
559 Some(linkrs_core::Value::SmallInt(v)) => {
560 vs.push(v);
561 valid[i] = 1;
562 }
563 None => vs.push(0),
564 Some(_) => return None,
565 }
566 }
567 Some(ColumnValues::I16 { values: vs, valid })
568 }
569 DataType::Float => {
570 let mut vs = Vec::with_capacity(values.len());
571 let mut valid = vec![0u8; values.len()];
572 for (i, value) in values.into_iter().enumerate() {
573 match value {
574 Some(linkrs_core::Value::Float(v)) => {
575 vs.push(v);
576 valid[i] = 1;
577 }
578 None => vs.push(0.0),
579 Some(_) => return None,
580 }
581 }
582 Some(ColumnValues::F32 { values: vs, valid })
583 }
584 _ => None,
585 }
586 }
587
588 pub fn all_valid(&self) -> bool {
591 match self {
592 ColumnValues::I64 { valid, .. }
593 | ColumnValues::F64 { valid, .. }
594 | ColumnValues::I32 { valid, .. }
595 | ColumnValues::Bool { valid, .. }
596 | ColumnValues::I16 { valid, .. }
597 | ColumnValues::F32 { valid, .. } => valid.iter().all(|&v| v == 1),
598 ColumnValues::General(values) => values.iter().all(|v| v.is_some()),
599 }
600 }
601}
602
603#[derive(Debug, Clone, PartialEq)]
605pub struct PropertyColumn {
606 pub name: std::sync::Arc<str>,
607 pub data_type: DataType,
608 pub values: ColumnValues,
609}
610
611#[derive(Debug, Clone, PartialEq)]
618pub struct VertexColumnBatch {
619 pub vids: Vec<linkrs_core::types::VertexId>,
620 pub internal_ids: Vec<i64>,
621 pub tag_names: Vec<String>,
623 pub columns: Vec<PropertyColumn>,
624}
625
626impl VertexColumnBatch {
627 pub fn empty() -> Self {
628 Self {
629 vids: Vec::new(),
630 internal_ids: Vec::new(),
631 tag_names: Vec::new(),
632 columns: Vec::new(),
633 }
634 }
635
636 pub fn is_empty(&self) -> bool {
637 self.vids.is_empty()
638 }
639
640 pub fn len(&self) -> usize {
641 self.vids.len()
642 }
643
644 pub fn select(&mut self, selection: &[usize]) {
646 let gather_ids = selection.iter().map(|&i| self.vids[i]).collect::<Vec<_>>();
647 let gather_internal = selection
648 .iter()
649 .map(|&i| self.internal_ids[i])
650 .collect::<Vec<_>>();
651 let gather_tags = selection
652 .iter()
653 .map(|&i| self.tag_names[i].clone())
654 .collect::<Vec<_>>();
655 self.vids = gather_ids;
656 self.internal_ids = gather_internal;
657 self.tag_names = gather_tags;
658 for column in &mut self.columns {
659 column.values.select(selection);
660 }
661 }
662}
663#[derive(Debug, Clone, PartialEq)]
669pub struct EdgeColumnBatch {
670 pub srcs: Vec<linkrs_core::types::VertexId>,
671 pub dsts: Vec<linkrs_core::types::VertexId>,
672 pub edge_types: Vec<String>,
673 pub rankings: Vec<i64>,
674 pub columns: Vec<PropertyColumn>,
675}
676
677impl EdgeColumnBatch {
678 pub fn empty() -> Self {
679 Self {
680 srcs: Vec::new(),
681 dsts: Vec::new(),
682 edge_types: Vec::new(),
683 rankings: Vec::new(),
684 columns: Vec::new(),
685 }
686 }
687
688 pub fn is_empty(&self) -> bool {
689 self.srcs.is_empty()
690 }
691
692 pub fn len(&self) -> usize {
693 self.srcs.len()
694 }
695
696 pub fn select(&mut self, selection: &[usize]) {
698 let gather_srcs = selection.iter().map(|&i| self.srcs[i]).collect::<Vec<_>>();
699 let gather_dsts = selection.iter().map(|&i| self.dsts[i]).collect::<Vec<_>>();
700 let gather_types = selection
701 .iter()
702 .map(|&i| self.edge_types[i].clone())
703 .collect::<Vec<_>>();
704 let gather_ranks = selection
705 .iter()
706 .map(|&i| self.rankings[i])
707 .collect::<Vec<_>>();
708 self.srcs = gather_srcs;
709 self.dsts = gather_dsts;
710 self.edge_types = gather_types;
711 self.rankings = gather_ranks;
712 for column in &mut self.columns {
713 column.values.select(selection);
714 }
715 }
716}
717
718#[cfg(test)]
719mod tests {
720 use super::*;
721
722 #[test]
723 fn new_typed_kinds_roundtrip_values() {
724 let b = ColumnValues::Bool {
725 values: vec![1, 0, 1],
726 valid: vec![1, 1, 0],
727 };
728 assert_eq!(b.value_at(0), Some(linkrs_core::Value::Bool(true)));
729 assert_eq!(b.value_at(1), Some(linkrs_core::Value::Bool(false)));
730 assert_eq!(b.value_at(2), None);
731
732 let mut with_null = b.clone();
733 with_null.set_value_at(0, None);
734 assert_eq!(with_null.value_at(0), None);
735 assert!(!with_null.all_valid());
736
737 let s = ColumnValues::I16 {
738 values: vec![7, -3],
739 valid: vec![1, 1],
740 };
741 assert_eq!(s.value_at(0), Some(linkrs_core::Value::SmallInt(7)));
742 assert_eq!(s.value_at(1), Some(linkrs_core::Value::SmallInt(-3)));
743
744 let f = ColumnValues::F32 {
745 values: vec![1.5, 0.0],
746 valid: vec![1, 0],
747 };
748 assert_eq!(f.value_at(0), Some(linkrs_core::Value::Float(1.5)));
749 assert_eq!(f.value_at(1), None);
750 }
751
752 #[test]
753 fn from_general_with_type_covers_new_kinds() {
754 let bools = vec![
755 Some(linkrs_core::Value::Bool(true)),
756 None,
757 Some(linkrs_core::Value::Bool(false)),
758 ];
759 let typed = ColumnValues::from_general_with_type(bools, &DataType::Bool).unwrap();
760 assert!(matches!(typed, ColumnValues::Bool { .. }));
761 assert_eq!(typed.len(), 3);
762
763 let smalls = vec![Some(linkrs_core::Value::SmallInt(2)), None];
764 let typed = ColumnValues::from_general_with_type(smalls, &DataType::SmallInt).unwrap();
765 assert!(matches!(typed, ColumnValues::I16 { .. }));
766
767 let floats = vec![Some(linkrs_core::Value::Float(2.5)), None];
768 let typed = ColumnValues::from_general_with_type(floats, &DataType::Float).unwrap();
769 assert!(matches!(typed, ColumnValues::F32 { .. }));
770
771 let mixed = vec![Some(linkrs_core::Value::Int(1))];
772 assert!(ColumnValues::from_general_with_type(mixed, &DataType::Bool).is_none());
773 }
774
775 #[test]
776 fn scatter_into_typed_target_writes_directly() {
777 let src = ColumnValues::I16 {
778 values: vec![9, 8],
779 valid: vec![1, 1],
780 };
781 let mut target = ColumnValues::I16 {
782 values: vec![0; 4],
783 valid: vec![0; 4],
784 };
785 src.scatter(&mut target, &[(1, 0), (3, 0)]);
786 assert_eq!(target.value_at(0), None);
787 assert_eq!(target.value_at(1), Some(linkrs_core::Value::SmallInt(9)));
788 assert_eq!(target.value_at(2), None);
789 assert_eq!(target.value_at(3), Some(linkrs_core::Value::SmallInt(8)));
790 }
791
792 #[test]
793 fn scatter_kind_mismatch_degrades_to_general() {
794 let src = ColumnValues::I16 {
795 values: vec![9],
796 valid: vec![1],
797 };
798 let mut target = ColumnValues::I32 {
799 values: vec![0; 2],
800 valid: vec![0; 2],
801 };
802 src.scatter(&mut target, &[(0, 0)]);
803 assert!(matches!(target, ColumnValues::General(_)));
804 assert_eq!(target.value_at(0), Some(linkrs_core::Value::SmallInt(9)));
805 assert_eq!(target.value_at(1), None);
806 }
807
808 #[test]
809 fn append_and_compact_cover_new_kinds() {
810 let mut a = ColumnValues::Bool {
811 values: vec![1],
812 valid: vec![1],
813 };
814 a.append(ColumnValues::Bool {
815 values: vec![0],
816 valid: vec![0],
817 });
818 assert_eq!(a.len(), 2);
819 assert_eq!(a.value_at(1), None);
820
821 let mut f = ColumnValues::F32 {
822 values: vec![1.0, 2.0, 3.0],
823 valid: vec![1, 1, 1],
824 };
825 f.compact(&[true, false, true]);
826 assert_eq!(f.len(), 2);
827 assert_eq!(f.value_at(1), Some(linkrs_core::Value::Float(3.0)));
828
829 let mut s = ColumnValues::I16 {
830 values: vec![1],
831 valid: vec![1],
832 };
833 s.append_nulls(2);
834 assert_eq!(s.len(), 3);
835 assert_eq!(s.value_at(2), None);
836 s.truncate(1);
837 assert_eq!(s.len(), 1);
838 }
839}