pub struct ReadBatch { /* private fields */ }Expand description
A batch of read-only queries for sequential execution in one transaction
This allows multiple related read queries to be executed atomically, with results stored in named variables that can be referenced by subsequent queries and returned as a structured result.
Important: ReadBatch only accepts read-only traversals (no mutations). Attempting to add a traversal containing mutation steps will fail at compile time.
Implementations§
Source§impl ReadBatch
impl ReadBatch
Sourcepub fn var_as<S: TraversalState>(
self,
name: &str,
traversal: Traversal<S, ReadOnly>,
) -> Self
pub fn var_as<S: TraversalState>( self, name: &str, traversal: Traversal<S, ReadOnly>, ) -> Self
Add a read-only query that stores result in a named variable
The traversal is executed and its result is stored in a variable
that can be referenced by subsequent queries using NodeRef::var().
Note: Only accepts read-only traversals. Mutation traversals will fail at compile time.
Examples found in repository?
159fn runtime_fixtures() -> Vec<Fixture> {
160 vec![
161 runtime(
162 "001-write-seed-core",
163 write_request(
164 write_batch()
165 .var_as(
166 "alice",
167 g().add_n(
168 "ParityUser",
169 user_props(
170 "user-alice",
171 "Alice",
172 31,
173 90.5,
174 "active",
175 "London",
176 "Alice writes graph database tests",
177 vec![1.0, 0.0, 0.0],
178 ),
179 ),
180 )
181 .var_as(
182 "bob",
183 g().add_n(
184 "ParityUser",
185 user_props(
186 "user-bob",
187 "Bob",
188 27,
189 72.25,
190 "active",
191 "Paris",
192 "Bob likes traversal testing",
193 vec![0.9, 0.1, 0.0],
194 ),
195 ),
196 )
197 .var_as(
198 "carol",
199 g().add_n(
200 "ParityUser",
201 user_props(
202 "user-carol",
203 "Carol",
204 42,
205 64.0,
206 "inactive",
207 "Berlin",
208 "Carol archives old records",
209 vec![0.0, 1.0, 0.0],
210 ),
211 ),
212 )
213 .var_as(
214 "alice_follows_bob",
215 g().n(NodeRef::var("alice")).add_e(
216 "FOLLOWS",
217 NodeRef::var("bob"),
218 vec![
219 ("weight", PropertyInput::from(1.0f64)),
220 ("since", PropertyInput::from("2024-01-01")),
221 ("note", PropertyInput::from("Alice follows Bob")),
222 (
223 "embedding",
224 PropertyInput::from(PropertyValue::from(vec![1.0f32, 0.0])),
225 ),
226 ],
227 ),
228 )
229 .var_as(
230 "bob_follows_carol",
231 g().n(NodeRef::var("bob")).add_e(
232 "FOLLOWS",
233 NodeRef::var("carol"),
234 vec![
235 ("weight", PropertyInput::from(0.5f64)),
236 ("since", PropertyInput::from("2024-02-01")),
237 ("note", PropertyInput::from("Bob follows Carol")),
238 (
239 "embedding",
240 PropertyInput::from(PropertyValue::from(vec![0.0f32, 1.0])),
241 ),
242 ],
243 ),
244 )
245 .returning([
246 "alice",
247 "bob",
248 "carol",
249 "alice_follows_bob",
250 "bob_follows_carol",
251 ]),
252 ),
253 ),
254 runtime(
255 "002-read-count-all-users",
256 read_request(
257 read_batch()
258 .var_as("user_count", g().n_with_label("ParityUser").count())
259 .returning(["user_count"]),
260 ),
261 ),
262 runtime(
263 "003-read-source-predicate-and-count",
264 read_request(
265 read_batch()
266 .var_as(
267 "active_adults",
268 g().n_with_label_where(
269 "ParityUser",
270 SourcePredicate::and(vec![
271 SourcePredicate::eq("status", "active"),
272 SourcePredicate::gte("age", 30i64),
273 ]),
274 )
275 .count(),
276 )
277 .returning(["active_adults"]),
278 ),
279 ),
280 runtime(
281 "004-read-value-map-projection",
282 read_request(
283 read_batch()
284 .var_as(
285 "alice",
286 g().n_with_label("ParityUser")
287 .where_(Predicate::eq("externalId", "user-alice"))
288 .project(vec![
289 Projection::property("externalId", "id"),
290 Projection::property("name", "name"),
291 Projection::expr(
292 "score_plus_one",
293 Expr::prop("score").add(Expr::val(1.0f64)),
294 ),
295 Projection::expr(
296 "status_label",
297 Expr::case(
298 vec![(
299 Predicate::eq("status", "active"),
300 Expr::val("enabled"),
301 )],
302 Some(Expr::val("disabled")),
303 ),
304 ),
305 ]),
306 )
307 .returning(["alice"]),
308 ),
309 ),
310 runtime(
311 "005-read-order-range-values",
312 read_request(
313 read_batch()
314 .var_as(
315 "ordered",
316 g().n_with_label("ParityUser")
317 .order_by_multiple(vec![("status", Order::Asc), ("age", Order::Desc)])
318 .range(0usize, 2usize)
319 .value_map(Some(vec!["externalId", "age", "status"])),
320 )
321 .returning(["ordered"]),
322 ),
323 ),
324 runtime(
325 "006-read-edge-count",
326 read_request(
327 read_batch()
328 .var_as(
329 "edge_count",
330 g().n_with_label("ParityUser")
331 .where_(Predicate::eq("externalId", "user-alice"))
332 .out_e(Some("FOLLOWS"))
333 .count(),
334 )
335 .returning(["edge_count"]),
336 ),
337 ),
338 runtime(
339 "007-read-edge-properties",
340 read_request(
341 read_batch()
342 .var_as(
343 "edges",
344 g().e_with_label("FOLLOWS")
345 .edge_has("weight", PropertyInput::from(1.0f64))
346 .edge_properties(),
347 )
348 .returning(["edges"]),
349 ),
350 ),
351 runtime(
352 "008-read-edge-endpoints",
353 read_request(
354 read_batch()
355 .var_as(
356 "from_nodes",
357 g().e_with_label("FOLLOWS")
358 .edge_has_label("FOLLOWS")
359 .in_n()
360 .value_map(Some(vec!["externalId", "name"])),
361 )
362 .var_as(
363 "to_nodes",
364 g().e_with_label("FOLLOWS")
365 .out_n()
366 .value_map(Some(vec!["externalId", "name"])),
367 )
368 .returning(["from_nodes", "to_nodes"]),
369 ),
370 ),
371 runtime(
372 "009-read-conditional-var-not-empty",
373 read_request(
374 read_batch()
375 .var_as(
376 "alice",
377 g().n_with_label("ParityUser")
378 .where_(Predicate::eq("externalId", "user-alice")),
379 )
380 .var_as_if(
381 "friends",
382 BatchCondition::VarNotEmpty("alice".to_string()),
383 g().n(NodeRef::var("alice"))
384 .out(Some("FOLLOWS"))
385 .value_map(Some(vec!["externalId", "name"])),
386 )
387 .returning(["alice", "friends"]),
388 ),
389 ),
390 runtime(
391 "010-read-conditional-var-empty",
392 read_request(
393 read_batch()
394 .var_as(
395 "missing",
396 g().n_with_label("ParityUser")
397 .where_(Predicate::eq("externalId", "missing-user")),
398 )
399 .var_as_if(
400 "fallback",
401 BatchCondition::VarEmpty("missing".to_string()),
402 g().n_with_label("ParityUser")
403 .limit(1usize)
404 .value_map(Some(vec!["externalId"])),
405 )
406 .returning(["missing", "fallback"]),
407 ),
408 ),
409 runtime(
410 "011-read-conditional-var-min-size-prev",
411 read_request(
412 read_batch()
413 .var_as("users", g().n_with_label("ParityUser").limit(3usize))
414 .var_as_if(
415 "min_two",
416 BatchCondition::VarMinSize("users".to_string(), 2),
417 g().n(NodeRef::var("users")).count(),
418 )
419 .var_as_if(
420 "prev_ok",
421 BatchCondition::PrevNotEmpty,
422 g().n(NodeRef::var("users")).exists(),
423 )
424 .returning(["min_two", "prev_ok"]),
425 ),
426 ),
427 runtime(
428 "012-read-foreach-param",
429 with_params(
430 read_request(
431 read_batch()
432 .for_each_param(
433 "lookups",
434 read_batch().var_as(
435 "matched",
436 g().n_with_label("ParityUser")
437 .where_(Predicate::eq_param("externalId", "externalId"))
438 .value_map(Some(vec!["externalId", "name"])),
439 ),
440 )
441 .returning(["matched"]),
442 ),
443 vec![(
444 "lookups",
445 array(vec![
446 object(vec![("externalId", string("user-alice"))]),
447 object(vec![("externalId", string("user-carol"))]),
448 ]),
449 )],
450 vec![(
451 "lookups",
452 QueryParamType::Array(Box::new(QueryParamType::Object)),
453 )],
454 ),
455 ),
456 runtime(
457 "013-write-foreach-param-create",
458 with_params(
459 write_request(
460 write_batch()
461 .for_each_param(
462 "rows",
463 write_batch().var_as(
464 "created",
465 g().add_n(
466 "ParityEvent",
467 vec![
468 ("eventId", PropertyInput::param("eventId")),
469 ("kind", PropertyInput::param("kind")),
470 ("score", PropertyInput::param("score")),
471 ],
472 ),
473 ),
474 )
475 .returning(["created"]),
476 ),
477 vec![(
478 "rows",
479 array(vec![
480 object(vec![
481 ("eventId", string("event-1")),
482 ("kind", string("click")),
483 ("score", i64_value(10)),
484 ]),
485 object(vec![
486 ("eventId", string("event-2")),
487 ("kind", string("view")),
488 ("score", i64_value(5)),
489 ]),
490 ]),
491 )],
492 vec![(
493 "rows",
494 QueryParamType::Array(Box::new(QueryParamType::Object)),
495 )],
496 ),
497 ),
498 runtime(
499 "014-read-after-foreach-param",
500 read_request(
501 read_batch()
502 .var_as("event_count", g().n_with_label("ParityEvent").count())
503 .returning(["event_count"]),
504 ),
505 ),
506 runtime(
507 "015-write-set-remove-properties",
508 write_request(
509 write_batch()
510 .var_as(
511 "updated",
512 g().n_with_label("ParityUser")
513 .where_(Predicate::eq("externalId", "user-bob"))
514 .set_property("status", PropertyInput::from("inactive"))
515 .set_property(
516 "updatedAt",
517 PropertyInput::from(DateTime::from_millis(1_777_000_000_000)),
518 )
519 .remove_property("city")
520 .count(),
521 )
522 .returning(["updated"]),
523 ),
524 ),
525 runtime(
526 "016-read-updated-properties",
527 read_request(
528 read_batch()
529 .var_as(
530 "bob",
531 g().n_with_label("ParityUser")
532 .where_(Predicate::eq("externalId", "user-bob"))
533 .value_map(Some(vec!["externalId", "status", "updatedAt", "city"])),
534 )
535 .returning(["bob"]),
536 ),
537 ),
538 runtime(
539 "017-read-repeat-union",
540 read_request(
541 read_batch()
542 .var_as(
543 "walked",
544 g().n_with_label("ParityUser")
545 .where_(Predicate::eq("externalId", "user-alice"))
546 .repeat(
547 RepeatConfig::new(sub().out(Some("FOLLOWS")))
548 .times(2)
549 .emit_all()
550 .max_depth(4),
551 )
552 .union(vec![sub().out(Some("FOLLOWS")), sub().in_(Some("FOLLOWS"))])
553 .dedup()
554 .value_map(Some(vec!["externalId", "name"])),
555 )
556 .returning(["walked"]),
557 ),
558 ),
559 runtime(
560 "018-read-choose-coalesce-optional",
561 read_request(
562 read_batch()
563 .var_as(
564 "branched",
565 g().n_with_label("ParityUser")
566 .where_(Predicate::eq("externalId", "user-alice"))
567 .choose(
568 Predicate::eq("status", "active"),
569 sub().out(Some("FOLLOWS")),
570 Some(sub().in_(Some("FOLLOWS"))),
571 )
572 .coalesce(vec![sub().out(Some("FOLLOWS")), sub().in_(Some("FOLLOWS"))])
573 .optional(sub().out(Some("FOLLOWS")))
574 .dedup()
575 .value_map(Some(vec!["externalId", "name"])),
576 )
577 .returning(["branched"]),
578 ),
579 ),
580 runtime(
581 "019-read-aggregations",
582 read_request(
583 read_batch()
584 .var_as(
585 "by_status",
586 g().n_with_label("ParityUser").group_count("status"),
587 )
588 .var_as(
589 "mean_score",
590 g().n_with_label("ParityUser")
591 .aggregate_by(AggregateFunction::Mean, "score"),
592 )
593 .var_as(
594 "max_age",
595 g().n_with_label("ParityUser")
596 .aggregate_by(AggregateFunction::Max, "age"),
597 )
598 .returning(["by_status", "mean_score", "max_age"]),
599 ),
600 ),
601 runtime(
602 "020-write-index-create",
603 write_request(
604 write_batch()
605 .var_as(
606 "node_eq",
607 g().create_index_if_not_exists(IndexSpec::node_equality(
608 "ParityUser",
609 "externalId",
610 )),
611 )
612 .var_as(
613 "node_range",
614 g().create_index_if_not_exists(IndexSpec::node_range("ParityUser", "age")),
615 )
616 .var_as(
617 "edge_eq",
618 g().create_index_if_not_exists(IndexSpec::edge_equality(
619 "FOLLOWS", "since",
620 )),
621 )
622 .var_as(
623 "edge_range",
624 g().create_index_if_not_exists(IndexSpec::edge_range("FOLLOWS", "weight")),
625 )
626 .returning(["node_eq", "node_range", "edge_eq", "edge_range"]),
627 ),
628 ),
629 runtime(
630 "021-read-parameter-types",
631 with_params(
632 read_request(
633 read_batch()
634 .var_as(
635 "matches",
636 g().n_with_label("ParityUser")
637 .where_(Predicate::is_in_param("status", "statuses"))
638 .where_(Predicate::gte_param("createdAt", "created_after"))
639 .limit(Expr::param("limit"))
640 .value_map(Some(vec!["externalId", "status"])),
641 )
642 .returning(["matches"]),
643 ),
644 vec![
645 (
646 "statuses",
647 array(vec![string("active"), string("inactive")]),
648 ),
649 ("created_after", string("2026-01-01T00:00:00.000Z")),
650 ("limit", i64_value(5)),
651 ],
652 vec![
653 (
654 "statuses",
655 QueryParamType::Array(Box::new(QueryParamType::String)),
656 ),
657 ("created_after", QueryParamType::DateTime),
658 ("limit", QueryParamType::I64),
659 ],
660 ),
661 ),
662 runtime(
663 "022-write-property-value-variants",
664 write_request(
665 write_batch()
666 .var_as(
667 "variant_node",
668 g().add_n(
669 "ParityVariant",
670 vec![
671 ("nullValue", PropertyInput::from(PropertyValue::Null)),
672 ("boolValue", PropertyInput::from(true)),
673 (
674 "i64Value",
675 PropertyInput::from(9_223_372_036_854_775_000i64),
676 ),
677 (
678 "dateTimeValue",
679 PropertyInput::from(DateTime::from_millis(-1)),
680 ),
681 ("f64Value", PropertyInput::from(3.25f64)),
682 ("f32Value", PropertyInput::from(1.5f32)),
683 ("stringValue", PropertyInput::from("variant")),
684 (
685 "bytesValue",
686 PropertyInput::from(PropertyValue::from(vec![1u8, 2u8, 3u8])),
687 ),
688 (
689 "i64Array",
690 PropertyInput::from(PropertyValue::from(vec![
691 1i64, 2i64, 3i64,
692 ])),
693 ),
694 (
695 "f64Array",
696 PropertyInput::from(PropertyValue::from(vec![1.0f64, 2.0f64])),
697 ),
698 (
699 "f32Array",
700 PropertyInput::from(PropertyValue::from(vec![1.0f32, 2.0f32])),
701 ),
702 (
703 "stringArray",
704 PropertyInput::from(PropertyValue::from(vec![
705 "a".to_string(),
706 "b".to_string(),
707 ])),
708 ),
709 ],
710 ),
711 )
712 .returning(["variant_node"]),
713 ),
714 ),
715 runtime(
716 "023-read-property-value-variants",
717 read_request(
718 read_batch()
719 .var_as(
720 "variant",
721 g().n_with_label("ParityVariant")
722 .value_map(None::<Vec<&str>>),
723 )
724 .returning(["variant"]),
725 ),
726 ),
727 runtime(
728 "024-write-text-vector-indexes",
729 write_request(
730 write_batch()
731 .var_as(
732 "node_text",
733 g().create_text_index_nodes("ParityUser", "bio", None::<&str>),
734 )
735 .var_as(
736 "node_vector",
737 g().create_vector_index_nodes("ParityUser", "embedding", None::<&str>),
738 )
739 .var_as(
740 "edge_text",
741 g().create_text_index_edges("FOLLOWS", "note", None::<&str>),
742 )
743 .var_as(
744 "edge_vector",
745 g().create_vector_index_edges("FOLLOWS", "embedding", None::<&str>),
746 )
747 .returning(["node_text", "node_vector", "edge_text", "edge_vector"]),
748 ),
749 ),
750 runtime(
751 "025-read-text-search-nodes",
752 read_request(
753 read_batch()
754 .var_as(
755 "text_hits",
756 g().text_search_nodes("ParityUser", "bio", "graph", 5, None)
757 .value_map(Some(vec!["externalId", "bio", "$distance"])),
758 )
759 .returning(["text_hits"]),
760 ),
761 ),
762 runtime(
763 "026-read-vector-search-nodes",
764 read_request(
765 read_batch()
766 .var_as(
767 "vector_hits",
768 g().vector_search_nodes(
769 "ParityUser",
770 "embedding",
771 vec![1.0, 0.0, 0.0],
772 3,
773 None,
774 )
775 .project(vec![
776 Projection::property("externalId", "externalId"),
777 Projection::property("$distance", "distance"),
778 ]),
779 )
780 .returning(["vector_hits"]),
781 ),
782 ),
783 runtime(
784 "027-read-text-search-edges",
785 read_request(
786 read_batch()
787 .var_as(
788 "edge_text_hits",
789 g().text_search_edges("FOLLOWS", "note", "follows", 5, None)
790 .edge_properties(),
791 )
792 .returning(["edge_text_hits"]),
793 ),
794 ),
795 runtime(
796 "028-read-vector-search-edges",
797 read_request(
798 read_batch()
799 .var_as(
800 "edge_vector_hits",
801 g().vector_search_edges("FOLLOWS", "embedding", vec![1.0, 0.0], 5, None)
802 .edge_properties(),
803 )
804 .returning(["edge_vector_hits"]),
805 ),
806 ),
807 runtime(
808 "029-write-drop-temp-node",
809 write_request(
810 write_batch()
811 .var_as(
812 "temp",
813 g().add_n("ParityTemp", vec![("name", PropertyInput::from("temp"))]),
814 )
815 .var_as("dropped", g().n(NodeRef::var("temp")).drop().count())
816 .returning(["dropped"]),
817 ),
818 ),
819 runtime(
820 "030-read-final-counts",
821 read_request(
822 read_batch()
823 .var_as("users", g().n_with_label("ParityUser").count())
824 .var_as("events", g().n_with_label("ParityEvent").count())
825 .var_as("variants", g().n_with_label("ParityVariant").count())
826 .returning(["users", "events", "variants"]),
827 ),
828 ),
829 runtime(
830 "031-read-source-predicate-eq-param",
831 with_params(
832 read_request(
833 read_batch()
834 .var_as(
835 "user",
836 g().n_where(SourcePredicate::and(vec![
837 SourcePredicate::eq("$label", "ParityUser"),
838 SourcePredicate::eq("name", Expr::param("name")),
839 ]))
840 .value_map(Some(vec!["externalId", "name"])),
841 )
842 .returning(["user"]),
843 ),
844 vec![("name", string("Alice"))],
845 vec![("name", QueryParamType::String)],
846 ),
847 ),
848 runtime(
849 "032-read-source-predicate-between-param",
850 with_params(
851 read_request(
852 read_batch()
853 .var_as(
854 "adults",
855 g().n_where(SourcePredicate::and(vec![
856 SourcePredicate::eq("$label", "ParityUser"),
857 SourcePredicate::between("age", Expr::param("min_age"), 65i64),
858 ]))
859 .value_map(Some(vec!["externalId", "age"])),
860 )
861 .returning(["adults"]),
862 ),
863 vec![("min_age", i64_value(30))],
864 vec![("min_age", QueryParamType::I64)],
865 ),
866 ),
867 ]
868}
869
870fn node_permutation_fixtures() -> Vec<Fixture> {
871 let sources = ["label", "where", "all"];
872 let filters = ["none", "has", "logic", "expr"];
873 let bounds = ["none", "limit", "skip", "range"];
874 let terminals = ["count", "exists", "value_map", "project"];
875
876 let mut fixtures = Vec::new();
877 let mut index = 100;
878 for source in sources {
879 for filter in filters {
880 for bound in bounds {
881 for terminal in terminals {
882 let name =
883 format!("{index:03}-combo-node-{source}-{filter}-{bound}-{terminal}");
884 index += 1;
885 fixtures.push(runtime(
886 name,
887 read_request(node_combo_batch(source, filter, bound, terminal)),
888 ));
889 }
890 }
891 }
892 }
893 fixtures
894}
895
896fn node_combo_batch(source: &str, filter: &str, bound: &str, terminal: &str) -> ReadBatch {
897 let traversal = apply_node_bound(apply_node_filter(node_source(source), filter), bound)
898 .order_by("externalId", Order::Asc);
899 let traversal = match terminal {
900 "count" => traversal.count(),
901 "exists" => traversal.exists(),
902 "value_map" => traversal.value_map(Some(vec!["externalId", "name", "age", "status"])),
903 "project" => traversal.project(vec![
904 Projection::property("externalId", "externalId"),
905 Projection::property("status", "status"),
906 Projection::expr("age_plus_two", Expr::prop("age").add(Expr::val(2i64))),
907 ]),
908 other => panic!("unknown terminal {other}"),
909 };
910 read_batch()
911 .var_as("result", traversal)
912 .returning(["result"])
913}
914
915fn node_source(source: &str) -> Traversal<OnNodes, ReadOnly> {
916 match source {
917 "label" => g().n_with_label("ParityUser"),
918 "where" => g().n_where(SourcePredicate::eq("$label", "ParityUser")),
919 "all" => g().n(NodeRef::all()).has_label("ParityUser"),
920 other => panic!("unknown source {other}"),
921 }
922}
923
924fn apply_node_filter(
925 traversal: Traversal<OnNodes, ReadOnly>,
926 filter: &str,
927) -> Traversal<OnNodes, ReadOnly> {
928 match filter {
929 "none" => traversal,
930 "has" => traversal.has("status", "active"),
931 "logic" => traversal.where_(Predicate::and(vec![
932 Predicate::has_key("externalId"),
933 Predicate::or(vec![
934 Predicate::starts_with("name", "A"),
935 Predicate::ends_with("name", "b"),
936 ]),
937 Predicate::not(Predicate::is_null("age")),
938 ])),
939 "expr" => traversal.where_(Predicate::compare(
940 Expr::prop("score").add(Expr::val(1.0f64)),
941 CompareOp::Gt,
942 Expr::val(65.0f64),
943 )),
944 other => panic!("unknown filter {other}"),
945 }
946}
947
948fn apply_node_bound(
949 traversal: Traversal<OnNodes, ReadOnly>,
950 bound: &str,
951) -> Traversal<OnNodes, ReadOnly> {
952 match bound {
953 "none" => traversal,
954 "limit" => traversal.limit(2usize),
955 "skip" => traversal.skip(1usize),
956 "range" => traversal.range(0usize, 2usize),
957 other => panic!("unknown bound {other}"),
958 }
959}
960
961fn json_only_fixtures() -> Vec<Fixture> {
962 vec![
963 json_only(
964 "900-exhaustive-raw-read-steps",
965 with_params(
966 read_request(
967 read_batch()
968 .var_as(
969 "raw_nodes",
970 Traversal::<OnNodes, ReadOnly>::from_steps(vec![
971 Step::N(NodeRef::Param("node_ids".to_string())),
972 Step::Has("name".to_string(), PropertyValue::from("Alice")),
973 Step::Where(Predicate::contains_param("bio", "needle")),
974 Step::LimitBy(Expr::param("limit")),
975 Step::SkipBy(Expr::param("skip")),
976 Step::RangeBy(
977 StreamBound::literal(0),
978 StreamBound::expr(Expr::param("end")),
979 ),
980 Step::As("a".to_string()),
981 Step::Store("stored".to_string()),
982 Step::Select("stored".to_string()),
983 Step::Dedup,
984 Step::Within("stored".to_string()),
985 Step::Without("missing".to_string()),
986 Step::Fold,
987 Step::Unfold,
988 Step::Path,
989 Step::SimplePath,
990 Step::WithSack(PropertyValue::from(0i64)),
991 Step::SackSet("score".to_string()),
992 Step::SackAdd("score".to_string()),
993 Step::SackGet,
994 Step::Project(vec![
995 Projection::property("externalId", "externalId"),
996 Projection::expr("neg_age", Expr::prop("age").neg()),
997 ]),
998 ]),
999 )
1000 .var_as(
1001 "raw_edges",
1002 Traversal::<helix_db::OnEdges, ReadOnly>::from_steps(vec![
1003 Step::E(EdgeRef::Param("edge_ids".to_string())),
1004 Step::EWhere(SourcePredicate::or(vec![
1005 SourcePredicate::has_key("since"),
1006 SourcePredicate::starts_with("note", "Alice"),
1007 ])),
1008 Step::OutN,
1009 Step::InN,
1010 Step::OtherN,
1011 Step::EdgeHas("weight".to_string(), PropertyInput::from(1.0f64)),
1012 Step::EdgeHasLabel("FOLLOWS".to_string()),
1013 Step::OrderBy("weight".to_string(), Order::Desc),
1014 Step::EdgeProperties,
1015 ]),
1016 )
1017 .returning(["raw_nodes", "raw_edges"]),
1018 ),
1019 vec![
1020 ("node_ids", array(vec![i64_value(1), i64_value(2)])),
1021 ("edge_ids", array(vec![i64_value(1)])),
1022 ("needle", string("graph")),
1023 ("limit", i64_value(10)),
1024 ("skip", i64_value(0)),
1025 ("end", i64_value(10)),
1026 ],
1027 vec![
1028 (
1029 "node_ids",
1030 QueryParamType::Array(Box::new(QueryParamType::I64)),
1031 ),
1032 (
1033 "edge_ids",
1034 QueryParamType::Array(Box::new(QueryParamType::I64)),
1035 ),
1036 ("needle", QueryParamType::String),
1037 ("limit", QueryParamType::I64),
1038 ("skip", QueryParamType::I64),
1039 ("end", QueryParamType::I64),
1040 ],
1041 ),
1042 ),
1043 json_only(
1044 "901-exhaustive-raw-write-steps",
1045 write_request(
1046 write_batch()
1047 .var_as(
1048 "raw_indexes",
1049 Traversal::<helix_db::Terminal, WriteEnabled>::from_steps(vec![
1050 Step::CreateIndex {
1051 spec: IndexSpec::node_unique_equality("ParityUser", "externalId"),
1052 if_not_exists: true,
1053 },
1054 Step::DropIndex {
1055 spec: IndexSpec::node_range("ParityUser", "age"),
1056 },
1057 Step::CreateVectorIndexNodes {
1058 label: "ParityUser".to_string(),
1059 property: "embedding".to_string(),
1060 tenant_property: Some("tenantId".to_string()),
1061 },
1062 Step::CreateVectorIndexEdges {
1063 label: "FOLLOWS".to_string(),
1064 property: "embedding".to_string(),
1065 tenant_property: Some("tenantId".to_string()),
1066 },
1067 Step::CreateTextIndexNodes {
1068 label: "ParityUser".to_string(),
1069 property: "bio".to_string(),
1070 tenant_property: Some("tenantId".to_string()),
1071 },
1072 Step::CreateTextIndexEdges {
1073 label: "FOLLOWS".to_string(),
1074 property: "note".to_string(),
1075 tenant_property: Some("tenantId".to_string()),
1076 },
1077 ]),
1078 )
1079 .var_as(
1080 "raw_mutations",
1081 Traversal::<OnNodes, WriteEnabled>::from_steps(vec![
1082 Step::AddN {
1083 label: "RawNode".to_string(),
1084 properties: vec![("name".to_string(), PropertyInput::from("raw"))],
1085 },
1086 Step::AddE {
1087 label: "RAW_EDGE".to_string(),
1088 to: NodeRef::Var("raw_mutations".to_string()),
1089 properties: vec![("weight".to_string(), PropertyInput::from(1i64))],
1090 },
1091 Step::SetProperty(
1092 "name".to_string(),
1093 PropertyInput::Expr(Expr::param("name")),
1094 ),
1095 Step::RemoveProperty("old".to_string()),
1096 Step::DropEdge(NodeRef::Ids(vec![999_999])),
1097 Step::DropEdgeLabeled {
1098 to: NodeRef::Ids(vec![999_999]),
1099 label: "RAW_EDGE".to_string(),
1100 },
1101 Step::DropEdgeById(EdgeRef::Ids(vec![999_999])),
1102 Step::Drop,
1103 ]),
1104 )
1105 .returning(["raw_indexes", "raw_mutations"]),
1106 ),
1107 ),
1108 json_only(
1109 "902-dynamic-value-and-param-type-shapes",
1110 with_params(
1111 read_request(
1112 read_batch()
1113 .var_as("empty", g().n_with_label("Missing").count())
1114 .returning(["empty"]),
1115 ),
1116 vec![
1117 ("null", DynamicQueryValue::Null),
1118 ("bool", DynamicQueryValue::Bool(true)),
1119 ("i64", DynamicQueryValue::I64(i64::MAX)),
1120 ("f64", DynamicQueryValue::F64(1.25)),
1121 ("f32", DynamicQueryValue::F32(1.5)),
1122 ("string", string("value")),
1123 ("array", array(vec![i64_value(1), string("two")])),
1124 (
1125 "object",
1126 object(vec![("nested", DynamicQueryValue::Bool(true))]),
1127 ),
1128 ],
1129 vec![
1130 ("null", QueryParamType::Value),
1131 ("bool", QueryParamType::Bool),
1132 ("i64", QueryParamType::I64),
1133 ("f64", QueryParamType::F64),
1134 ("f32", QueryParamType::F32),
1135 ("string", QueryParamType::String),
1136 (
1137 "array",
1138 QueryParamType::Array(Box::new(QueryParamType::Value)),
1139 ),
1140 ("object", QueryParamType::Object),
1141 ],
1142 ),
1143 ),
1144 json_only(
1145 "903-empty-source-vector-text-runtime-inputs",
1146 with_params(
1147 read_request(
1148 read_batch()
1149 .var_as(
1150 "vector_nodes",
1151 g().vector_search_nodes_with(
1152 "ParityUser",
1153 "embedding",
1154 PropertyInput::param("query_vector"),
1155 Expr::param("limit"),
1156 Some(PropertyInput::param("tenant")),
1157 ),
1158 )
1159 .var_as(
1160 "text_nodes",
1161 g().text_search_nodes_with(
1162 "ParityUser",
1163 "bio",
1164 PropertyInput::param("query_text"),
1165 Expr::param("limit"),
1166 Some(PropertyInput::param("tenant")),
1167 ),
1168 )
1169 .returning(["vector_nodes", "text_nodes"]),
1170 ),
1171 vec![
1172 (
1173 "query_vector",
1174 array(vec![f64_value(1.0), f64_value(0.0), f64_value(0.0)]),
1175 ),
1176 ("query_text", string("graph")),
1177 ("limit", i64_value(5)),
1178 ("tenant", string("tenant-a")),
1179 ],
1180 vec![
1181 (
1182 "query_vector",
1183 QueryParamType::Array(Box::new(QueryParamType::F64)),
1184 ),
1185 ("query_text", QueryParamType::String),
1186 ("limit", QueryParamType::I64),
1187 ("tenant", QueryParamType::String),
1188 ],
1189 ),
1190 ),
1191 json_only(
1192 "904-empty-query-and-node-edge-ref-shapes",
1193 read_request(
1194 read_batch()
1195 .var_as(
1196 "all_nodes",
1197 Traversal::<OnNodes, ReadOnly>::from_steps(vec![
1198 Step::N(NodeRef::All),
1199 Step::Count,
1200 ]),
1201 )
1202 .var_as(
1203 "node_ids",
1204 Traversal::<OnNodes, ReadOnly>::from_steps(vec![
1205 Step::N(NodeRef::ids([1, 2])),
1206 Step::Id,
1207 ]),
1208 )
1209 .var_as(
1210 "node_var",
1211 Traversal::<OnNodes, ReadOnly>::from_steps(vec![
1212 Step::N(NodeRef::Var("all_nodes".to_string())),
1213 Step::Label,
1214 ]),
1215 )
1216 .var_as(
1217 "edge_ids",
1218 Traversal::<helix_db::OnEdges, ReadOnly>::from_steps(vec![
1219 Step::E(EdgeRef::ids([1, 2])),
1220 Step::Id,
1221 ]),
1222 )
1223 .var_as(
1224 "edge_var",
1225 Traversal::<helix_db::OnEdges, ReadOnly>::from_steps(vec![
1226 Step::E(EdgeRef::Var("edge_ids".to_string())),
1227 Step::Label,
1228 ]),
1229 )
1230 .returning(["all_nodes", "node_ids", "node_var", "edge_ids", "edge_var"]),
1231 ),
1232 ),
1233 json_only(
1234 "905-empty-traversal-source-mutators",
1235 write_request(
1236 write_batch()
1237 .var_as(
1238 "inject",
1239 Traversal::<Empty, ReadOnly>::new()
1240 .inject("some_var")
1241 .count(),
1242 )
1243 .var_as(
1244 "drop_edge_by_id",
1245 g().drop_edge_by_id(EdgeRef::id(123_456)).count(),
1246 )
1247 .returning(["inject", "drop_edge_by_id"]),
1248 ),
1249 ),
1250 json_only(
1251 "906-nested-dynamic-property-write-shapes",
1252 with_params(
1253 write_request(
1254 write_batch()
1255 .var_as(
1256 "created",
1257 g().add_n(
1258 "ParityNested",
1259 vec![
1260 ("name", PropertyInput::from("nested")),
1261 (
1262 "metadata",
1263 PropertyInput::from(nested_metadata_property(
1264 "some_id", 20,
1265 )),
1266 ),
1267 ],
1268 ),
1269 )
1270 .var_as(
1271 "updated",
1272 g().n(NodeRef::var("created"))
1273 .set_property("metadata", PropertyInput::param("metadata"))
1274 .value_map(Some(vec!["metadata.externalID"])),
1275 )
1276 .var_as(
1277 "target",
1278 g().add_n(
1279 "ParityNestedTarget",
1280 vec![("name", PropertyInput::from("target"))],
1281 ),
1282 )
1283 .var_as(
1284 "edge",
1285 g().n(NodeRef::var("created"))
1286 .add_e(
1287 "NESTED_LINK",
1288 NodeRef::var("target"),
1289 vec![(
1290 "metadata",
1291 PropertyInput::from(nested_metadata_property("edge_id", 5)),
1292 )],
1293 )
1294 .count(),
1295 )
1296 .returning(["created", "updated", "edge"]),
1297 ),
1298 vec![("metadata", nested_metadata_param("param_id", 22))],
1299 vec![("metadata", QueryParamType::Object)],
1300 ),
1301 ),
1302 json_only(
1303 "907-nested-dynamic-property-read-shapes",
1304 with_params(
1305 read_request(
1306 read_batch()
1307 .var_as(
1308 "nested_users",
1309 g().n_where(SourcePredicate::and(vec![
1310 SourcePredicate::eq("$label", "ParityNested"),
1311 SourcePredicate::eq(
1312 "metadata.externalID",
1313 Expr::param("external_id"),
1314 ),
1315 ]))
1316 .where_(Predicate::compare(
1317 Expr::prop("metadata.score"),
1318 CompareOp::Gt,
1319 Expr::val(10i64),
1320 ))
1321 .order_by_multiple(vec![
1322 ("metadata.score", Order::Desc),
1323 ("name", Order::Asc),
1324 ])
1325 .project(vec![
1326 Projection::property("metadata.externalID", "external_id"),
1327 Projection::expr("score_copy", Expr::prop("metadata.score")),
1328 ]),
1329 )
1330 .var_as(
1331 "nested_values",
1332 g().n_with_label("ParityNested")
1333 .values(vec!["metadata.externalID"]),
1334 )
1335 .var_as(
1336 "nested_map",
1337 g().n_with_label("ParityNested")
1338 .value_map(Some(vec!["metadata.externalID", "metadata.score"])),
1339 )
1340 .var_as(
1341 "nested_edges",
1342 g().e_where(SourcePredicate::and(vec![
1343 SourcePredicate::eq("$label", "NESTED_LINK"),
1344 SourcePredicate::eq("metadata.externalID", "edge_id"),
1345 ]))
1346 .edge_has("metadata.externalID", PropertyInput::from("edge_id"))
1347 .edge_properties(),
1348 )
1349 .returning([
1350 "nested_users",
1351 "nested_values",
1352 "nested_map",
1353 "nested_edges",
1354 ]),
1355 ),
1356 vec![("external_id", string("param_id"))],
1357 vec![("external_id", QueryParamType::String)],
1358 ),
1359 ),
1360 json_only(
1361 "908-edge-endpoint-projection",
1362 read_request(
1363 read_batch()
1364 .var_as(
1365 "endpoints",
1366 g().e_with_label("FOLLOWS").project(vec![
1367 Projection::from_endpoint("externalId", "from_id"),
1368 Projection::to_endpoint("externalId", "to_id"),
1369 Projection::property("$id", "edge_id"),
1370 ]),
1371 )
1372 .returning(["endpoints"]),
1373 ),
1374 ),
1375 json_only(
1376 "909-row-binding-basic-projection",
1377 read_request(
1378 read_batch()
1379 .var_as(
1380 "bindings",
1381 g().n_with_label("ParityService")
1382 .bind("service")
1383 .project_bindings(vec![
1384 BindingProjection::binding("service", "$id", "service_id"),
1385 BindingProjection::current("metadata.name", "current_name"),
1386 BindingProjection::binding(
1387 "missing_binding",
1388 "externalId",
1389 "missing_external_id",
1390 ),
1391 ]),
1392 )
1393 .returning(["bindings"]),
1394 ),
1395 ),
1396 json_only(
1397 "910-row-binding-branch-distinct-projection",
1398 read_request(
1399 read_batch()
1400 .var_as(
1401 "workloads",
1402 g().n_with_label("ParityService")
1403 .bind("service")
1404 .out(Some("ROUTES_TO"))
1405 .bind("pod")
1406 .optional(sub().in_(Some("CREATES")).bind("deployment"))
1407 .union(vec![
1408 sub().in_(Some("MANAGES")).bind("owner"),
1409 sub().out(Some("ROUTES_TO")).bind("workload"),
1410 ])
1411 .project_distinct_bindings(vec![
1412 BindingProjection::binding("service", "$id", "service_id"),
1413 BindingProjection::coalesce(
1414 vec![
1415 BindingValueRef::binding("deployment", "$id"),
1416 BindingValueRef::binding("owner", "$id"),
1417 BindingValueRef::binding("workload", "$id"),
1418 ],
1419 "workload_id",
1420 ),
1421 ]),
1422 )
1423 .returning(["workloads"]),
1424 ),
1425 ),
1426 json_only(
1427 "911-range-index-direction",
1428 write_request(
1429 write_batch()
1430 .var_as(
1431 "node_desc",
1432 g().create_index_if_not_exists(IndexSpec::node_range_desc(
1433 "ParityUser",
1434 "age",
1435 )),
1436 )
1437 .var_as(
1438 "edge_desc",
1439 g().create_index_if_not_exists(IndexSpec::edge_range_desc(
1440 "FOLLOWS", "weight",
1441 )),
1442 )
1443 .var_as(
1444 "node_asc",
1445 g().create_index_if_not_exists(IndexSpec::node_range(
1446 "ParityUser",
1447 "score",
1448 )),
1449 )
1450 .returning(["node_desc", "edge_desc", "node_asc"]),
1451 ),
1452 ),
1453 ]
1454}Sourcepub fn var_as_if<S: TraversalState>(
self,
name: &str,
condition: BatchCondition,
traversal: Traversal<S, ReadOnly>,
) -> Self
pub fn var_as_if<S: TraversalState>( self, name: &str, condition: BatchCondition, traversal: Traversal<S, ReadOnly>, ) -> Self
Add a conditional read-only query that only executes if the condition is met
Examples found in repository?
159fn runtime_fixtures() -> Vec<Fixture> {
160 vec![
161 runtime(
162 "001-write-seed-core",
163 write_request(
164 write_batch()
165 .var_as(
166 "alice",
167 g().add_n(
168 "ParityUser",
169 user_props(
170 "user-alice",
171 "Alice",
172 31,
173 90.5,
174 "active",
175 "London",
176 "Alice writes graph database tests",
177 vec![1.0, 0.0, 0.0],
178 ),
179 ),
180 )
181 .var_as(
182 "bob",
183 g().add_n(
184 "ParityUser",
185 user_props(
186 "user-bob",
187 "Bob",
188 27,
189 72.25,
190 "active",
191 "Paris",
192 "Bob likes traversal testing",
193 vec![0.9, 0.1, 0.0],
194 ),
195 ),
196 )
197 .var_as(
198 "carol",
199 g().add_n(
200 "ParityUser",
201 user_props(
202 "user-carol",
203 "Carol",
204 42,
205 64.0,
206 "inactive",
207 "Berlin",
208 "Carol archives old records",
209 vec![0.0, 1.0, 0.0],
210 ),
211 ),
212 )
213 .var_as(
214 "alice_follows_bob",
215 g().n(NodeRef::var("alice")).add_e(
216 "FOLLOWS",
217 NodeRef::var("bob"),
218 vec![
219 ("weight", PropertyInput::from(1.0f64)),
220 ("since", PropertyInput::from("2024-01-01")),
221 ("note", PropertyInput::from("Alice follows Bob")),
222 (
223 "embedding",
224 PropertyInput::from(PropertyValue::from(vec![1.0f32, 0.0])),
225 ),
226 ],
227 ),
228 )
229 .var_as(
230 "bob_follows_carol",
231 g().n(NodeRef::var("bob")).add_e(
232 "FOLLOWS",
233 NodeRef::var("carol"),
234 vec![
235 ("weight", PropertyInput::from(0.5f64)),
236 ("since", PropertyInput::from("2024-02-01")),
237 ("note", PropertyInput::from("Bob follows Carol")),
238 (
239 "embedding",
240 PropertyInput::from(PropertyValue::from(vec![0.0f32, 1.0])),
241 ),
242 ],
243 ),
244 )
245 .returning([
246 "alice",
247 "bob",
248 "carol",
249 "alice_follows_bob",
250 "bob_follows_carol",
251 ]),
252 ),
253 ),
254 runtime(
255 "002-read-count-all-users",
256 read_request(
257 read_batch()
258 .var_as("user_count", g().n_with_label("ParityUser").count())
259 .returning(["user_count"]),
260 ),
261 ),
262 runtime(
263 "003-read-source-predicate-and-count",
264 read_request(
265 read_batch()
266 .var_as(
267 "active_adults",
268 g().n_with_label_where(
269 "ParityUser",
270 SourcePredicate::and(vec![
271 SourcePredicate::eq("status", "active"),
272 SourcePredicate::gte("age", 30i64),
273 ]),
274 )
275 .count(),
276 )
277 .returning(["active_adults"]),
278 ),
279 ),
280 runtime(
281 "004-read-value-map-projection",
282 read_request(
283 read_batch()
284 .var_as(
285 "alice",
286 g().n_with_label("ParityUser")
287 .where_(Predicate::eq("externalId", "user-alice"))
288 .project(vec![
289 Projection::property("externalId", "id"),
290 Projection::property("name", "name"),
291 Projection::expr(
292 "score_plus_one",
293 Expr::prop("score").add(Expr::val(1.0f64)),
294 ),
295 Projection::expr(
296 "status_label",
297 Expr::case(
298 vec![(
299 Predicate::eq("status", "active"),
300 Expr::val("enabled"),
301 )],
302 Some(Expr::val("disabled")),
303 ),
304 ),
305 ]),
306 )
307 .returning(["alice"]),
308 ),
309 ),
310 runtime(
311 "005-read-order-range-values",
312 read_request(
313 read_batch()
314 .var_as(
315 "ordered",
316 g().n_with_label("ParityUser")
317 .order_by_multiple(vec![("status", Order::Asc), ("age", Order::Desc)])
318 .range(0usize, 2usize)
319 .value_map(Some(vec!["externalId", "age", "status"])),
320 )
321 .returning(["ordered"]),
322 ),
323 ),
324 runtime(
325 "006-read-edge-count",
326 read_request(
327 read_batch()
328 .var_as(
329 "edge_count",
330 g().n_with_label("ParityUser")
331 .where_(Predicate::eq("externalId", "user-alice"))
332 .out_e(Some("FOLLOWS"))
333 .count(),
334 )
335 .returning(["edge_count"]),
336 ),
337 ),
338 runtime(
339 "007-read-edge-properties",
340 read_request(
341 read_batch()
342 .var_as(
343 "edges",
344 g().e_with_label("FOLLOWS")
345 .edge_has("weight", PropertyInput::from(1.0f64))
346 .edge_properties(),
347 )
348 .returning(["edges"]),
349 ),
350 ),
351 runtime(
352 "008-read-edge-endpoints",
353 read_request(
354 read_batch()
355 .var_as(
356 "from_nodes",
357 g().e_with_label("FOLLOWS")
358 .edge_has_label("FOLLOWS")
359 .in_n()
360 .value_map(Some(vec!["externalId", "name"])),
361 )
362 .var_as(
363 "to_nodes",
364 g().e_with_label("FOLLOWS")
365 .out_n()
366 .value_map(Some(vec!["externalId", "name"])),
367 )
368 .returning(["from_nodes", "to_nodes"]),
369 ),
370 ),
371 runtime(
372 "009-read-conditional-var-not-empty",
373 read_request(
374 read_batch()
375 .var_as(
376 "alice",
377 g().n_with_label("ParityUser")
378 .where_(Predicate::eq("externalId", "user-alice")),
379 )
380 .var_as_if(
381 "friends",
382 BatchCondition::VarNotEmpty("alice".to_string()),
383 g().n(NodeRef::var("alice"))
384 .out(Some("FOLLOWS"))
385 .value_map(Some(vec!["externalId", "name"])),
386 )
387 .returning(["alice", "friends"]),
388 ),
389 ),
390 runtime(
391 "010-read-conditional-var-empty",
392 read_request(
393 read_batch()
394 .var_as(
395 "missing",
396 g().n_with_label("ParityUser")
397 .where_(Predicate::eq("externalId", "missing-user")),
398 )
399 .var_as_if(
400 "fallback",
401 BatchCondition::VarEmpty("missing".to_string()),
402 g().n_with_label("ParityUser")
403 .limit(1usize)
404 .value_map(Some(vec!["externalId"])),
405 )
406 .returning(["missing", "fallback"]),
407 ),
408 ),
409 runtime(
410 "011-read-conditional-var-min-size-prev",
411 read_request(
412 read_batch()
413 .var_as("users", g().n_with_label("ParityUser").limit(3usize))
414 .var_as_if(
415 "min_two",
416 BatchCondition::VarMinSize("users".to_string(), 2),
417 g().n(NodeRef::var("users")).count(),
418 )
419 .var_as_if(
420 "prev_ok",
421 BatchCondition::PrevNotEmpty,
422 g().n(NodeRef::var("users")).exists(),
423 )
424 .returning(["min_two", "prev_ok"]),
425 ),
426 ),
427 runtime(
428 "012-read-foreach-param",
429 with_params(
430 read_request(
431 read_batch()
432 .for_each_param(
433 "lookups",
434 read_batch().var_as(
435 "matched",
436 g().n_with_label("ParityUser")
437 .where_(Predicate::eq_param("externalId", "externalId"))
438 .value_map(Some(vec!["externalId", "name"])),
439 ),
440 )
441 .returning(["matched"]),
442 ),
443 vec![(
444 "lookups",
445 array(vec![
446 object(vec![("externalId", string("user-alice"))]),
447 object(vec![("externalId", string("user-carol"))]),
448 ]),
449 )],
450 vec![(
451 "lookups",
452 QueryParamType::Array(Box::new(QueryParamType::Object)),
453 )],
454 ),
455 ),
456 runtime(
457 "013-write-foreach-param-create",
458 with_params(
459 write_request(
460 write_batch()
461 .for_each_param(
462 "rows",
463 write_batch().var_as(
464 "created",
465 g().add_n(
466 "ParityEvent",
467 vec![
468 ("eventId", PropertyInput::param("eventId")),
469 ("kind", PropertyInput::param("kind")),
470 ("score", PropertyInput::param("score")),
471 ],
472 ),
473 ),
474 )
475 .returning(["created"]),
476 ),
477 vec![(
478 "rows",
479 array(vec![
480 object(vec![
481 ("eventId", string("event-1")),
482 ("kind", string("click")),
483 ("score", i64_value(10)),
484 ]),
485 object(vec![
486 ("eventId", string("event-2")),
487 ("kind", string("view")),
488 ("score", i64_value(5)),
489 ]),
490 ]),
491 )],
492 vec![(
493 "rows",
494 QueryParamType::Array(Box::new(QueryParamType::Object)),
495 )],
496 ),
497 ),
498 runtime(
499 "014-read-after-foreach-param",
500 read_request(
501 read_batch()
502 .var_as("event_count", g().n_with_label("ParityEvent").count())
503 .returning(["event_count"]),
504 ),
505 ),
506 runtime(
507 "015-write-set-remove-properties",
508 write_request(
509 write_batch()
510 .var_as(
511 "updated",
512 g().n_with_label("ParityUser")
513 .where_(Predicate::eq("externalId", "user-bob"))
514 .set_property("status", PropertyInput::from("inactive"))
515 .set_property(
516 "updatedAt",
517 PropertyInput::from(DateTime::from_millis(1_777_000_000_000)),
518 )
519 .remove_property("city")
520 .count(),
521 )
522 .returning(["updated"]),
523 ),
524 ),
525 runtime(
526 "016-read-updated-properties",
527 read_request(
528 read_batch()
529 .var_as(
530 "bob",
531 g().n_with_label("ParityUser")
532 .where_(Predicate::eq("externalId", "user-bob"))
533 .value_map(Some(vec!["externalId", "status", "updatedAt", "city"])),
534 )
535 .returning(["bob"]),
536 ),
537 ),
538 runtime(
539 "017-read-repeat-union",
540 read_request(
541 read_batch()
542 .var_as(
543 "walked",
544 g().n_with_label("ParityUser")
545 .where_(Predicate::eq("externalId", "user-alice"))
546 .repeat(
547 RepeatConfig::new(sub().out(Some("FOLLOWS")))
548 .times(2)
549 .emit_all()
550 .max_depth(4),
551 )
552 .union(vec![sub().out(Some("FOLLOWS")), sub().in_(Some("FOLLOWS"))])
553 .dedup()
554 .value_map(Some(vec!["externalId", "name"])),
555 )
556 .returning(["walked"]),
557 ),
558 ),
559 runtime(
560 "018-read-choose-coalesce-optional",
561 read_request(
562 read_batch()
563 .var_as(
564 "branched",
565 g().n_with_label("ParityUser")
566 .where_(Predicate::eq("externalId", "user-alice"))
567 .choose(
568 Predicate::eq("status", "active"),
569 sub().out(Some("FOLLOWS")),
570 Some(sub().in_(Some("FOLLOWS"))),
571 )
572 .coalesce(vec![sub().out(Some("FOLLOWS")), sub().in_(Some("FOLLOWS"))])
573 .optional(sub().out(Some("FOLLOWS")))
574 .dedup()
575 .value_map(Some(vec!["externalId", "name"])),
576 )
577 .returning(["branched"]),
578 ),
579 ),
580 runtime(
581 "019-read-aggregations",
582 read_request(
583 read_batch()
584 .var_as(
585 "by_status",
586 g().n_with_label("ParityUser").group_count("status"),
587 )
588 .var_as(
589 "mean_score",
590 g().n_with_label("ParityUser")
591 .aggregate_by(AggregateFunction::Mean, "score"),
592 )
593 .var_as(
594 "max_age",
595 g().n_with_label("ParityUser")
596 .aggregate_by(AggregateFunction::Max, "age"),
597 )
598 .returning(["by_status", "mean_score", "max_age"]),
599 ),
600 ),
601 runtime(
602 "020-write-index-create",
603 write_request(
604 write_batch()
605 .var_as(
606 "node_eq",
607 g().create_index_if_not_exists(IndexSpec::node_equality(
608 "ParityUser",
609 "externalId",
610 )),
611 )
612 .var_as(
613 "node_range",
614 g().create_index_if_not_exists(IndexSpec::node_range("ParityUser", "age")),
615 )
616 .var_as(
617 "edge_eq",
618 g().create_index_if_not_exists(IndexSpec::edge_equality(
619 "FOLLOWS", "since",
620 )),
621 )
622 .var_as(
623 "edge_range",
624 g().create_index_if_not_exists(IndexSpec::edge_range("FOLLOWS", "weight")),
625 )
626 .returning(["node_eq", "node_range", "edge_eq", "edge_range"]),
627 ),
628 ),
629 runtime(
630 "021-read-parameter-types",
631 with_params(
632 read_request(
633 read_batch()
634 .var_as(
635 "matches",
636 g().n_with_label("ParityUser")
637 .where_(Predicate::is_in_param("status", "statuses"))
638 .where_(Predicate::gte_param("createdAt", "created_after"))
639 .limit(Expr::param("limit"))
640 .value_map(Some(vec!["externalId", "status"])),
641 )
642 .returning(["matches"]),
643 ),
644 vec![
645 (
646 "statuses",
647 array(vec![string("active"), string("inactive")]),
648 ),
649 ("created_after", string("2026-01-01T00:00:00.000Z")),
650 ("limit", i64_value(5)),
651 ],
652 vec![
653 (
654 "statuses",
655 QueryParamType::Array(Box::new(QueryParamType::String)),
656 ),
657 ("created_after", QueryParamType::DateTime),
658 ("limit", QueryParamType::I64),
659 ],
660 ),
661 ),
662 runtime(
663 "022-write-property-value-variants",
664 write_request(
665 write_batch()
666 .var_as(
667 "variant_node",
668 g().add_n(
669 "ParityVariant",
670 vec![
671 ("nullValue", PropertyInput::from(PropertyValue::Null)),
672 ("boolValue", PropertyInput::from(true)),
673 (
674 "i64Value",
675 PropertyInput::from(9_223_372_036_854_775_000i64),
676 ),
677 (
678 "dateTimeValue",
679 PropertyInput::from(DateTime::from_millis(-1)),
680 ),
681 ("f64Value", PropertyInput::from(3.25f64)),
682 ("f32Value", PropertyInput::from(1.5f32)),
683 ("stringValue", PropertyInput::from("variant")),
684 (
685 "bytesValue",
686 PropertyInput::from(PropertyValue::from(vec![1u8, 2u8, 3u8])),
687 ),
688 (
689 "i64Array",
690 PropertyInput::from(PropertyValue::from(vec![
691 1i64, 2i64, 3i64,
692 ])),
693 ),
694 (
695 "f64Array",
696 PropertyInput::from(PropertyValue::from(vec![1.0f64, 2.0f64])),
697 ),
698 (
699 "f32Array",
700 PropertyInput::from(PropertyValue::from(vec![1.0f32, 2.0f32])),
701 ),
702 (
703 "stringArray",
704 PropertyInput::from(PropertyValue::from(vec![
705 "a".to_string(),
706 "b".to_string(),
707 ])),
708 ),
709 ],
710 ),
711 )
712 .returning(["variant_node"]),
713 ),
714 ),
715 runtime(
716 "023-read-property-value-variants",
717 read_request(
718 read_batch()
719 .var_as(
720 "variant",
721 g().n_with_label("ParityVariant")
722 .value_map(None::<Vec<&str>>),
723 )
724 .returning(["variant"]),
725 ),
726 ),
727 runtime(
728 "024-write-text-vector-indexes",
729 write_request(
730 write_batch()
731 .var_as(
732 "node_text",
733 g().create_text_index_nodes("ParityUser", "bio", None::<&str>),
734 )
735 .var_as(
736 "node_vector",
737 g().create_vector_index_nodes("ParityUser", "embedding", None::<&str>),
738 )
739 .var_as(
740 "edge_text",
741 g().create_text_index_edges("FOLLOWS", "note", None::<&str>),
742 )
743 .var_as(
744 "edge_vector",
745 g().create_vector_index_edges("FOLLOWS", "embedding", None::<&str>),
746 )
747 .returning(["node_text", "node_vector", "edge_text", "edge_vector"]),
748 ),
749 ),
750 runtime(
751 "025-read-text-search-nodes",
752 read_request(
753 read_batch()
754 .var_as(
755 "text_hits",
756 g().text_search_nodes("ParityUser", "bio", "graph", 5, None)
757 .value_map(Some(vec!["externalId", "bio", "$distance"])),
758 )
759 .returning(["text_hits"]),
760 ),
761 ),
762 runtime(
763 "026-read-vector-search-nodes",
764 read_request(
765 read_batch()
766 .var_as(
767 "vector_hits",
768 g().vector_search_nodes(
769 "ParityUser",
770 "embedding",
771 vec![1.0, 0.0, 0.0],
772 3,
773 None,
774 )
775 .project(vec![
776 Projection::property("externalId", "externalId"),
777 Projection::property("$distance", "distance"),
778 ]),
779 )
780 .returning(["vector_hits"]),
781 ),
782 ),
783 runtime(
784 "027-read-text-search-edges",
785 read_request(
786 read_batch()
787 .var_as(
788 "edge_text_hits",
789 g().text_search_edges("FOLLOWS", "note", "follows", 5, None)
790 .edge_properties(),
791 )
792 .returning(["edge_text_hits"]),
793 ),
794 ),
795 runtime(
796 "028-read-vector-search-edges",
797 read_request(
798 read_batch()
799 .var_as(
800 "edge_vector_hits",
801 g().vector_search_edges("FOLLOWS", "embedding", vec![1.0, 0.0], 5, None)
802 .edge_properties(),
803 )
804 .returning(["edge_vector_hits"]),
805 ),
806 ),
807 runtime(
808 "029-write-drop-temp-node",
809 write_request(
810 write_batch()
811 .var_as(
812 "temp",
813 g().add_n("ParityTemp", vec![("name", PropertyInput::from("temp"))]),
814 )
815 .var_as("dropped", g().n(NodeRef::var("temp")).drop().count())
816 .returning(["dropped"]),
817 ),
818 ),
819 runtime(
820 "030-read-final-counts",
821 read_request(
822 read_batch()
823 .var_as("users", g().n_with_label("ParityUser").count())
824 .var_as("events", g().n_with_label("ParityEvent").count())
825 .var_as("variants", g().n_with_label("ParityVariant").count())
826 .returning(["users", "events", "variants"]),
827 ),
828 ),
829 runtime(
830 "031-read-source-predicate-eq-param",
831 with_params(
832 read_request(
833 read_batch()
834 .var_as(
835 "user",
836 g().n_where(SourcePredicate::and(vec![
837 SourcePredicate::eq("$label", "ParityUser"),
838 SourcePredicate::eq("name", Expr::param("name")),
839 ]))
840 .value_map(Some(vec!["externalId", "name"])),
841 )
842 .returning(["user"]),
843 ),
844 vec![("name", string("Alice"))],
845 vec![("name", QueryParamType::String)],
846 ),
847 ),
848 runtime(
849 "032-read-source-predicate-between-param",
850 with_params(
851 read_request(
852 read_batch()
853 .var_as(
854 "adults",
855 g().n_where(SourcePredicate::and(vec![
856 SourcePredicate::eq("$label", "ParityUser"),
857 SourcePredicate::between("age", Expr::param("min_age"), 65i64),
858 ]))
859 .value_map(Some(vec!["externalId", "age"])),
860 )
861 .returning(["adults"]),
862 ),
863 vec![("min_age", i64_value(30))],
864 vec![("min_age", QueryParamType::I64)],
865 ),
866 ),
867 ]
868}Sourcepub fn for_each_param(self, param: &str, body: ReadBatch) -> Self
pub fn for_each_param(self, param: &str, body: ReadBatch) -> Self
Execute the provided body once per object in the named array parameter.
Examples found in repository?
159fn runtime_fixtures() -> Vec<Fixture> {
160 vec![
161 runtime(
162 "001-write-seed-core",
163 write_request(
164 write_batch()
165 .var_as(
166 "alice",
167 g().add_n(
168 "ParityUser",
169 user_props(
170 "user-alice",
171 "Alice",
172 31,
173 90.5,
174 "active",
175 "London",
176 "Alice writes graph database tests",
177 vec![1.0, 0.0, 0.0],
178 ),
179 ),
180 )
181 .var_as(
182 "bob",
183 g().add_n(
184 "ParityUser",
185 user_props(
186 "user-bob",
187 "Bob",
188 27,
189 72.25,
190 "active",
191 "Paris",
192 "Bob likes traversal testing",
193 vec![0.9, 0.1, 0.0],
194 ),
195 ),
196 )
197 .var_as(
198 "carol",
199 g().add_n(
200 "ParityUser",
201 user_props(
202 "user-carol",
203 "Carol",
204 42,
205 64.0,
206 "inactive",
207 "Berlin",
208 "Carol archives old records",
209 vec![0.0, 1.0, 0.0],
210 ),
211 ),
212 )
213 .var_as(
214 "alice_follows_bob",
215 g().n(NodeRef::var("alice")).add_e(
216 "FOLLOWS",
217 NodeRef::var("bob"),
218 vec![
219 ("weight", PropertyInput::from(1.0f64)),
220 ("since", PropertyInput::from("2024-01-01")),
221 ("note", PropertyInput::from("Alice follows Bob")),
222 (
223 "embedding",
224 PropertyInput::from(PropertyValue::from(vec![1.0f32, 0.0])),
225 ),
226 ],
227 ),
228 )
229 .var_as(
230 "bob_follows_carol",
231 g().n(NodeRef::var("bob")).add_e(
232 "FOLLOWS",
233 NodeRef::var("carol"),
234 vec![
235 ("weight", PropertyInput::from(0.5f64)),
236 ("since", PropertyInput::from("2024-02-01")),
237 ("note", PropertyInput::from("Bob follows Carol")),
238 (
239 "embedding",
240 PropertyInput::from(PropertyValue::from(vec![0.0f32, 1.0])),
241 ),
242 ],
243 ),
244 )
245 .returning([
246 "alice",
247 "bob",
248 "carol",
249 "alice_follows_bob",
250 "bob_follows_carol",
251 ]),
252 ),
253 ),
254 runtime(
255 "002-read-count-all-users",
256 read_request(
257 read_batch()
258 .var_as("user_count", g().n_with_label("ParityUser").count())
259 .returning(["user_count"]),
260 ),
261 ),
262 runtime(
263 "003-read-source-predicate-and-count",
264 read_request(
265 read_batch()
266 .var_as(
267 "active_adults",
268 g().n_with_label_where(
269 "ParityUser",
270 SourcePredicate::and(vec![
271 SourcePredicate::eq("status", "active"),
272 SourcePredicate::gte("age", 30i64),
273 ]),
274 )
275 .count(),
276 )
277 .returning(["active_adults"]),
278 ),
279 ),
280 runtime(
281 "004-read-value-map-projection",
282 read_request(
283 read_batch()
284 .var_as(
285 "alice",
286 g().n_with_label("ParityUser")
287 .where_(Predicate::eq("externalId", "user-alice"))
288 .project(vec![
289 Projection::property("externalId", "id"),
290 Projection::property("name", "name"),
291 Projection::expr(
292 "score_plus_one",
293 Expr::prop("score").add(Expr::val(1.0f64)),
294 ),
295 Projection::expr(
296 "status_label",
297 Expr::case(
298 vec![(
299 Predicate::eq("status", "active"),
300 Expr::val("enabled"),
301 )],
302 Some(Expr::val("disabled")),
303 ),
304 ),
305 ]),
306 )
307 .returning(["alice"]),
308 ),
309 ),
310 runtime(
311 "005-read-order-range-values",
312 read_request(
313 read_batch()
314 .var_as(
315 "ordered",
316 g().n_with_label("ParityUser")
317 .order_by_multiple(vec![("status", Order::Asc), ("age", Order::Desc)])
318 .range(0usize, 2usize)
319 .value_map(Some(vec!["externalId", "age", "status"])),
320 )
321 .returning(["ordered"]),
322 ),
323 ),
324 runtime(
325 "006-read-edge-count",
326 read_request(
327 read_batch()
328 .var_as(
329 "edge_count",
330 g().n_with_label("ParityUser")
331 .where_(Predicate::eq("externalId", "user-alice"))
332 .out_e(Some("FOLLOWS"))
333 .count(),
334 )
335 .returning(["edge_count"]),
336 ),
337 ),
338 runtime(
339 "007-read-edge-properties",
340 read_request(
341 read_batch()
342 .var_as(
343 "edges",
344 g().e_with_label("FOLLOWS")
345 .edge_has("weight", PropertyInput::from(1.0f64))
346 .edge_properties(),
347 )
348 .returning(["edges"]),
349 ),
350 ),
351 runtime(
352 "008-read-edge-endpoints",
353 read_request(
354 read_batch()
355 .var_as(
356 "from_nodes",
357 g().e_with_label("FOLLOWS")
358 .edge_has_label("FOLLOWS")
359 .in_n()
360 .value_map(Some(vec!["externalId", "name"])),
361 )
362 .var_as(
363 "to_nodes",
364 g().e_with_label("FOLLOWS")
365 .out_n()
366 .value_map(Some(vec!["externalId", "name"])),
367 )
368 .returning(["from_nodes", "to_nodes"]),
369 ),
370 ),
371 runtime(
372 "009-read-conditional-var-not-empty",
373 read_request(
374 read_batch()
375 .var_as(
376 "alice",
377 g().n_with_label("ParityUser")
378 .where_(Predicate::eq("externalId", "user-alice")),
379 )
380 .var_as_if(
381 "friends",
382 BatchCondition::VarNotEmpty("alice".to_string()),
383 g().n(NodeRef::var("alice"))
384 .out(Some("FOLLOWS"))
385 .value_map(Some(vec!["externalId", "name"])),
386 )
387 .returning(["alice", "friends"]),
388 ),
389 ),
390 runtime(
391 "010-read-conditional-var-empty",
392 read_request(
393 read_batch()
394 .var_as(
395 "missing",
396 g().n_with_label("ParityUser")
397 .where_(Predicate::eq("externalId", "missing-user")),
398 )
399 .var_as_if(
400 "fallback",
401 BatchCondition::VarEmpty("missing".to_string()),
402 g().n_with_label("ParityUser")
403 .limit(1usize)
404 .value_map(Some(vec!["externalId"])),
405 )
406 .returning(["missing", "fallback"]),
407 ),
408 ),
409 runtime(
410 "011-read-conditional-var-min-size-prev",
411 read_request(
412 read_batch()
413 .var_as("users", g().n_with_label("ParityUser").limit(3usize))
414 .var_as_if(
415 "min_two",
416 BatchCondition::VarMinSize("users".to_string(), 2),
417 g().n(NodeRef::var("users")).count(),
418 )
419 .var_as_if(
420 "prev_ok",
421 BatchCondition::PrevNotEmpty,
422 g().n(NodeRef::var("users")).exists(),
423 )
424 .returning(["min_two", "prev_ok"]),
425 ),
426 ),
427 runtime(
428 "012-read-foreach-param",
429 with_params(
430 read_request(
431 read_batch()
432 .for_each_param(
433 "lookups",
434 read_batch().var_as(
435 "matched",
436 g().n_with_label("ParityUser")
437 .where_(Predicate::eq_param("externalId", "externalId"))
438 .value_map(Some(vec!["externalId", "name"])),
439 ),
440 )
441 .returning(["matched"]),
442 ),
443 vec![(
444 "lookups",
445 array(vec![
446 object(vec![("externalId", string("user-alice"))]),
447 object(vec![("externalId", string("user-carol"))]),
448 ]),
449 )],
450 vec![(
451 "lookups",
452 QueryParamType::Array(Box::new(QueryParamType::Object)),
453 )],
454 ),
455 ),
456 runtime(
457 "013-write-foreach-param-create",
458 with_params(
459 write_request(
460 write_batch()
461 .for_each_param(
462 "rows",
463 write_batch().var_as(
464 "created",
465 g().add_n(
466 "ParityEvent",
467 vec![
468 ("eventId", PropertyInput::param("eventId")),
469 ("kind", PropertyInput::param("kind")),
470 ("score", PropertyInput::param("score")),
471 ],
472 ),
473 ),
474 )
475 .returning(["created"]),
476 ),
477 vec![(
478 "rows",
479 array(vec![
480 object(vec![
481 ("eventId", string("event-1")),
482 ("kind", string("click")),
483 ("score", i64_value(10)),
484 ]),
485 object(vec![
486 ("eventId", string("event-2")),
487 ("kind", string("view")),
488 ("score", i64_value(5)),
489 ]),
490 ]),
491 )],
492 vec![(
493 "rows",
494 QueryParamType::Array(Box::new(QueryParamType::Object)),
495 )],
496 ),
497 ),
498 runtime(
499 "014-read-after-foreach-param",
500 read_request(
501 read_batch()
502 .var_as("event_count", g().n_with_label("ParityEvent").count())
503 .returning(["event_count"]),
504 ),
505 ),
506 runtime(
507 "015-write-set-remove-properties",
508 write_request(
509 write_batch()
510 .var_as(
511 "updated",
512 g().n_with_label("ParityUser")
513 .where_(Predicate::eq("externalId", "user-bob"))
514 .set_property("status", PropertyInput::from("inactive"))
515 .set_property(
516 "updatedAt",
517 PropertyInput::from(DateTime::from_millis(1_777_000_000_000)),
518 )
519 .remove_property("city")
520 .count(),
521 )
522 .returning(["updated"]),
523 ),
524 ),
525 runtime(
526 "016-read-updated-properties",
527 read_request(
528 read_batch()
529 .var_as(
530 "bob",
531 g().n_with_label("ParityUser")
532 .where_(Predicate::eq("externalId", "user-bob"))
533 .value_map(Some(vec!["externalId", "status", "updatedAt", "city"])),
534 )
535 .returning(["bob"]),
536 ),
537 ),
538 runtime(
539 "017-read-repeat-union",
540 read_request(
541 read_batch()
542 .var_as(
543 "walked",
544 g().n_with_label("ParityUser")
545 .where_(Predicate::eq("externalId", "user-alice"))
546 .repeat(
547 RepeatConfig::new(sub().out(Some("FOLLOWS")))
548 .times(2)
549 .emit_all()
550 .max_depth(4),
551 )
552 .union(vec![sub().out(Some("FOLLOWS")), sub().in_(Some("FOLLOWS"))])
553 .dedup()
554 .value_map(Some(vec!["externalId", "name"])),
555 )
556 .returning(["walked"]),
557 ),
558 ),
559 runtime(
560 "018-read-choose-coalesce-optional",
561 read_request(
562 read_batch()
563 .var_as(
564 "branched",
565 g().n_with_label("ParityUser")
566 .where_(Predicate::eq("externalId", "user-alice"))
567 .choose(
568 Predicate::eq("status", "active"),
569 sub().out(Some("FOLLOWS")),
570 Some(sub().in_(Some("FOLLOWS"))),
571 )
572 .coalesce(vec![sub().out(Some("FOLLOWS")), sub().in_(Some("FOLLOWS"))])
573 .optional(sub().out(Some("FOLLOWS")))
574 .dedup()
575 .value_map(Some(vec!["externalId", "name"])),
576 )
577 .returning(["branched"]),
578 ),
579 ),
580 runtime(
581 "019-read-aggregations",
582 read_request(
583 read_batch()
584 .var_as(
585 "by_status",
586 g().n_with_label("ParityUser").group_count("status"),
587 )
588 .var_as(
589 "mean_score",
590 g().n_with_label("ParityUser")
591 .aggregate_by(AggregateFunction::Mean, "score"),
592 )
593 .var_as(
594 "max_age",
595 g().n_with_label("ParityUser")
596 .aggregate_by(AggregateFunction::Max, "age"),
597 )
598 .returning(["by_status", "mean_score", "max_age"]),
599 ),
600 ),
601 runtime(
602 "020-write-index-create",
603 write_request(
604 write_batch()
605 .var_as(
606 "node_eq",
607 g().create_index_if_not_exists(IndexSpec::node_equality(
608 "ParityUser",
609 "externalId",
610 )),
611 )
612 .var_as(
613 "node_range",
614 g().create_index_if_not_exists(IndexSpec::node_range("ParityUser", "age")),
615 )
616 .var_as(
617 "edge_eq",
618 g().create_index_if_not_exists(IndexSpec::edge_equality(
619 "FOLLOWS", "since",
620 )),
621 )
622 .var_as(
623 "edge_range",
624 g().create_index_if_not_exists(IndexSpec::edge_range("FOLLOWS", "weight")),
625 )
626 .returning(["node_eq", "node_range", "edge_eq", "edge_range"]),
627 ),
628 ),
629 runtime(
630 "021-read-parameter-types",
631 with_params(
632 read_request(
633 read_batch()
634 .var_as(
635 "matches",
636 g().n_with_label("ParityUser")
637 .where_(Predicate::is_in_param("status", "statuses"))
638 .where_(Predicate::gte_param("createdAt", "created_after"))
639 .limit(Expr::param("limit"))
640 .value_map(Some(vec!["externalId", "status"])),
641 )
642 .returning(["matches"]),
643 ),
644 vec![
645 (
646 "statuses",
647 array(vec![string("active"), string("inactive")]),
648 ),
649 ("created_after", string("2026-01-01T00:00:00.000Z")),
650 ("limit", i64_value(5)),
651 ],
652 vec![
653 (
654 "statuses",
655 QueryParamType::Array(Box::new(QueryParamType::String)),
656 ),
657 ("created_after", QueryParamType::DateTime),
658 ("limit", QueryParamType::I64),
659 ],
660 ),
661 ),
662 runtime(
663 "022-write-property-value-variants",
664 write_request(
665 write_batch()
666 .var_as(
667 "variant_node",
668 g().add_n(
669 "ParityVariant",
670 vec![
671 ("nullValue", PropertyInput::from(PropertyValue::Null)),
672 ("boolValue", PropertyInput::from(true)),
673 (
674 "i64Value",
675 PropertyInput::from(9_223_372_036_854_775_000i64),
676 ),
677 (
678 "dateTimeValue",
679 PropertyInput::from(DateTime::from_millis(-1)),
680 ),
681 ("f64Value", PropertyInput::from(3.25f64)),
682 ("f32Value", PropertyInput::from(1.5f32)),
683 ("stringValue", PropertyInput::from("variant")),
684 (
685 "bytesValue",
686 PropertyInput::from(PropertyValue::from(vec![1u8, 2u8, 3u8])),
687 ),
688 (
689 "i64Array",
690 PropertyInput::from(PropertyValue::from(vec![
691 1i64, 2i64, 3i64,
692 ])),
693 ),
694 (
695 "f64Array",
696 PropertyInput::from(PropertyValue::from(vec![1.0f64, 2.0f64])),
697 ),
698 (
699 "f32Array",
700 PropertyInput::from(PropertyValue::from(vec![1.0f32, 2.0f32])),
701 ),
702 (
703 "stringArray",
704 PropertyInput::from(PropertyValue::from(vec![
705 "a".to_string(),
706 "b".to_string(),
707 ])),
708 ),
709 ],
710 ),
711 )
712 .returning(["variant_node"]),
713 ),
714 ),
715 runtime(
716 "023-read-property-value-variants",
717 read_request(
718 read_batch()
719 .var_as(
720 "variant",
721 g().n_with_label("ParityVariant")
722 .value_map(None::<Vec<&str>>),
723 )
724 .returning(["variant"]),
725 ),
726 ),
727 runtime(
728 "024-write-text-vector-indexes",
729 write_request(
730 write_batch()
731 .var_as(
732 "node_text",
733 g().create_text_index_nodes("ParityUser", "bio", None::<&str>),
734 )
735 .var_as(
736 "node_vector",
737 g().create_vector_index_nodes("ParityUser", "embedding", None::<&str>),
738 )
739 .var_as(
740 "edge_text",
741 g().create_text_index_edges("FOLLOWS", "note", None::<&str>),
742 )
743 .var_as(
744 "edge_vector",
745 g().create_vector_index_edges("FOLLOWS", "embedding", None::<&str>),
746 )
747 .returning(["node_text", "node_vector", "edge_text", "edge_vector"]),
748 ),
749 ),
750 runtime(
751 "025-read-text-search-nodes",
752 read_request(
753 read_batch()
754 .var_as(
755 "text_hits",
756 g().text_search_nodes("ParityUser", "bio", "graph", 5, None)
757 .value_map(Some(vec!["externalId", "bio", "$distance"])),
758 )
759 .returning(["text_hits"]),
760 ),
761 ),
762 runtime(
763 "026-read-vector-search-nodes",
764 read_request(
765 read_batch()
766 .var_as(
767 "vector_hits",
768 g().vector_search_nodes(
769 "ParityUser",
770 "embedding",
771 vec![1.0, 0.0, 0.0],
772 3,
773 None,
774 )
775 .project(vec![
776 Projection::property("externalId", "externalId"),
777 Projection::property("$distance", "distance"),
778 ]),
779 )
780 .returning(["vector_hits"]),
781 ),
782 ),
783 runtime(
784 "027-read-text-search-edges",
785 read_request(
786 read_batch()
787 .var_as(
788 "edge_text_hits",
789 g().text_search_edges("FOLLOWS", "note", "follows", 5, None)
790 .edge_properties(),
791 )
792 .returning(["edge_text_hits"]),
793 ),
794 ),
795 runtime(
796 "028-read-vector-search-edges",
797 read_request(
798 read_batch()
799 .var_as(
800 "edge_vector_hits",
801 g().vector_search_edges("FOLLOWS", "embedding", vec![1.0, 0.0], 5, None)
802 .edge_properties(),
803 )
804 .returning(["edge_vector_hits"]),
805 ),
806 ),
807 runtime(
808 "029-write-drop-temp-node",
809 write_request(
810 write_batch()
811 .var_as(
812 "temp",
813 g().add_n("ParityTemp", vec![("name", PropertyInput::from("temp"))]),
814 )
815 .var_as("dropped", g().n(NodeRef::var("temp")).drop().count())
816 .returning(["dropped"]),
817 ),
818 ),
819 runtime(
820 "030-read-final-counts",
821 read_request(
822 read_batch()
823 .var_as("users", g().n_with_label("ParityUser").count())
824 .var_as("events", g().n_with_label("ParityEvent").count())
825 .var_as("variants", g().n_with_label("ParityVariant").count())
826 .returning(["users", "events", "variants"]),
827 ),
828 ),
829 runtime(
830 "031-read-source-predicate-eq-param",
831 with_params(
832 read_request(
833 read_batch()
834 .var_as(
835 "user",
836 g().n_where(SourcePredicate::and(vec![
837 SourcePredicate::eq("$label", "ParityUser"),
838 SourcePredicate::eq("name", Expr::param("name")),
839 ]))
840 .value_map(Some(vec!["externalId", "name"])),
841 )
842 .returning(["user"]),
843 ),
844 vec![("name", string("Alice"))],
845 vec![("name", QueryParamType::String)],
846 ),
847 ),
848 runtime(
849 "032-read-source-predicate-between-param",
850 with_params(
851 read_request(
852 read_batch()
853 .var_as(
854 "adults",
855 g().n_where(SourcePredicate::and(vec![
856 SourcePredicate::eq("$label", "ParityUser"),
857 SourcePredicate::between("age", Expr::param("min_age"), 65i64),
858 ]))
859 .value_map(Some(vec!["externalId", "age"])),
860 )
861 .returning(["adults"]),
862 ),
863 vec![("min_age", i64_value(30))],
864 vec![("min_age", QueryParamType::I64)],
865 ),
866 ),
867 ]
868}Sourcepub fn returning<I, S>(self, vars: I) -> Self
pub fn returning<I, S>(self, vars: I) -> Self
Specify which variables to return (call at end)
If not called, all named variables are returned.
Examples found in repository?
159fn runtime_fixtures() -> Vec<Fixture> {
160 vec![
161 runtime(
162 "001-write-seed-core",
163 write_request(
164 write_batch()
165 .var_as(
166 "alice",
167 g().add_n(
168 "ParityUser",
169 user_props(
170 "user-alice",
171 "Alice",
172 31,
173 90.5,
174 "active",
175 "London",
176 "Alice writes graph database tests",
177 vec![1.0, 0.0, 0.0],
178 ),
179 ),
180 )
181 .var_as(
182 "bob",
183 g().add_n(
184 "ParityUser",
185 user_props(
186 "user-bob",
187 "Bob",
188 27,
189 72.25,
190 "active",
191 "Paris",
192 "Bob likes traversal testing",
193 vec![0.9, 0.1, 0.0],
194 ),
195 ),
196 )
197 .var_as(
198 "carol",
199 g().add_n(
200 "ParityUser",
201 user_props(
202 "user-carol",
203 "Carol",
204 42,
205 64.0,
206 "inactive",
207 "Berlin",
208 "Carol archives old records",
209 vec![0.0, 1.0, 0.0],
210 ),
211 ),
212 )
213 .var_as(
214 "alice_follows_bob",
215 g().n(NodeRef::var("alice")).add_e(
216 "FOLLOWS",
217 NodeRef::var("bob"),
218 vec![
219 ("weight", PropertyInput::from(1.0f64)),
220 ("since", PropertyInput::from("2024-01-01")),
221 ("note", PropertyInput::from("Alice follows Bob")),
222 (
223 "embedding",
224 PropertyInput::from(PropertyValue::from(vec![1.0f32, 0.0])),
225 ),
226 ],
227 ),
228 )
229 .var_as(
230 "bob_follows_carol",
231 g().n(NodeRef::var("bob")).add_e(
232 "FOLLOWS",
233 NodeRef::var("carol"),
234 vec![
235 ("weight", PropertyInput::from(0.5f64)),
236 ("since", PropertyInput::from("2024-02-01")),
237 ("note", PropertyInput::from("Bob follows Carol")),
238 (
239 "embedding",
240 PropertyInput::from(PropertyValue::from(vec![0.0f32, 1.0])),
241 ),
242 ],
243 ),
244 )
245 .returning([
246 "alice",
247 "bob",
248 "carol",
249 "alice_follows_bob",
250 "bob_follows_carol",
251 ]),
252 ),
253 ),
254 runtime(
255 "002-read-count-all-users",
256 read_request(
257 read_batch()
258 .var_as("user_count", g().n_with_label("ParityUser").count())
259 .returning(["user_count"]),
260 ),
261 ),
262 runtime(
263 "003-read-source-predicate-and-count",
264 read_request(
265 read_batch()
266 .var_as(
267 "active_adults",
268 g().n_with_label_where(
269 "ParityUser",
270 SourcePredicate::and(vec![
271 SourcePredicate::eq("status", "active"),
272 SourcePredicate::gte("age", 30i64),
273 ]),
274 )
275 .count(),
276 )
277 .returning(["active_adults"]),
278 ),
279 ),
280 runtime(
281 "004-read-value-map-projection",
282 read_request(
283 read_batch()
284 .var_as(
285 "alice",
286 g().n_with_label("ParityUser")
287 .where_(Predicate::eq("externalId", "user-alice"))
288 .project(vec![
289 Projection::property("externalId", "id"),
290 Projection::property("name", "name"),
291 Projection::expr(
292 "score_plus_one",
293 Expr::prop("score").add(Expr::val(1.0f64)),
294 ),
295 Projection::expr(
296 "status_label",
297 Expr::case(
298 vec![(
299 Predicate::eq("status", "active"),
300 Expr::val("enabled"),
301 )],
302 Some(Expr::val("disabled")),
303 ),
304 ),
305 ]),
306 )
307 .returning(["alice"]),
308 ),
309 ),
310 runtime(
311 "005-read-order-range-values",
312 read_request(
313 read_batch()
314 .var_as(
315 "ordered",
316 g().n_with_label("ParityUser")
317 .order_by_multiple(vec![("status", Order::Asc), ("age", Order::Desc)])
318 .range(0usize, 2usize)
319 .value_map(Some(vec!["externalId", "age", "status"])),
320 )
321 .returning(["ordered"]),
322 ),
323 ),
324 runtime(
325 "006-read-edge-count",
326 read_request(
327 read_batch()
328 .var_as(
329 "edge_count",
330 g().n_with_label("ParityUser")
331 .where_(Predicate::eq("externalId", "user-alice"))
332 .out_e(Some("FOLLOWS"))
333 .count(),
334 )
335 .returning(["edge_count"]),
336 ),
337 ),
338 runtime(
339 "007-read-edge-properties",
340 read_request(
341 read_batch()
342 .var_as(
343 "edges",
344 g().e_with_label("FOLLOWS")
345 .edge_has("weight", PropertyInput::from(1.0f64))
346 .edge_properties(),
347 )
348 .returning(["edges"]),
349 ),
350 ),
351 runtime(
352 "008-read-edge-endpoints",
353 read_request(
354 read_batch()
355 .var_as(
356 "from_nodes",
357 g().e_with_label("FOLLOWS")
358 .edge_has_label("FOLLOWS")
359 .in_n()
360 .value_map(Some(vec!["externalId", "name"])),
361 )
362 .var_as(
363 "to_nodes",
364 g().e_with_label("FOLLOWS")
365 .out_n()
366 .value_map(Some(vec!["externalId", "name"])),
367 )
368 .returning(["from_nodes", "to_nodes"]),
369 ),
370 ),
371 runtime(
372 "009-read-conditional-var-not-empty",
373 read_request(
374 read_batch()
375 .var_as(
376 "alice",
377 g().n_with_label("ParityUser")
378 .where_(Predicate::eq("externalId", "user-alice")),
379 )
380 .var_as_if(
381 "friends",
382 BatchCondition::VarNotEmpty("alice".to_string()),
383 g().n(NodeRef::var("alice"))
384 .out(Some("FOLLOWS"))
385 .value_map(Some(vec!["externalId", "name"])),
386 )
387 .returning(["alice", "friends"]),
388 ),
389 ),
390 runtime(
391 "010-read-conditional-var-empty",
392 read_request(
393 read_batch()
394 .var_as(
395 "missing",
396 g().n_with_label("ParityUser")
397 .where_(Predicate::eq("externalId", "missing-user")),
398 )
399 .var_as_if(
400 "fallback",
401 BatchCondition::VarEmpty("missing".to_string()),
402 g().n_with_label("ParityUser")
403 .limit(1usize)
404 .value_map(Some(vec!["externalId"])),
405 )
406 .returning(["missing", "fallback"]),
407 ),
408 ),
409 runtime(
410 "011-read-conditional-var-min-size-prev",
411 read_request(
412 read_batch()
413 .var_as("users", g().n_with_label("ParityUser").limit(3usize))
414 .var_as_if(
415 "min_two",
416 BatchCondition::VarMinSize("users".to_string(), 2),
417 g().n(NodeRef::var("users")).count(),
418 )
419 .var_as_if(
420 "prev_ok",
421 BatchCondition::PrevNotEmpty,
422 g().n(NodeRef::var("users")).exists(),
423 )
424 .returning(["min_two", "prev_ok"]),
425 ),
426 ),
427 runtime(
428 "012-read-foreach-param",
429 with_params(
430 read_request(
431 read_batch()
432 .for_each_param(
433 "lookups",
434 read_batch().var_as(
435 "matched",
436 g().n_with_label("ParityUser")
437 .where_(Predicate::eq_param("externalId", "externalId"))
438 .value_map(Some(vec!["externalId", "name"])),
439 ),
440 )
441 .returning(["matched"]),
442 ),
443 vec![(
444 "lookups",
445 array(vec![
446 object(vec![("externalId", string("user-alice"))]),
447 object(vec![("externalId", string("user-carol"))]),
448 ]),
449 )],
450 vec![(
451 "lookups",
452 QueryParamType::Array(Box::new(QueryParamType::Object)),
453 )],
454 ),
455 ),
456 runtime(
457 "013-write-foreach-param-create",
458 with_params(
459 write_request(
460 write_batch()
461 .for_each_param(
462 "rows",
463 write_batch().var_as(
464 "created",
465 g().add_n(
466 "ParityEvent",
467 vec![
468 ("eventId", PropertyInput::param("eventId")),
469 ("kind", PropertyInput::param("kind")),
470 ("score", PropertyInput::param("score")),
471 ],
472 ),
473 ),
474 )
475 .returning(["created"]),
476 ),
477 vec![(
478 "rows",
479 array(vec![
480 object(vec![
481 ("eventId", string("event-1")),
482 ("kind", string("click")),
483 ("score", i64_value(10)),
484 ]),
485 object(vec![
486 ("eventId", string("event-2")),
487 ("kind", string("view")),
488 ("score", i64_value(5)),
489 ]),
490 ]),
491 )],
492 vec![(
493 "rows",
494 QueryParamType::Array(Box::new(QueryParamType::Object)),
495 )],
496 ),
497 ),
498 runtime(
499 "014-read-after-foreach-param",
500 read_request(
501 read_batch()
502 .var_as("event_count", g().n_with_label("ParityEvent").count())
503 .returning(["event_count"]),
504 ),
505 ),
506 runtime(
507 "015-write-set-remove-properties",
508 write_request(
509 write_batch()
510 .var_as(
511 "updated",
512 g().n_with_label("ParityUser")
513 .where_(Predicate::eq("externalId", "user-bob"))
514 .set_property("status", PropertyInput::from("inactive"))
515 .set_property(
516 "updatedAt",
517 PropertyInput::from(DateTime::from_millis(1_777_000_000_000)),
518 )
519 .remove_property("city")
520 .count(),
521 )
522 .returning(["updated"]),
523 ),
524 ),
525 runtime(
526 "016-read-updated-properties",
527 read_request(
528 read_batch()
529 .var_as(
530 "bob",
531 g().n_with_label("ParityUser")
532 .where_(Predicate::eq("externalId", "user-bob"))
533 .value_map(Some(vec!["externalId", "status", "updatedAt", "city"])),
534 )
535 .returning(["bob"]),
536 ),
537 ),
538 runtime(
539 "017-read-repeat-union",
540 read_request(
541 read_batch()
542 .var_as(
543 "walked",
544 g().n_with_label("ParityUser")
545 .where_(Predicate::eq("externalId", "user-alice"))
546 .repeat(
547 RepeatConfig::new(sub().out(Some("FOLLOWS")))
548 .times(2)
549 .emit_all()
550 .max_depth(4),
551 )
552 .union(vec![sub().out(Some("FOLLOWS")), sub().in_(Some("FOLLOWS"))])
553 .dedup()
554 .value_map(Some(vec!["externalId", "name"])),
555 )
556 .returning(["walked"]),
557 ),
558 ),
559 runtime(
560 "018-read-choose-coalesce-optional",
561 read_request(
562 read_batch()
563 .var_as(
564 "branched",
565 g().n_with_label("ParityUser")
566 .where_(Predicate::eq("externalId", "user-alice"))
567 .choose(
568 Predicate::eq("status", "active"),
569 sub().out(Some("FOLLOWS")),
570 Some(sub().in_(Some("FOLLOWS"))),
571 )
572 .coalesce(vec![sub().out(Some("FOLLOWS")), sub().in_(Some("FOLLOWS"))])
573 .optional(sub().out(Some("FOLLOWS")))
574 .dedup()
575 .value_map(Some(vec!["externalId", "name"])),
576 )
577 .returning(["branched"]),
578 ),
579 ),
580 runtime(
581 "019-read-aggregations",
582 read_request(
583 read_batch()
584 .var_as(
585 "by_status",
586 g().n_with_label("ParityUser").group_count("status"),
587 )
588 .var_as(
589 "mean_score",
590 g().n_with_label("ParityUser")
591 .aggregate_by(AggregateFunction::Mean, "score"),
592 )
593 .var_as(
594 "max_age",
595 g().n_with_label("ParityUser")
596 .aggregate_by(AggregateFunction::Max, "age"),
597 )
598 .returning(["by_status", "mean_score", "max_age"]),
599 ),
600 ),
601 runtime(
602 "020-write-index-create",
603 write_request(
604 write_batch()
605 .var_as(
606 "node_eq",
607 g().create_index_if_not_exists(IndexSpec::node_equality(
608 "ParityUser",
609 "externalId",
610 )),
611 )
612 .var_as(
613 "node_range",
614 g().create_index_if_not_exists(IndexSpec::node_range("ParityUser", "age")),
615 )
616 .var_as(
617 "edge_eq",
618 g().create_index_if_not_exists(IndexSpec::edge_equality(
619 "FOLLOWS", "since",
620 )),
621 )
622 .var_as(
623 "edge_range",
624 g().create_index_if_not_exists(IndexSpec::edge_range("FOLLOWS", "weight")),
625 )
626 .returning(["node_eq", "node_range", "edge_eq", "edge_range"]),
627 ),
628 ),
629 runtime(
630 "021-read-parameter-types",
631 with_params(
632 read_request(
633 read_batch()
634 .var_as(
635 "matches",
636 g().n_with_label("ParityUser")
637 .where_(Predicate::is_in_param("status", "statuses"))
638 .where_(Predicate::gte_param("createdAt", "created_after"))
639 .limit(Expr::param("limit"))
640 .value_map(Some(vec!["externalId", "status"])),
641 )
642 .returning(["matches"]),
643 ),
644 vec![
645 (
646 "statuses",
647 array(vec![string("active"), string("inactive")]),
648 ),
649 ("created_after", string("2026-01-01T00:00:00.000Z")),
650 ("limit", i64_value(5)),
651 ],
652 vec![
653 (
654 "statuses",
655 QueryParamType::Array(Box::new(QueryParamType::String)),
656 ),
657 ("created_after", QueryParamType::DateTime),
658 ("limit", QueryParamType::I64),
659 ],
660 ),
661 ),
662 runtime(
663 "022-write-property-value-variants",
664 write_request(
665 write_batch()
666 .var_as(
667 "variant_node",
668 g().add_n(
669 "ParityVariant",
670 vec![
671 ("nullValue", PropertyInput::from(PropertyValue::Null)),
672 ("boolValue", PropertyInput::from(true)),
673 (
674 "i64Value",
675 PropertyInput::from(9_223_372_036_854_775_000i64),
676 ),
677 (
678 "dateTimeValue",
679 PropertyInput::from(DateTime::from_millis(-1)),
680 ),
681 ("f64Value", PropertyInput::from(3.25f64)),
682 ("f32Value", PropertyInput::from(1.5f32)),
683 ("stringValue", PropertyInput::from("variant")),
684 (
685 "bytesValue",
686 PropertyInput::from(PropertyValue::from(vec![1u8, 2u8, 3u8])),
687 ),
688 (
689 "i64Array",
690 PropertyInput::from(PropertyValue::from(vec![
691 1i64, 2i64, 3i64,
692 ])),
693 ),
694 (
695 "f64Array",
696 PropertyInput::from(PropertyValue::from(vec![1.0f64, 2.0f64])),
697 ),
698 (
699 "f32Array",
700 PropertyInput::from(PropertyValue::from(vec![1.0f32, 2.0f32])),
701 ),
702 (
703 "stringArray",
704 PropertyInput::from(PropertyValue::from(vec![
705 "a".to_string(),
706 "b".to_string(),
707 ])),
708 ),
709 ],
710 ),
711 )
712 .returning(["variant_node"]),
713 ),
714 ),
715 runtime(
716 "023-read-property-value-variants",
717 read_request(
718 read_batch()
719 .var_as(
720 "variant",
721 g().n_with_label("ParityVariant")
722 .value_map(None::<Vec<&str>>),
723 )
724 .returning(["variant"]),
725 ),
726 ),
727 runtime(
728 "024-write-text-vector-indexes",
729 write_request(
730 write_batch()
731 .var_as(
732 "node_text",
733 g().create_text_index_nodes("ParityUser", "bio", None::<&str>),
734 )
735 .var_as(
736 "node_vector",
737 g().create_vector_index_nodes("ParityUser", "embedding", None::<&str>),
738 )
739 .var_as(
740 "edge_text",
741 g().create_text_index_edges("FOLLOWS", "note", None::<&str>),
742 )
743 .var_as(
744 "edge_vector",
745 g().create_vector_index_edges("FOLLOWS", "embedding", None::<&str>),
746 )
747 .returning(["node_text", "node_vector", "edge_text", "edge_vector"]),
748 ),
749 ),
750 runtime(
751 "025-read-text-search-nodes",
752 read_request(
753 read_batch()
754 .var_as(
755 "text_hits",
756 g().text_search_nodes("ParityUser", "bio", "graph", 5, None)
757 .value_map(Some(vec!["externalId", "bio", "$distance"])),
758 )
759 .returning(["text_hits"]),
760 ),
761 ),
762 runtime(
763 "026-read-vector-search-nodes",
764 read_request(
765 read_batch()
766 .var_as(
767 "vector_hits",
768 g().vector_search_nodes(
769 "ParityUser",
770 "embedding",
771 vec![1.0, 0.0, 0.0],
772 3,
773 None,
774 )
775 .project(vec![
776 Projection::property("externalId", "externalId"),
777 Projection::property("$distance", "distance"),
778 ]),
779 )
780 .returning(["vector_hits"]),
781 ),
782 ),
783 runtime(
784 "027-read-text-search-edges",
785 read_request(
786 read_batch()
787 .var_as(
788 "edge_text_hits",
789 g().text_search_edges("FOLLOWS", "note", "follows", 5, None)
790 .edge_properties(),
791 )
792 .returning(["edge_text_hits"]),
793 ),
794 ),
795 runtime(
796 "028-read-vector-search-edges",
797 read_request(
798 read_batch()
799 .var_as(
800 "edge_vector_hits",
801 g().vector_search_edges("FOLLOWS", "embedding", vec![1.0, 0.0], 5, None)
802 .edge_properties(),
803 )
804 .returning(["edge_vector_hits"]),
805 ),
806 ),
807 runtime(
808 "029-write-drop-temp-node",
809 write_request(
810 write_batch()
811 .var_as(
812 "temp",
813 g().add_n("ParityTemp", vec![("name", PropertyInput::from("temp"))]),
814 )
815 .var_as("dropped", g().n(NodeRef::var("temp")).drop().count())
816 .returning(["dropped"]),
817 ),
818 ),
819 runtime(
820 "030-read-final-counts",
821 read_request(
822 read_batch()
823 .var_as("users", g().n_with_label("ParityUser").count())
824 .var_as("events", g().n_with_label("ParityEvent").count())
825 .var_as("variants", g().n_with_label("ParityVariant").count())
826 .returning(["users", "events", "variants"]),
827 ),
828 ),
829 runtime(
830 "031-read-source-predicate-eq-param",
831 with_params(
832 read_request(
833 read_batch()
834 .var_as(
835 "user",
836 g().n_where(SourcePredicate::and(vec![
837 SourcePredicate::eq("$label", "ParityUser"),
838 SourcePredicate::eq("name", Expr::param("name")),
839 ]))
840 .value_map(Some(vec!["externalId", "name"])),
841 )
842 .returning(["user"]),
843 ),
844 vec![("name", string("Alice"))],
845 vec![("name", QueryParamType::String)],
846 ),
847 ),
848 runtime(
849 "032-read-source-predicate-between-param",
850 with_params(
851 read_request(
852 read_batch()
853 .var_as(
854 "adults",
855 g().n_where(SourcePredicate::and(vec![
856 SourcePredicate::eq("$label", "ParityUser"),
857 SourcePredicate::between("age", Expr::param("min_age"), 65i64),
858 ]))
859 .value_map(Some(vec!["externalId", "age"])),
860 )
861 .returning(["adults"]),
862 ),
863 vec![("min_age", i64_value(30))],
864 vec![("min_age", QueryParamType::I64)],
865 ),
866 ),
867 ]
868}
869
870fn node_permutation_fixtures() -> Vec<Fixture> {
871 let sources = ["label", "where", "all"];
872 let filters = ["none", "has", "logic", "expr"];
873 let bounds = ["none", "limit", "skip", "range"];
874 let terminals = ["count", "exists", "value_map", "project"];
875
876 let mut fixtures = Vec::new();
877 let mut index = 100;
878 for source in sources {
879 for filter in filters {
880 for bound in bounds {
881 for terminal in terminals {
882 let name =
883 format!("{index:03}-combo-node-{source}-{filter}-{bound}-{terminal}");
884 index += 1;
885 fixtures.push(runtime(
886 name,
887 read_request(node_combo_batch(source, filter, bound, terminal)),
888 ));
889 }
890 }
891 }
892 }
893 fixtures
894}
895
896fn node_combo_batch(source: &str, filter: &str, bound: &str, terminal: &str) -> ReadBatch {
897 let traversal = apply_node_bound(apply_node_filter(node_source(source), filter), bound)
898 .order_by("externalId", Order::Asc);
899 let traversal = match terminal {
900 "count" => traversal.count(),
901 "exists" => traversal.exists(),
902 "value_map" => traversal.value_map(Some(vec!["externalId", "name", "age", "status"])),
903 "project" => traversal.project(vec![
904 Projection::property("externalId", "externalId"),
905 Projection::property("status", "status"),
906 Projection::expr("age_plus_two", Expr::prop("age").add(Expr::val(2i64))),
907 ]),
908 other => panic!("unknown terminal {other}"),
909 };
910 read_batch()
911 .var_as("result", traversal)
912 .returning(["result"])
913}
914
915fn node_source(source: &str) -> Traversal<OnNodes, ReadOnly> {
916 match source {
917 "label" => g().n_with_label("ParityUser"),
918 "where" => g().n_where(SourcePredicate::eq("$label", "ParityUser")),
919 "all" => g().n(NodeRef::all()).has_label("ParityUser"),
920 other => panic!("unknown source {other}"),
921 }
922}
923
924fn apply_node_filter(
925 traversal: Traversal<OnNodes, ReadOnly>,
926 filter: &str,
927) -> Traversal<OnNodes, ReadOnly> {
928 match filter {
929 "none" => traversal,
930 "has" => traversal.has("status", "active"),
931 "logic" => traversal.where_(Predicate::and(vec![
932 Predicate::has_key("externalId"),
933 Predicate::or(vec![
934 Predicate::starts_with("name", "A"),
935 Predicate::ends_with("name", "b"),
936 ]),
937 Predicate::not(Predicate::is_null("age")),
938 ])),
939 "expr" => traversal.where_(Predicate::compare(
940 Expr::prop("score").add(Expr::val(1.0f64)),
941 CompareOp::Gt,
942 Expr::val(65.0f64),
943 )),
944 other => panic!("unknown filter {other}"),
945 }
946}
947
948fn apply_node_bound(
949 traversal: Traversal<OnNodes, ReadOnly>,
950 bound: &str,
951) -> Traversal<OnNodes, ReadOnly> {
952 match bound {
953 "none" => traversal,
954 "limit" => traversal.limit(2usize),
955 "skip" => traversal.skip(1usize),
956 "range" => traversal.range(0usize, 2usize),
957 other => panic!("unknown bound {other}"),
958 }
959}
960
961fn json_only_fixtures() -> Vec<Fixture> {
962 vec![
963 json_only(
964 "900-exhaustive-raw-read-steps",
965 with_params(
966 read_request(
967 read_batch()
968 .var_as(
969 "raw_nodes",
970 Traversal::<OnNodes, ReadOnly>::from_steps(vec![
971 Step::N(NodeRef::Param("node_ids".to_string())),
972 Step::Has("name".to_string(), PropertyValue::from("Alice")),
973 Step::Where(Predicate::contains_param("bio", "needle")),
974 Step::LimitBy(Expr::param("limit")),
975 Step::SkipBy(Expr::param("skip")),
976 Step::RangeBy(
977 StreamBound::literal(0),
978 StreamBound::expr(Expr::param("end")),
979 ),
980 Step::As("a".to_string()),
981 Step::Store("stored".to_string()),
982 Step::Select("stored".to_string()),
983 Step::Dedup,
984 Step::Within("stored".to_string()),
985 Step::Without("missing".to_string()),
986 Step::Fold,
987 Step::Unfold,
988 Step::Path,
989 Step::SimplePath,
990 Step::WithSack(PropertyValue::from(0i64)),
991 Step::SackSet("score".to_string()),
992 Step::SackAdd("score".to_string()),
993 Step::SackGet,
994 Step::Project(vec![
995 Projection::property("externalId", "externalId"),
996 Projection::expr("neg_age", Expr::prop("age").neg()),
997 ]),
998 ]),
999 )
1000 .var_as(
1001 "raw_edges",
1002 Traversal::<helix_db::OnEdges, ReadOnly>::from_steps(vec![
1003 Step::E(EdgeRef::Param("edge_ids".to_string())),
1004 Step::EWhere(SourcePredicate::or(vec![
1005 SourcePredicate::has_key("since"),
1006 SourcePredicate::starts_with("note", "Alice"),
1007 ])),
1008 Step::OutN,
1009 Step::InN,
1010 Step::OtherN,
1011 Step::EdgeHas("weight".to_string(), PropertyInput::from(1.0f64)),
1012 Step::EdgeHasLabel("FOLLOWS".to_string()),
1013 Step::OrderBy("weight".to_string(), Order::Desc),
1014 Step::EdgeProperties,
1015 ]),
1016 )
1017 .returning(["raw_nodes", "raw_edges"]),
1018 ),
1019 vec![
1020 ("node_ids", array(vec![i64_value(1), i64_value(2)])),
1021 ("edge_ids", array(vec![i64_value(1)])),
1022 ("needle", string("graph")),
1023 ("limit", i64_value(10)),
1024 ("skip", i64_value(0)),
1025 ("end", i64_value(10)),
1026 ],
1027 vec![
1028 (
1029 "node_ids",
1030 QueryParamType::Array(Box::new(QueryParamType::I64)),
1031 ),
1032 (
1033 "edge_ids",
1034 QueryParamType::Array(Box::new(QueryParamType::I64)),
1035 ),
1036 ("needle", QueryParamType::String),
1037 ("limit", QueryParamType::I64),
1038 ("skip", QueryParamType::I64),
1039 ("end", QueryParamType::I64),
1040 ],
1041 ),
1042 ),
1043 json_only(
1044 "901-exhaustive-raw-write-steps",
1045 write_request(
1046 write_batch()
1047 .var_as(
1048 "raw_indexes",
1049 Traversal::<helix_db::Terminal, WriteEnabled>::from_steps(vec![
1050 Step::CreateIndex {
1051 spec: IndexSpec::node_unique_equality("ParityUser", "externalId"),
1052 if_not_exists: true,
1053 },
1054 Step::DropIndex {
1055 spec: IndexSpec::node_range("ParityUser", "age"),
1056 },
1057 Step::CreateVectorIndexNodes {
1058 label: "ParityUser".to_string(),
1059 property: "embedding".to_string(),
1060 tenant_property: Some("tenantId".to_string()),
1061 },
1062 Step::CreateVectorIndexEdges {
1063 label: "FOLLOWS".to_string(),
1064 property: "embedding".to_string(),
1065 tenant_property: Some("tenantId".to_string()),
1066 },
1067 Step::CreateTextIndexNodes {
1068 label: "ParityUser".to_string(),
1069 property: "bio".to_string(),
1070 tenant_property: Some("tenantId".to_string()),
1071 },
1072 Step::CreateTextIndexEdges {
1073 label: "FOLLOWS".to_string(),
1074 property: "note".to_string(),
1075 tenant_property: Some("tenantId".to_string()),
1076 },
1077 ]),
1078 )
1079 .var_as(
1080 "raw_mutations",
1081 Traversal::<OnNodes, WriteEnabled>::from_steps(vec![
1082 Step::AddN {
1083 label: "RawNode".to_string(),
1084 properties: vec![("name".to_string(), PropertyInput::from("raw"))],
1085 },
1086 Step::AddE {
1087 label: "RAW_EDGE".to_string(),
1088 to: NodeRef::Var("raw_mutations".to_string()),
1089 properties: vec![("weight".to_string(), PropertyInput::from(1i64))],
1090 },
1091 Step::SetProperty(
1092 "name".to_string(),
1093 PropertyInput::Expr(Expr::param("name")),
1094 ),
1095 Step::RemoveProperty("old".to_string()),
1096 Step::DropEdge(NodeRef::Ids(vec![999_999])),
1097 Step::DropEdgeLabeled {
1098 to: NodeRef::Ids(vec![999_999]),
1099 label: "RAW_EDGE".to_string(),
1100 },
1101 Step::DropEdgeById(EdgeRef::Ids(vec![999_999])),
1102 Step::Drop,
1103 ]),
1104 )
1105 .returning(["raw_indexes", "raw_mutations"]),
1106 ),
1107 ),
1108 json_only(
1109 "902-dynamic-value-and-param-type-shapes",
1110 with_params(
1111 read_request(
1112 read_batch()
1113 .var_as("empty", g().n_with_label("Missing").count())
1114 .returning(["empty"]),
1115 ),
1116 vec![
1117 ("null", DynamicQueryValue::Null),
1118 ("bool", DynamicQueryValue::Bool(true)),
1119 ("i64", DynamicQueryValue::I64(i64::MAX)),
1120 ("f64", DynamicQueryValue::F64(1.25)),
1121 ("f32", DynamicQueryValue::F32(1.5)),
1122 ("string", string("value")),
1123 ("array", array(vec![i64_value(1), string("two")])),
1124 (
1125 "object",
1126 object(vec![("nested", DynamicQueryValue::Bool(true))]),
1127 ),
1128 ],
1129 vec![
1130 ("null", QueryParamType::Value),
1131 ("bool", QueryParamType::Bool),
1132 ("i64", QueryParamType::I64),
1133 ("f64", QueryParamType::F64),
1134 ("f32", QueryParamType::F32),
1135 ("string", QueryParamType::String),
1136 (
1137 "array",
1138 QueryParamType::Array(Box::new(QueryParamType::Value)),
1139 ),
1140 ("object", QueryParamType::Object),
1141 ],
1142 ),
1143 ),
1144 json_only(
1145 "903-empty-source-vector-text-runtime-inputs",
1146 with_params(
1147 read_request(
1148 read_batch()
1149 .var_as(
1150 "vector_nodes",
1151 g().vector_search_nodes_with(
1152 "ParityUser",
1153 "embedding",
1154 PropertyInput::param("query_vector"),
1155 Expr::param("limit"),
1156 Some(PropertyInput::param("tenant")),
1157 ),
1158 )
1159 .var_as(
1160 "text_nodes",
1161 g().text_search_nodes_with(
1162 "ParityUser",
1163 "bio",
1164 PropertyInput::param("query_text"),
1165 Expr::param("limit"),
1166 Some(PropertyInput::param("tenant")),
1167 ),
1168 )
1169 .returning(["vector_nodes", "text_nodes"]),
1170 ),
1171 vec![
1172 (
1173 "query_vector",
1174 array(vec![f64_value(1.0), f64_value(0.0), f64_value(0.0)]),
1175 ),
1176 ("query_text", string("graph")),
1177 ("limit", i64_value(5)),
1178 ("tenant", string("tenant-a")),
1179 ],
1180 vec![
1181 (
1182 "query_vector",
1183 QueryParamType::Array(Box::new(QueryParamType::F64)),
1184 ),
1185 ("query_text", QueryParamType::String),
1186 ("limit", QueryParamType::I64),
1187 ("tenant", QueryParamType::String),
1188 ],
1189 ),
1190 ),
1191 json_only(
1192 "904-empty-query-and-node-edge-ref-shapes",
1193 read_request(
1194 read_batch()
1195 .var_as(
1196 "all_nodes",
1197 Traversal::<OnNodes, ReadOnly>::from_steps(vec![
1198 Step::N(NodeRef::All),
1199 Step::Count,
1200 ]),
1201 )
1202 .var_as(
1203 "node_ids",
1204 Traversal::<OnNodes, ReadOnly>::from_steps(vec![
1205 Step::N(NodeRef::ids([1, 2])),
1206 Step::Id,
1207 ]),
1208 )
1209 .var_as(
1210 "node_var",
1211 Traversal::<OnNodes, ReadOnly>::from_steps(vec![
1212 Step::N(NodeRef::Var("all_nodes".to_string())),
1213 Step::Label,
1214 ]),
1215 )
1216 .var_as(
1217 "edge_ids",
1218 Traversal::<helix_db::OnEdges, ReadOnly>::from_steps(vec![
1219 Step::E(EdgeRef::ids([1, 2])),
1220 Step::Id,
1221 ]),
1222 )
1223 .var_as(
1224 "edge_var",
1225 Traversal::<helix_db::OnEdges, ReadOnly>::from_steps(vec![
1226 Step::E(EdgeRef::Var("edge_ids".to_string())),
1227 Step::Label,
1228 ]),
1229 )
1230 .returning(["all_nodes", "node_ids", "node_var", "edge_ids", "edge_var"]),
1231 ),
1232 ),
1233 json_only(
1234 "905-empty-traversal-source-mutators",
1235 write_request(
1236 write_batch()
1237 .var_as(
1238 "inject",
1239 Traversal::<Empty, ReadOnly>::new()
1240 .inject("some_var")
1241 .count(),
1242 )
1243 .var_as(
1244 "drop_edge_by_id",
1245 g().drop_edge_by_id(EdgeRef::id(123_456)).count(),
1246 )
1247 .returning(["inject", "drop_edge_by_id"]),
1248 ),
1249 ),
1250 json_only(
1251 "906-nested-dynamic-property-write-shapes",
1252 with_params(
1253 write_request(
1254 write_batch()
1255 .var_as(
1256 "created",
1257 g().add_n(
1258 "ParityNested",
1259 vec![
1260 ("name", PropertyInput::from("nested")),
1261 (
1262 "metadata",
1263 PropertyInput::from(nested_metadata_property(
1264 "some_id", 20,
1265 )),
1266 ),
1267 ],
1268 ),
1269 )
1270 .var_as(
1271 "updated",
1272 g().n(NodeRef::var("created"))
1273 .set_property("metadata", PropertyInput::param("metadata"))
1274 .value_map(Some(vec!["metadata.externalID"])),
1275 )
1276 .var_as(
1277 "target",
1278 g().add_n(
1279 "ParityNestedTarget",
1280 vec![("name", PropertyInput::from("target"))],
1281 ),
1282 )
1283 .var_as(
1284 "edge",
1285 g().n(NodeRef::var("created"))
1286 .add_e(
1287 "NESTED_LINK",
1288 NodeRef::var("target"),
1289 vec![(
1290 "metadata",
1291 PropertyInput::from(nested_metadata_property("edge_id", 5)),
1292 )],
1293 )
1294 .count(),
1295 )
1296 .returning(["created", "updated", "edge"]),
1297 ),
1298 vec![("metadata", nested_metadata_param("param_id", 22))],
1299 vec![("metadata", QueryParamType::Object)],
1300 ),
1301 ),
1302 json_only(
1303 "907-nested-dynamic-property-read-shapes",
1304 with_params(
1305 read_request(
1306 read_batch()
1307 .var_as(
1308 "nested_users",
1309 g().n_where(SourcePredicate::and(vec![
1310 SourcePredicate::eq("$label", "ParityNested"),
1311 SourcePredicate::eq(
1312 "metadata.externalID",
1313 Expr::param("external_id"),
1314 ),
1315 ]))
1316 .where_(Predicate::compare(
1317 Expr::prop("metadata.score"),
1318 CompareOp::Gt,
1319 Expr::val(10i64),
1320 ))
1321 .order_by_multiple(vec![
1322 ("metadata.score", Order::Desc),
1323 ("name", Order::Asc),
1324 ])
1325 .project(vec![
1326 Projection::property("metadata.externalID", "external_id"),
1327 Projection::expr("score_copy", Expr::prop("metadata.score")),
1328 ]),
1329 )
1330 .var_as(
1331 "nested_values",
1332 g().n_with_label("ParityNested")
1333 .values(vec!["metadata.externalID"]),
1334 )
1335 .var_as(
1336 "nested_map",
1337 g().n_with_label("ParityNested")
1338 .value_map(Some(vec!["metadata.externalID", "metadata.score"])),
1339 )
1340 .var_as(
1341 "nested_edges",
1342 g().e_where(SourcePredicate::and(vec![
1343 SourcePredicate::eq("$label", "NESTED_LINK"),
1344 SourcePredicate::eq("metadata.externalID", "edge_id"),
1345 ]))
1346 .edge_has("metadata.externalID", PropertyInput::from("edge_id"))
1347 .edge_properties(),
1348 )
1349 .returning([
1350 "nested_users",
1351 "nested_values",
1352 "nested_map",
1353 "nested_edges",
1354 ]),
1355 ),
1356 vec![("external_id", string("param_id"))],
1357 vec![("external_id", QueryParamType::String)],
1358 ),
1359 ),
1360 json_only(
1361 "908-edge-endpoint-projection",
1362 read_request(
1363 read_batch()
1364 .var_as(
1365 "endpoints",
1366 g().e_with_label("FOLLOWS").project(vec![
1367 Projection::from_endpoint("externalId", "from_id"),
1368 Projection::to_endpoint("externalId", "to_id"),
1369 Projection::property("$id", "edge_id"),
1370 ]),
1371 )
1372 .returning(["endpoints"]),
1373 ),
1374 ),
1375 json_only(
1376 "909-row-binding-basic-projection",
1377 read_request(
1378 read_batch()
1379 .var_as(
1380 "bindings",
1381 g().n_with_label("ParityService")
1382 .bind("service")
1383 .project_bindings(vec![
1384 BindingProjection::binding("service", "$id", "service_id"),
1385 BindingProjection::current("metadata.name", "current_name"),
1386 BindingProjection::binding(
1387 "missing_binding",
1388 "externalId",
1389 "missing_external_id",
1390 ),
1391 ]),
1392 )
1393 .returning(["bindings"]),
1394 ),
1395 ),
1396 json_only(
1397 "910-row-binding-branch-distinct-projection",
1398 read_request(
1399 read_batch()
1400 .var_as(
1401 "workloads",
1402 g().n_with_label("ParityService")
1403 .bind("service")
1404 .out(Some("ROUTES_TO"))
1405 .bind("pod")
1406 .optional(sub().in_(Some("CREATES")).bind("deployment"))
1407 .union(vec![
1408 sub().in_(Some("MANAGES")).bind("owner"),
1409 sub().out(Some("ROUTES_TO")).bind("workload"),
1410 ])
1411 .project_distinct_bindings(vec![
1412 BindingProjection::binding("service", "$id", "service_id"),
1413 BindingProjection::coalesce(
1414 vec![
1415 BindingValueRef::binding("deployment", "$id"),
1416 BindingValueRef::binding("owner", "$id"),
1417 BindingValueRef::binding("workload", "$id"),
1418 ],
1419 "workload_id",
1420 ),
1421 ]),
1422 )
1423 .returning(["workloads"]),
1424 ),
1425 ),
1426 json_only(
1427 "911-range-index-direction",
1428 write_request(
1429 write_batch()
1430 .var_as(
1431 "node_desc",
1432 g().create_index_if_not_exists(IndexSpec::node_range_desc(
1433 "ParityUser",
1434 "age",
1435 )),
1436 )
1437 .var_as(
1438 "edge_desc",
1439 g().create_index_if_not_exists(IndexSpec::edge_range_desc(
1440 "FOLLOWS", "weight",
1441 )),
1442 )
1443 .var_as(
1444 "node_asc",
1445 g().create_index_if_not_exists(IndexSpec::node_range(
1446 "ParityUser",
1447 "score",
1448 )),
1449 )
1450 .returning(["node_desc", "edge_desc", "node_asc"]),
1451 ),
1452 ),
1453 ]
1454}